From e0875e09241e211b1d60169d1eed5ab962951a75 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 28 Jul 2017 12:01:10 +1000 Subject: [PATCH] Get rid of unnecessary allocations in receiving packets. This reduces pressure on the GC for when many players are connected. --- MCGalaxy/Commands/Economy/CmdBuy.cs | 1 - MCGalaxy/Events/PlayerEvents.cs | 2 +- MCGalaxy/Network/Socket/TcpSocket.cs | 30 +++++++++++++++---------- MCGalaxy/Player/Player.Handlers.cs | 33 ++++++++++++++-------------- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/MCGalaxy/Commands/Economy/CmdBuy.cs b/MCGalaxy/Commands/Economy/CmdBuy.cs index 3e5b68b45..3dc168408 100644 --- a/MCGalaxy/Commands/Economy/CmdBuy.cs +++ b/MCGalaxy/Commands/Economy/CmdBuy.cs @@ -33,7 +33,6 @@ namespace MCGalaxy.Commands.Eco { Item item = Economy.GetItem(parts[0]); if (item == null) { Help(p); return; } - if (!item.Enabled) { Player.Message(p, "%cThe {0} item is not currently buyable.", item.Name); return; } diff --git a/MCGalaxy/Events/PlayerEvents.cs b/MCGalaxy/Events/PlayerEvents.cs index 0c2e169ee..bf2d0bb7e 100644 --- a/MCGalaxy/Events/PlayerEvents.cs +++ b/MCGalaxy/Events/PlayerEvents.cs @@ -22,7 +22,7 @@ namespace MCGalaxy.Events.PlayerEvents { public delegate void OnPlayerChat(Player p, string message); /// Called whenever a player chats on the server. - /// You must cancel this event to prevent the message being sent to the user. + /// You must cancel this event to prevent the message being sent to the user (and others). public sealed class OnPlayerChatEvent : IEvent { public static void Call(Player p, string message) { diff --git a/MCGalaxy/Network/Socket/TcpSocket.cs b/MCGalaxy/Network/Socket/TcpSocket.cs index e3d409e7e..f7883e27f 100644 --- a/MCGalaxy/Network/Socket/TcpSocket.cs +++ b/MCGalaxy/Network/Socket/TcpSocket.cs @@ -22,8 +22,10 @@ namespace MCGalaxy.Network { public sealed class TcpSocket : INetworkSocket { readonly Player player; readonly Socket socket; - byte[] unprocessed = new byte[0]; + + byte[] unprocessed = new byte[352]; byte[] recvBuffer = new byte[256]; + int unprocessedLen; public TcpSocket(Player p, Socket s) { player = p; socket = s; @@ -48,19 +50,25 @@ namespace MCGalaxy.Network { if (p.disconnected) return; try { - int length = s.socket.EndReceive(result); - if (length == 0) { p.Disconnect(); return; } + int recvLen = s.socket.EndReceive(result); + if (recvLen == 0) { p.Disconnect(); return; } - byte[] allData = new byte[s.unprocessed.Length + length]; - Buffer.BlockCopy(s.unprocessed, 0, allData, 0, s.unprocessed.Length); - Buffer.BlockCopy(s.recvBuffer, 0, allData, s.unprocessed.Length, length); - s.unprocessed = p.ProcessReceived(allData); + // Packets may not always be fully received in a Receive call + // As such, we may need to retain a little bit of partial packet data + Buffer.BlockCopy(s.recvBuffer, 0, s.unprocessed, s.unprocessedLen, recvLen); + s.unprocessedLen += recvLen; + int processedLen = p.ProcessReceived(s.unprocessed, s.unprocessedLen); - if (p.nonPlayerClient && s.unprocessed.Length == 0) { - s.Close(); - p.disconnected = true; - return; + // Disconnect invalid clients + if (p.nonPlayerClient && processedLen == -1) { s.Close(); p.disconnected = true; } + if (processedLen == -1) return; + + // move remaining partial packet data back to start of unprocessed buffer + for (int i = processedLen; i < s.unprocessedLen; i++) { + s.unprocessed[i - processedLen] = s.unprocessed[i]; } + s.unprocessedLen -= processedLen; + if (!p.disconnected) s.ReceiveNextAsync(); } catch (SocketException) { p.Disconnect(); diff --git a/MCGalaxy/Player/Player.Handlers.cs b/MCGalaxy/Player/Player.Handlers.cs index 1629b873e..66b6407af 100644 --- a/MCGalaxy/Player/Player.Handlers.cs +++ b/MCGalaxy/Player/Player.Handlers.cs @@ -185,28 +185,27 @@ namespace MCGalaxy { return block == Block.Air || (block >= Block.Water && block <= Block.StillLava); } - internal byte[] ProcessReceived(byte[] buffer) { + internal int ProcessReceived(byte[] buffer, int bufferLen) { + int processedLen = 0; try { - int size = PacketSize(buffer); - if (size == -2) return new byte[1]; // WoM get request - if (size == -1) return new byte[0]; // invalid packet - - if (buffer.Length < size) return buffer; - HandlePacket(buffer, 0); - if (buffer.Length == size) return new byte[0]; - - byte[] remaining = new byte[buffer.Length - size]; - Buffer.BlockCopy(buffer, size, remaining, 0, remaining.Length); - return ProcessReceived(remaining); + while (processedLen < bufferLen) { + int packetLen = PacketSize(buffer[processedLen]); + if (packetLen == -1) return -1; + + // Partial packet data received + if (processedLen + packetLen > bufferLen) return processedLen; + HandlePacket(buffer, processedLen); + processedLen += packetLen; + } } catch (Exception ex) { Logger.LogError(ex); } - return buffer; + return processedLen; } - int PacketSize(byte[] buffer) { - switch (buffer[0]) { - case (byte)'G': return -2; //For wom + int PacketSize(byte opcode) { + switch (opcode) { + case (byte)'G': return -1; // HTTP GET, ignore it case Opcode.Handshake: return 131; case Opcode.SetBlockClient: if (!loggedIn) goto default; @@ -226,7 +225,7 @@ namespace MCGalaxy { default: if (!nonPlayerClient) { - string msg = "Unhandled message id \"" + buffer[0] + "\"!"; + string msg = "Unhandled message id \"" + opcode + "\"!"; Leave(msg, msg, true); } return -1;