/// <summary>
        /// Main thread loop
        /// </summary>
        private void MainLoop()
        {
            //initialize the pipe
            Logger.Info("Initializing Thread. Creating pipe object.");

            //Forever trying to connect unless the abort signal is sent
            //Keep Alive Loop
            while (!aborting && !shutdown)
            {
                try
                {
                    //Wrap everything up in a try get
                    //Dispose of the pipe if we have any (could be broken)
                    if (namedPipe == null)
                    {
                        Logger.Error("Something bad has happened with our pipe client!");
                        aborting = true;
                        return;
                    }

                    //Connect to a new pipe
                    Logger.Info("Connecting to the pipe through the {0}", namedPipe.GetType().FullName);
                    if (namedPipe.Connect(targetPipe))
                    {
                        #region Connected
                        //We connected to a pipe! Reset the delay
                        Logger.Info("Connected to the pipe. Attempting to establish handshake...");
                        EnqueueMessage(new ConnectionEstablishedMessage()
                        {
                            ConnectedPipe = namedPipe.ConnectedPipe
                        });
                        delay.Reset();

                        //Attempt to establish a handshake
                        EstablishHandshake();
                        Logger.Info("Connection Established. Starting reading loop...");

                        //Continously iterate, waiting for the frame
                        //We want to only stop reading if the inside tells us (mainloop), if we are aborting (abort) or the pipe disconnects
                        // We dont want to exit on a shutdown, as we still have information
                        PipeFrame frame;
                        bool      mainloop = true;
                        while (mainloop && !aborting && namedPipe.IsConnected)
                        {
                            #region Read Loop

                            //Iterate over every frame we have queued up, processing its contents
                            if (namedPipe.ReadFrame(out frame))
                            {
                                #region Read Payload
                                Logger.Info("Read Payload: {0}", frame.Opcode);

                                //Do some basic processing on the frame
                                switch (frame.Opcode)
                                {
                                //We have been told by discord to close, so we will consider it an abort
                                case Opcode.Close:

                                    ClosePayload close = frame.GetObject <ClosePayload>();
                                    Logger.Info("We have been told to terminate by discord: ({0}) {1}", close.Code, close.Reason);
                                    EnqueueMessage(new CloseMessage()
                                    {
                                        Code = close.Code, Reason = close.Reason
                                    });
                                    mainloop = false;
                                    break;

                                //We have pinged, so we will flip it and respond back with pong
                                case Opcode.Ping:
                                    Logger.Info("PING");
                                    frame.Opcode = Opcode.Pong;
                                    namedPipe.WriteFrame(frame);
                                    break;

                                //We have ponged? I have no idea if Discord actually sends ping/pongs.
                                case Opcode.Pong:
                                    Logger.Info("PONG");
                                    break;

                                //A frame has been sent, we should deal with that
                                case Opcode.Frame:
                                    if (shutdown)
                                    {
                                        //We are shutting down, so skip it
                                        Logger.Warning("Skipping frame because we are shutting down.");
                                        break;
                                    }

                                    if (frame.Data == null)
                                    {
                                        //We have invalid data, thats not good.
                                        Logger.Error("We received no data from the frame so we cannot get the event payload!");
                                        break;
                                    }

                                    //We have a frame, so we are going to process the payload and add it to the stack
                                    EventPayload response = frame.GetObject <EventPayload>();
                                    ProcessFrame(response);
                                    break;


                                default:
                                case Opcode.Handshake:
                                    //We have a invalid opcode, better terminate to be safe
                                    Logger.Error("Invalid opcode: {0}", frame.Opcode);
                                    mainloop = false;
                                    break;
                                }

                                #endregion
                            }

                            if (!aborting && namedPipe.IsConnected)
                            {
                                //Process the entire command queue we have left
                                ProcessCommandQueue();

                                //Wait for some time, or until a command has been queued up
                                queueUpdatedEvent.WaitOne(POLL_RATE);
                            }

                            #endregion
                        }
                        #endregion

                        Logger.Info("Left main read loop for some reason. Aborting: {0}, Shutting Down: {1}", aborting, shutdown);
                    }
                    else
                    {
                        Logger.Error("Failed to connect for some reason.");
                        EnqueueMessage(new ConnectionFailedMessage()
                        {
                            FailedPipe = targetPipe
                        });
                    }

                    //If we are not aborting, we have to wait a bit before trying to connect again
                    if (!aborting && !shutdown)
                    {
                        //We have disconnected for some reason, either a failed pipe or a bad reading,
                        // so we are going to wait a bit before doing it again
                        long sleep = delay.NextDelay();

                        Logger.Info("Waiting {0}ms before attempting to connect again", sleep);
                        Thread.Sleep(delay.NextDelay());
                    }
                }
                catch (InvalidPipeException e)
                {
                    Logger.Error("Invalid Pipe Exception: {0}", e.Message);
                }
                catch (Exception e)
                {
                    Logger.Error("Unhandled Exception: {0}", e.GetType().FullName);
                    Logger.Error(e.Message);
                    Logger.Error(e.StackTrace);
                }
                finally
                {
                    //Disconnect from the pipe because something bad has happened. An exception has been thrown or the main read loop has terminated.
                    if (namedPipe.IsConnected)
                    {
                        //Terminate the pipe
                        Logger.Info("Closing the named pipe.");
                        namedPipe.Close();
                    }

                    //Update our state
                    SetConnectionState(RpcState.Disconnected);
                }
            }

            //We have disconnected, so dispose of the thread and the pipe.
            Logger.Info("Left Main Loop");
            namedPipe.Dispose();

            Logger.Info("Thread Terminated");
        }
        /// <summary>Handles the response from the pipe and calls appropriate events and changes states.</summary>
        /// <param name="response">The response received by the server.</param>
        private void ProcessFrame(EventPayload response)
        {
            Logger.Info("Handling Response. Cmd: {0}, Event: {1}", response.Command, response.Event);

            //Check if it is an error
            if (response.Event.HasValue && response.Event.Value == ServerEvent.Error)
            {
                //We have an error
                Logger.Error("Error received from the RPC");

                //Create the event objetc and push it to the queue
                ErrorMessage err = response.GetObject <ErrorMessage>();
                Logger.Error("Server responded with an error message: ({0}) {1}", err.Code.ToString(), err.Message);

                //Enqueue the messsage and then end
                EnqueueMessage(err);
                return;
            }

            //Check if its a handshake
            if (State == RpcState.Connecting)
            {
                if (response.Command == Command.Dispatch && response.Event.HasValue && response.Event.Value == ServerEvent.Ready)
                {
                    Logger.Info("Connection established with the RPC");
                    SetConnectionState(RpcState.Connected);
                    delay.Reset();

                    //Prepare the object
                    ReadyMessage ready = response.GetObject <ReadyMessage>();
                    lock (l_config)
                    {
                        _configuration = ready.Configuration;
                        ready.User.SetConfiguration(_configuration);
                    }

                    //Enqueue the message
                    EnqueueMessage(ready);
                    return;
                }
            }

            if (State == RpcState.Connected)
            {
                switch (response.Command)
                {
                //We were sent a dispatch, better process it
                case Command.Dispatch:
                    ProcessDispatch(response);
                    break;

                //We were sent a Activity Update, better enqueue it
                case Command.SetActivity:
                    if (response.Data == null)
                    {
                        EnqueueMessage(new PresenceMessage());
                    }
                    else
                    {
                        RichPresenceResponse rp = response.GetObject <RichPresenceResponse>();
                        EnqueueMessage(new PresenceMessage(rp));
                    }
                    break;

                case Command.Unsubscribe:
                case Command.Subscribe:

                    //Prepare a serializer that can account for snake_case enums.
                    JsonSerializer serializer = new JsonSerializer();
                    serializer.Converters.Add(new Converters.EnumSnakeCaseConverter());

                    //Go through the data, looking for the evt property, casting it to a server event
                    var evt = response.GetObject <EventPayload>().Event.Value;

                    //Enqueue the appropriate message.
                    if (response.Command == Command.Subscribe)
                    {
                        EnqueueMessage(new SubscribeMessage(evt));
                    }
                    else
                    {
                        EnqueueMessage(new UnsubscribeMessage(evt));
                    }

                    break;


                case Command.SendActivityJoinInvite:
                    Logger.Info("Got invite response ack.");
                    break;

                case Command.CloseActivityJoinRequest:
                    Logger.Info("Got invite response reject ack.");
                    break;

                //we have no idea what we were sent
                default:
                    Logger.Error("Unkown frame was received! {0}", response.Command);
                    return;
                }
                return;
            }

            Logger.Info("Received a frame while we are disconnected. Ignoring. Cmd: {0}, Event: {1}", response.Command, response.Event);
        }
Beispiel #3
0
        /// <summary>Handles the response from the pipe and calls appropriate events and changes states.</summary>
        /// <param name="response">The response received by the server.</param>
        private void ProcessFrame(EventPayload response)
        {
            Console.WriteLine($"Handling Response. Cmd: {response.Command}, Event: {response.Event}");

            //Check if it is an error
            if (response.Event.HasValue && response.Event.Value == ServerEvent.Error)
            {
                //We have an error
                Console.WriteLine("Error received from the RPC");

                //Create the event objetc and push it to the queue
                ErrorMessage err = response.GetObject <ErrorMessage>();
                Console.WriteLine($"Server responded with an error message: ({err.Code.ToString()}) {err.Message}");

                //Enqueue the messsage and then end
                EnqueueMessage(err);
                return;
            }

            //Check if its a handshake
            if (State == RpcState.Connecting)
            {
                if (response.Command == Command.Dispatch && response.Event.HasValue && response.Event.Value == ServerEvent.Ready)
                {
                    Console.WriteLine("Connection established with the RPC");
                    SetConnectionState(RpcState.Connected);
                    delay.Reset();

                    //Prepare the object
                    ReadyMessage ready = response.GetObject <ReadyMessage>();
                    lock (l_config)
                    {
                        _configuration = ready.Configuration;
                        ready.User.SetConfiguration(_configuration);
                    }

                    //Enqueue the message
                    EnqueueMessage(ready);
                    return;
                }
            }

            if (State == RpcState.Connected)
            {
                switch (response.Command)
                {
                //We were sent a Activity Update, better enqueue it
                case Command.SetActivity:
                    if (response.Data == null)
                    {
                        EnqueueMessage(new PresenceMessage());
                    }
                    else
                    {
                        RichPresenceResponse rp = response.GetObject <RichPresenceResponse>();
                        EnqueueMessage(new PresenceMessage(rp));
                    }
                    break;


                //we have no idea what we were sent
                default:
                    Console.WriteLine($"Unkown frame was received! {response.Command}");
                    return;
                }
                return;
            }

            Console.WriteLine($"Received a frame while we are disconnected. Ignoring. Cmd: {response.Command}, Event: {response.Event}");
        }