public override void Send(NetworkChunk packet) { if (packet.DataSize > NetworkCore.MAX_PAYLOAD) { Debug.Warning("network", $"packet payload too big, length={packet.DataSize}"); return; } if (packet.Flags.HasFlag(SendFlags.CONNLESS)) { NetworkCore.SendPacketConnless(UdpClient, packet.EndPoint, packet.Data, packet.DataSize); return; } Debug.Assert(packet.ClientId == 0, "wrong client id"); var flags = ChunkFlags.NONE; if (packet.Flags.HasFlag(SendFlags.VITAL)) { flags = ChunkFlags.VITAL; } Connection.QueueChunk(flags, packet.Data, packet.DataSize); if (packet.Flags.HasFlag(SendFlags.FLUSH)) { Connection.Flush(); } }
public override bool Receive(out NetworkChunk packet) { while (true) { if (ChunkReceiver.FetchChunk(out packet)) { return(true); } if (UdpClient.Available <= 0) { return(false); } var remote = (IPEndPoint)null; byte[] data; try { data = UdpClient.Receive(ref remote); } catch { continue; } if (data.Length == 0) { continue; } if (!NetworkCore.UnpackPacket(data, data.Length, ChunkReceiver.ChunkConstruct)) { continue; } if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.CONNLESS)) { packet = new NetworkChunk { ClientId = -1, Flags = SendFlags.CONNLESS, EndPoint = remote, DataSize = ChunkReceiver.ChunkConstruct.DataSize, Data = ChunkReceiver.ChunkConstruct.Data }; return(true); } if (Connection.Feed(ChunkReceiver.ChunkConstruct, remote)) { ChunkReceiver.Start(remote, Connection, 0); } } }
public abstract void Send(NetworkChunk packet);
public abstract bool Receive(out NetworkChunk packet);
public override bool FetchChunk(out NetworkChunk packet) { var header = new NetworkChunkHeader(); var end = ChunkConstruct.DataSize; while (true) { if (!Valid || CurrentChunk >= ChunkConstruct.NumChunks) { Clear(); packet = null; return(false); } var dataOffset = 0; for (var i = 0; i < CurrentChunk; i++) { dataOffset = header.Unpack(ChunkConstruct.Data, dataOffset); dataOffset += header.Size; } dataOffset = header.Unpack(ChunkConstruct.Data, dataOffset); CurrentChunk++; if (dataOffset + header.Size > end) { Clear(); packet = null; return(false); } if (Connection != null && header.Flags.HasFlag(ChunkFlags.VITAL)) { if (Connection.UnknownAck || header.Sequence == (Connection.Ack + 1) % NetworkCore.MAX_SEQUENCE) { Connection.UnknownAck = false; Connection.Ack = (Connection.Ack + 1) % NetworkCore.MAX_SEQUENCE; } else { if (NetworkCore.IsSeqInBackroom(header.Sequence, Connection.Ack)) { continue; } Debug.Log("connection", $"asking for resend {header.Sequence} {(Connection.Ack + 1) % NetworkCore.MAX_SEQUENCE}"); Connection.SignalResend(); continue; } } packet = new NetworkChunk { ClientId = ClientId, EndPoint = EndPoint, Flags = (SendFlags)header.Flags, DataSize = header.Size, Data = new byte[header.Size] }; Buffer.BlockCopy(ChunkConstruct.Data, dataOffset, packet.Data, 0, header.Size); return(true); } }
public abstract bool FetchChunk(out NetworkChunk packet);
public override bool Receive(out NetworkChunk packet) { while (true) { if (ChunkReceiver.FetchChunk(out packet)) { return(true); } if (UdpClient.Available <= 0) { return(false); } var remote = (IPEndPoint)null; byte[] data; try { data = UdpClient.Receive(ref remote); } catch { continue; } if (data.Length <= 0) { continue; } if (!NetworkCore.UnpackPacket(data, data.Length, ChunkReceiver.ChunkConstruct)) { continue; } var useToken = false; var token = (uint)0; if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.TOKEN)) { useToken = true; token = ChunkReceiver.ChunkConstruct.Token; } else if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.CONTROL) && ChunkReceiver.ChunkConstruct.Data[0] == (int)ConnectionMessages.CONNECT && ChunkReceiver.ChunkConstruct.DataSize >= 1 + 512) { useToken = true; token = ChunkReceiver.ChunkConstruct.Data.ToUInt32(5); } if (NetworkBan.IsBanned(remote, out var reason)) { NetworkCore.SendControlMsg(UdpClient, remote, 0, useToken, token, ConnectionMessages.CLOSE, reason); continue; } if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.CONNLESS)) { packet = new NetworkChunk { ClientId = -1, Flags = SendFlags.CONNLESS, EndPoint = remote, DataSize = ChunkReceiver.ChunkConstruct.DataSize, Data = ChunkReceiver.ChunkConstruct.Data }; return(true); } var clientId = FindSlot(remote, true); if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.CONTROL) && ChunkReceiver.ChunkConstruct.Data[0] == (int)ConnectionMessages.CONNECT) { if (clientId != -1) { continue; } if (ChunkReceiver.ChunkConstruct.DataSize >= 1 + 512) { var connectAccept = new byte[4]; GetToken(remote).ToByteArray(connectAccept, 0); NetworkCore.SendControlMsg(UdpClient, remote, 0, true, token, ConnectionMessages.CONNECTACCEPT, connectAccept); Debug.Log("netserver", "got connect, sending connect+accept challenge"); } else if (Config["SvAllowOldClients"].AsBoolean() && string.IsNullOrWhiteSpace(Config["Password"].AsString())) { if (LegacyRateLimit()) { Debug.Log("netserver", "dropping legacy connect due to ratelimit"); continue; } var packets = new NetworkChunkConstruct[] { new NetworkChunkConstruct(), new NetworkChunkConstruct(), }; var legacyToken = GetLegacyToken(remote); ConstructLegacyHandshake(packets[0], packets[1], legacyToken); for (var i = 0; i < 2; i++) { NetworkCore.SendPacket(UdpClient, remote, packets[i]); } Debug.Log("netserver", "got legacy connect, sending legacy challenge"); } else { Debug.Log("netserver", $"dropping short connect packet, size={ChunkReceiver.ChunkConstruct.DataSize}"); } } else { if (clientId == -1) { if (!useToken || !IsCorrectToken(remote, token)) { if (!useToken && Config["SvAllowOldClients"].AsBoolean()) { ChunkReceiver.Start(remote, null, -1); var chunk = new NetworkChunk(); var correct = false; while (ChunkReceiver.FetchChunk(out chunk)) { if (DecodeLegacyHandShake(chunk.Data, chunk.DataSize, out var legacyToken)) { if (IsCorrectLegacyToken(remote, legacyToken)) { correct = true; break; } } } ChunkReceiver.Clear(); if (!correct) { continue; } } else { Debug.Log("netserver", !useToken ? "dropping packet with missing token" : $"dropping packet with invalid token, token={token}"); continue; } } var sameIps = 0; for (var i = 0; i < Connections.Length; i++) { if (Connections[i].State == ConnectionState.OFFLINE) { if (clientId < 0) { clientId = i; } continue; } if (!NetworkCore.CompareEndPoints(Connections[i].EndPoint, remote, false)) { continue; } sameIps++; if (sameIps >= ServerConfig.MaxClientsPerIp) { NetworkCore.SendControlMsg(UdpClient, remote, 0, useToken, token, ConnectionMessages.CLOSE, $"Only {ServerConfig.MaxClientsPerIp} players with the same IP are allowed"); return(false); } } if (clientId < 0) { for (var i = 0; i < Connections.Length; i++) { if (Connections[i].State == ConnectionState.OFFLINE) { clientId = i; break; } } } if (clientId < 0) { NetworkCore.SendControlMsg(UdpClient, remote, 0, useToken, token, ConnectionMessages.CLOSE, "This server is full"); return(false); } if (useToken) { Connections[clientId].Accept(remote, token); } else { Connections[clientId].AcceptLegacy(remote); } NewClientCallback?.Invoke(clientId, !useToken); if (!useToken) { continue; } Connections[clientId].Feed(ChunkReceiver.ChunkConstruct, remote); } if (Connections[clientId].Feed(ChunkReceiver.ChunkConstruct, remote)) { if (ChunkReceiver.ChunkConstruct.DataSize != 0) { ChunkReceiver.Start(remote, Connections[clientId], clientId); } } } } }