/// <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); }
/// <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}"); }