public void PackUnpack() { var doc = TestHelpers.CreateStarDocument(); var original = DocdbGatewayMessage.Create("Star", doc); var storage = new InMemoryGatewayBlobStore(); byte[] packed = GatewayPacket.Pack(original, storage); var unpacked = GatewayPacket.Unpack <DocdbGatewayMessage>(packed, storage); Assert.AreEqual(original.Key, unpacked.Key); TestHelpers.AssertEqualStars( original.DocumentsAs <StarDocument>().First(), unpacked.DocumentsAs <StarDocument>().First() ); }
public void PackUnpack() { var entity = TestHelpers.CreateStarEntity(); var original = TableGatewayMessage.Create("Person", entity); var storage = new InMemoryGatewayBlobStore(); byte[] packed = GatewayPacket.Pack(original, storage); TableGatewayMessage unpacked = GatewayPacket.Unpack <TableGatewayMessage>(packed, storage); Assert.AreEqual(original.Key, unpacked.Key); TestHelpers.AssertEqualStars( original.EntitiesAs <StarEntity>().First(), unpacked.EntitiesAs <StarEntity>().First() ); }
private void SendTo(ServerState state, GatewayPacket packet) { if (state.State == SERVER_STATE.Running) { byte[] data = null; PACKET_TYPE type = PACKET_TYPE.PKT_UNKNOWN; switch (packet.PacketType) { case PACKET_TYPE.PKT_PUSH_DATA: data = new byte[4]; type = PACKET_TYPE.PKT_PUSH_ACK; break; case PACKET_TYPE.PKT_PULL_DATA: data = new byte[12]; packet.EUI.CopyTo(data, 4); type = PACKET_TYPE.PKT_PULL_ACK; break; default: break; } if (type != PACKET_TYPE.PKT_UNKNOWN) { data[0] = packet.Version; BitConverter.GetBytes(packet.Token).CopyTo(data, 1); data[3] = (byte)type; try { state.Socket.BeginSendTo(data, 0, data.Length, SocketFlags.None, packet.Sender, new AsyncCallback(SendToCallback), state); } catch (Exception ex) { state.ErrorSendTo = ex; } } } }
public async Task Send(GatewayPacket packet) { // from `ManagedWebSocket.s_validSendStates` if (_client is not { State : WebSocketState.Open or WebSocketState.CloseReceived })
private async Task HandlePacket(string json, CancellationToken cancellationToken) { var genericGateway = JsonSerializer.Deserialize <GatewayPacket <object> >(json, _jsonOptions); if (genericGateway is null) { _logger.LogError("Unable to parse generic gateway packet"); return; } if (genericGateway.SequenceNumber is not null) { _lastSequenceNumber = genericGateway.SequenceNumber; } switch (_state) { case GatewayState.Connecting: if (genericGateway.Opcode == Opcode.Hello) { // We got a Hello, start sending heartbeats! var helloPacket = DeserializePacket <HelloData>(json); if (helloPacket is null || helloPacket.Data is null) { _logger.LogError("Invalid hello packet"); return; } _heartbeatInterval = TimeSpan.FromMilliseconds(helloPacket.Data.HeartbeatInterval); _logger.LogDebug("Heartbeat interval set to '{Interval}'", _heartbeatInterval); if (_heartbeatTask is null) { _heartbeatTask = new BackgroundTask(SendHeartbeatAsync); } // Queue up an Identify packet var identifyPacket = new GatewayPacket <IdentifyData> { Data = new IdentifyData { // TODO: Intents Intents = 1, Token = _settings.Value.Token, Properties = new IdentifyDataProperties { Browser = "octantis", Device = "octantis", Os = "linux" } }, Opcode = Opcode.Identify }; await TransmitGatewayAsync(identifyPacket, cancellationToken); } else if (genericGateway.Opcode == Opcode.Dispatch && genericGateway.EventName == Events.Ready) { var readyPacket = DeserializePacket <ReadyData>(json); if (readyPacket is null || readyPacket.Data is null) { _logger.LogError("Invalid ready packet"); return; } ApplicationId = readyPacket.Data.User?.Id ?? 0; _sessionId = readyPacket.Data.SessionId; _logger.LogInformation("Connected to gateway v{Version}, application id '{Id}', session id '{Id}'", readyPacket.Data.GatewayVersion, ApplicationId, _sessionId); foreach (var guild in readyPacket.Data.Guilds) { _logger.LogDebug("Guild '{Id}'", guild.Id); } // Got the ready event, move up to connected state! _state = GatewayState.Connected; } else { _logger.LogWarning("Unhandled opcode '{Opcode}'", genericGateway.Opcode); } break; case GatewayState.Connected: { // Handle events if (genericGateway.Opcode == Opcode.Heartbeat) { // Force heartbeat send? _logger.LogError("TODO: SEND HEARTBEAT"); } else if (genericGateway.Opcode == Opcode.Dispatch) { // EVENTS :D var type = Events.FromString(genericGateway.EventName !); List <Registration>?registrationsForEvent; lock (_registrations) { _registrations.TryGetValue(type, out registrationsForEvent); } if (registrationsForEvent is not null) { foreach (var registration in registrationsForEvent) { var packetType = typeof(GatewayPacket <>).MakeGenericType(registration.ArgumentType); dynamic deserializedAsType = JsonSerializer.Deserialize(json, packetType, _jsonOptions) !; registration.Action(deserializedAsType.Data); } } else { _logger.LogWarning("Unhandled event type '{Type}'", type); } } } break; } }
public async Task WritePacket(ClientWebSocket socket, GatewayPacket packet) { var bytes = JsonSerializer.SerializeToUtf8Bytes(packet, _jsonSerializerOptions); await socket.SendAsync(bytes.AsMemory(), WebSocketMessageType.Text, true, default); }
protected override async void PacketRecieved(string packetstr) { GatewayPacket packet = new GatewayPacket(packetstr); GatewayPacketType type = packet.GetPacketType(); if (type == GatewayPacketType.Hello) { Logger.Info("Server says hello!"); SendIdentityPacket(); } else if (type == GatewayPacketType.Heartbeat) { SendHeartBeat(); } else if (type == GatewayPacketType.HeartbeatACK) { } else if (type == GatewayPacketType.InvalidSession) { Logger.Error("Invalid session"); Disconnect(); } else if (type == GatewayPacketType.Reconnect) { await Reconnect(); } else if (type == GatewayPacketType.Dispatch) { string EventType = packet.GetEventType(); JObject data = packet.GetData <JObject>(); if (EventType == "READY") { string bot_name = (string)data["user"]["username"]; botUserId = (string)data["user"]["id"]; sessionId = (string)data["session_id"]; foreach (Processor p in processors) { p.OnReady(bot_name); } } else if (EventType == "MESSAGE_CREATE") { Message message = packet.GetData <Message>(); (Guild guild, Channel channel) = GetGuildAndChannel(message.channel_id); string[] words = SplitString(message.content); Logger.Info($"New message [{message.author.username} in {guild.name} -> {channel.name}] {message.content}"); if (!message.author.bot) { foreach (Processor p in processors) { string prefix = p.GetPrefix(); if (prefix != null) { if (words.Length >= 2 && prefix == words[0]) { List <string> w2 = new List <string>(words); Logger.Debug("Command detected!"); w2.RemoveAt(0); string command = w2[0]; w2.RemoveAt(0); p.OnCommand(message, channel, command, w2.ToArray()); } } p.OnMessage(message, channel); } } else { Logger.Info("Bot messeges are ignored"); } } else if (EventType == "TYPING_START") { string channel_id = (string)data["channel_id"]; string user_id = (string)data["user_id"]; foreach (Processor p in processors) { p.OnTypingStarted(channel_id, user_id); } } else if (EventType == "PRESENCE_UPDATE") { string status = (string)data["status"]; string game = null; try { game = (string)data["game"]["name"]; } catch { } string user_id = (string)data["user"]["id"]; foreach (Processor p in processors) { p.OnStatusChange(user_id, status, game); } } else if (EventType == "GUILD_CREATE") { Guild guild = packet.GetData <Guild>(); guilds.Add(guild); Logger.Info($"Guild created: {guild.name} #{guild.id}"); foreach (Processor p in processors) { p.OnGuildCreate(guild); } } else if (EventType == "GUILD_DELETE") { string guild_id = (string)data["id"]; Logger.Info($"Guild removed: #{guild_id}"); for (int i = guilds.Count - 1; i >= 0; i--) { if (guilds[i].id == guild_id) { guilds.RemoveAt(i); } } foreach (Processor p in processors) { p.OnGuildDelete(guild_id); } } else if (EventType == "VOICE_STATE_UPDATE") { string user_id = (string)data["user_id"]; userStatus[user_id] = packet.GetData <UserVoiceStatus>(); if (user_id != botUserId) { foreach (Processor p in processors) { p.OnUserVoiceStatusChange(user_id, userStatus[user_id]); } } } else if (EventType == "VOICE_SERVER_UPDATE") { string guildId = (string)data["guild_id"]; string endpoint = (string)data["endpoint"]; string token = (string)data["token"]; if (endpoint != null) { if (!voiceSockets.ContainsKey(guildId) || !voiceSockets[guildId].IsOk()) { string prefix = "wss://"; if (endpoint.Contains(":80")) { prefix = "ws://"; } voiceSockets[guildId] = new VoiceSocket(new Uri(prefix + endpoint), guildId, sessionId, token, botUserId); await voiceSockets[guildId].Connect(); } } } else { Logger.Debug($"Unknown event: {EventType}"); } } }
private void ReceiveFromCallback(IAsyncResult ar) { ServerState state = (ServerState)ar.AsyncState; if (state.State == SERVER_STATE.Running) { Socket socket = state.Socket; try { // Read packet from gateway IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 0); EndPoint epSender = (EndPoint)ipep; int bytesRead = socket.EndReceiveFrom(ar, ref epSender); byte[] buffer = state.Buffer; // Check packet from gateway if (bytesRead >= 4) { byte version = buffer[0]; if (version == 1 || version == 2) { GatewayPacket packet = new GatewayPacket(); packet.Version = version; packet.Token = BitConverter.ToUInt16(buffer, 1); byte type = buffer[3]; switch (type) { case (byte)PACKET_TYPE.PKT_PUSH_DATA: if (bytesRead > 12) { packet.Json = Encoding.ASCII.GetString(buffer, 12, bytesRead - 12); packet.SetEUI(buffer); } else { packet = null; } break; case (byte)PACKET_TYPE.PKT_PULL_DATA: if (bytesRead == 12) { packet.SetEUI(buffer); } else { packet = null; } break; case (byte)PACKET_TYPE.PKT_TX_ACK: if (bytesRead > 4) { packet.Json = Encoding.ASCII.GetString(buffer, 4, bytesRead - 4); } else { packet = null; } break; default: packet = null; break; } if (packet != null) { packet.PacketType = (PACKET_TYPE)type; packet.Sender = epSender; lock (state.LockPackets) { if (state.Packets.Count < 100) { state.Packets.Enqueue(packet); } else { state.LostPackets++; } state.ResetEvent.Set(); } } } } } catch (Exception ex) { state.ErrorReceiveFromCB = ex; } try { IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 0); EndPoint epSender = (EndPoint)ipep; socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveFromCallback), state); } catch (Exception ex) { state.ErrorReceiveFrom = ex; } } }
public void Run() { Console.WriteLine("\nPress any key to exit"); JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Culture = CultureInfo.InvariantCulture; try { state.Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); state.Socket.Bind(new IPEndPoint(IPAddress.Any, Properties.Settings.Default.LwgPort)); IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 0); EndPoint epSender = (EndPoint)ipep; state.Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveFromCallback), state); state.State = SERVER_STATE.Running; /* * Test PULL_RESP */ GatewayPullResp TestPullRest = new GatewayPullResp(); { GatewayTxpkV1 txpk = new GatewayTxpkV1(); txpk.imme = true; txpk.tmst = 0; txpk.time = DateTime.UtcNow; txpk.freq = 864.123456; txpk.rfch = 0; txpk.powe = 14; txpk.modu = "SF11BW125"; txpk.codr = "4/6"; txpk.ipol = false; txpk.size = 32; txpk.data = "H3P3N2i9qc4yt7rK7ldqoeCVJGBybzPY5h1Dd7P7p8v"; TestPullRest.Txpk = txpk; } DateTime keyTime = DateTime.Now; while (state.State == SERVER_STATE.Running) { while (state.Packets.Count > 0) { #region Process packets GatewayPacket packet = null; lock (state.LockPackets) { packet = state.Packets.Dequeue(); state.ResetEvent.Reset(); } if (packet == null) { continue; } Console.WriteLine(string.Format("Recv from {0}", packet.Sender)); Console.WriteLine(packet.ToString()); try { switch (packet.PacketType) { case PACKET_TYPE.PKT_TX_ACK: UInt32 token = packet.Token; GatewayTxAck data = string.IsNullOrEmpty(packet.Json) ? null : JsonConvert.DeserializeObject <GatewayTxAck>(packet.Json, settings); if (data != null) { // Error Console.WriteLine("ERROR:"); Console.WriteLine(JsonConvert.SerializeObject(data, Formatting.Indented)); if (state.PullRespPackets.ContainsKey(token)) { lock (state.LockPullResp) { GatewayPullResp pull = state.PullRespPackets[token]; if (pull.Retry > 0) { pull.Retry--; pull.TimeToSend = DateTime.Now.AddSeconds(15); pull.State = PULL_RESP_STATE.Ready; } else { state.PullRespPackets.Remove(token); } } } } else { // No error if (state.PullRespPackets.ContainsKey(token)) { lock (state.LockPullResp) state.PullRespPackets.Remove(token); } } break; case PACKET_TYPE.PKT_PULL_DATA: GatewayState gw; UInt64 eui = BitConverter.ToUInt64(packet.EUI, 0); if (state.Gateways.ContainsKey(eui)) { gw = state.Gateways[eui]; } else { gw = new GatewayState(); } gw.EndPoint = packet.Sender; gw.LastTime = DateTime.Now; SendTo(state, packet); break; case PACKET_TYPE.PKT_PUSH_DATA: if (string.IsNullOrEmpty(packet.Json)) { Console.WriteLine("JSON ERROR: Empty payload"); } else { GatewayPushData json = (packet.Version == 1) ? (GatewayPushData)JsonConvert.DeserializeObject <GatewayPushDataV1>(packet.Json, settings) : (GatewayPushData)JsonConvert.DeserializeObject <GatewayPushDataV2>(packet.Json, settings) ; if (json != null) { /* * Console.WriteLine("JSON:"); * Console.WriteLine(JsonConvert.SerializeObject(json, Formatting.Indented)); */ bool success = true; if (json.stat != null) { success &= ProcessingStat(json.stat); } if (json.rxpk != null && json.rxpk.Count > 0) { for (int idx = 0; idx < json.rxpk.Count; idx++) { success &= ProcessingRxpt(json.rxpk[idx]); } } if (success) { // Thread.Sleep(10); SendTo(state, packet); } } } break; default: break; } } catch (Exception ex) { Console.WriteLine(string.Format("Processing error:\n{0}", ex.ToString())); } #endregion } lock (state.LockPackets) if (state.Packets.Count == 0) { state.ResetEvent.Reset(); } if (!state.ResetEvent.WaitOne(500)) { if ((DateTime.Now - keyTime).Seconds >= 5) { keyTime = DateTime.Now; if (Console.KeyAvailable) { while (Console.KeyAvailable) { Console.ReadKey(true); } state.State = SERVER_STATE.Stoped; break; } } } } state.Socket.Shutdown(SocketShutdown.Both); state.Socket.Close(); } catch (Exception e) { Console.WriteLine(e.ToString()); } }
private async Task OnReceive(GatewayPacket packet) { switch (packet.Opcode) { case GatewayOpcode.Hello: { await HandleHello((JsonElement)packet.Payload !); break; } case GatewayOpcode.Heartbeat: { _logger.Debug("Shard {ShardId}: Received heartbeat request from shard, sending Ack", ShardId); await _conn !.Send(new GatewayPacket { Opcode = GatewayOpcode.HeartbeatAck }); break; } case GatewayOpcode.HeartbeatAck: { Latency = DateTimeOffset.UtcNow - _lastHeartbeatSent; _logger.Debug("Shard {ShardId}: Received heartbeat Ack with latency {Latency}", ShardId, Latency); if (Latency != null) { HeartbeatReceived?.Invoke(Latency !.Value); } _hasReceivedAck = true; break; } case GatewayOpcode.Reconnect: { _logger.Information("Shard {ShardId}: Received Reconnect, closing and reconnecting", ShardId); await _conn !.Disconnect(WebSocketCloseStatus.Empty, null); break; } case GatewayOpcode.InvalidSession: { var canResume = ((JsonElement)packet.Payload !).GetBoolean(); // Clear session info before DCing if (!canResume) { SessionInfo = SessionInfo with { Session = null } } ; var delay = TimeSpan.FromMilliseconds(new Random().Next(1000, 5000)); _logger.Information( "Shard {ShardId}: Received Invalid Session (can resume? {CanResume}), reconnecting after {ReconnectDelay}", ShardId, canResume, delay); await _conn !.Disconnect(WebSocketCloseStatus.Empty, null); // Will reconnect after exiting this "loop" await Task.Delay(delay); break; } case GatewayOpcode.Dispatch: { SessionInfo = SessionInfo with { LastSequence = packet.Sequence }; var evt = DeserializeEvent(packet.EventType !, (JsonElement)packet.Payload !) !; if (evt is ReadyEvent rdy) { if (State == ShardState.Connecting) { await HandleReady(rdy); } else { _logger.Warning("Shard {ShardId}: Received Ready event in unexpected state {ShardState}, ignoring?", ShardId, State); } } else if (evt is ResumedEvent) { if (State == ShardState.Connecting) { await HandleResumed(); } else { _logger.Warning("Shard {ShardId}: Received Resumed event in unexpected state {ShardState}, ignoring?", ShardId, State); } } await HandleEvent(evt); break; } default: { _logger.Debug("Shard {ShardId}: Received unknown gateway opcode {Opcode}", ShardId, packet.Opcode); break; } } }