From 567ba2aab1267823178d0f0a6bf6522f10b28996 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 1 Feb 2015 15:59:59 -0700 Subject: [PATCH] Fix several core networking bugs This should make the server much more stable over a longer period of time --- TrueCraft.API/Logic/IItemProvider.cs | 1 + TrueCraft/Handlers/InteractionHandlers.cs | 14 +++++- TrueCraft/MultiplayerServer.cs | 55 +++++++++++++++++++---- TrueCraft/RemoteClient.cs | 4 +- 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/TrueCraft.API/Logic/IItemProvider.cs b/TrueCraft.API/Logic/IItemProvider.cs index 929366b..50c0c56 100644 --- a/TrueCraft.API/Logic/IItemProvider.cs +++ b/TrueCraft.API/Logic/IItemProvider.cs @@ -7,5 +7,6 @@ namespace TrueCraft.API.Logic short ID { get; } sbyte MaximumStack { get; } string DisplayName { get; } + //bool ItemUsedOnBlock(ItemDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user); } } \ No newline at end of file diff --git a/TrueCraft/Handlers/InteractionHandlers.cs b/TrueCraft/Handlers/InteractionHandlers.cs index f2a3ee7..0dd2d5e 100644 --- a/TrueCraft/Handlers/InteractionHandlers.cs +++ b/TrueCraft/Handlers/InteractionHandlers.cs @@ -31,7 +31,9 @@ namespace TrueCraft.Handlers if (c.KnownEntities.Contains(client.Entity)) c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.SwingArm)); } - if (provider.Hardness == 0) + if (provider == null) + server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player digging)", descriptor.ID); + if (provider != null && provider.Hardness == 0) provider.BlockMined(descriptor, packet.Face, world, client); break; case PlayerDiggingPacket.Action.StopDigging: @@ -41,7 +43,8 @@ namespace TrueCraft.Handlers if (c.KnownEntities.Contains(client.Entity)) c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None)); } - provider.BlockMined(descriptor, packet.Face, world, client); + if (provider != null) + provider.BlockMined(descriptor, packet.Face, world, client); break; } } @@ -69,6 +72,13 @@ namespace TrueCraft.Handlers if (block != null) { var provider = server.BlockRepository.GetBlockProvider(block.Value.ID); + if (provider == null) + { + server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player placing)", block.Value.ID); + server.SendMessage(ChatColor.Red + "Error occured from client {0} at coordinates {1}", client.Username, block.Value.Coordinates); + server.SendMessage(ChatColor.Red + "Packet logged at {0}, please report upstream", DateTime.Now); + return; + } if (!provider.BlockRightClicked(block.Value, packet.Face, client.World, client)) { position += MathHelper.BlockFaceToCoordinates(packet.Face); diff --git a/TrueCraft/MultiplayerServer.cs b/TrueCraft/MultiplayerServer.cs index 7009e2c..c5a04c2 100644 --- a/TrueCraft/MultiplayerServer.cs +++ b/TrueCraft/MultiplayerServer.cs @@ -156,8 +156,8 @@ namespace TrueCraft { SendMessage(ChatColor.Yellow + "{0} has left the server.", client.Username); GetEntityManagerForWorld(client.World).DespawnEntity(client.Entity); + client.Disconnected = true; } - Clients.Remove(client); } private void AcceptClient(IAsyncResult result) @@ -184,21 +184,46 @@ namespace TrueCraft bool idle = true; for (int i = 0; i < Clients.Count && i >= 0; i++) { + Console.WriteLine("Running update " + DateTime.Now); var client = Clients[i] as RemoteClient; var sendTimeout = DateTime.Now.AddMilliseconds(200); while (client.PacketQueue.Count != 0 && DateTime.Now < sendTimeout) { idle = false; - IPacket packet; - while (!client.PacketQueue.TryDequeue(out packet)) ; - LogPacket(packet, false); - PacketReader.WritePacket(client.MinecraftStream, packet); - if (packet is DisconnectPacket) + try { + IPacket packet; + while (!client.PacketQueue.TryDequeue(out packet)) ; + LogPacket(packet, false); + PacketReader.WritePacket(client.MinecraftStream, packet); + client.MinecraftStream.BaseStream.Flush(); + if (packet is DisconnectPacket) + { + DisconnectClient(client); + break; + } + } + catch (SocketException e) + { + Log(LogCategory.Debug, "Disconnecting client due to exception in network worker"); + Log(LogCategory.Debug, e.ToString()); + PacketReader.WritePacket(client.MinecraftStream, new DisconnectPacket("An exception has occured on the server.")); + client.MinecraftStream.BaseStream.Flush(); DisconnectClient(client); - i--; break; } + catch (Exception e) + { + Log(LogCategory.Debug, "Disconnecting client due to exception in network worker"); + Log(LogCategory.Debug, e.ToString()); + DisconnectClient(client); + break; + } + } + if (client.Disconnected) + { + Clients.RemoveAt(i); + break; } var receiveTimeout = DateTime.Now.AddMilliseconds(200); while (client.DataAvailable && DateTime.Now < receiveTimeout) @@ -215,7 +240,14 @@ namespace TrueCraft catch (PlayerDisconnectException) { DisconnectClient(client); - i--; + break; + } + catch (SocketException e) + { + Log(LogCategory.Debug, "Disconnecting client due to exception in network worker"); + Log(LogCategory.Debug, e.ToString()); + DisconnectClient(client); + break; } catch (Exception e) { @@ -224,7 +256,7 @@ namespace TrueCraft PacketReader.WritePacket(client.MinecraftStream, new DisconnectPacket("An exception has occured on the server.")); client.MinecraftStream.BaseStream.Flush(); DisconnectClient(client); - i--; + break; } } else @@ -234,6 +266,11 @@ namespace TrueCraft } if (idle) Thread.Sleep(100); + if (client.Disconnected) + { + Clients.RemoveAt(i); + break; + } } } } diff --git a/TrueCraft/RemoteClient.cs b/TrueCraft/RemoteClient.cs index 67c36c1..301e8e2 100644 --- a/TrueCraft/RemoteClient.cs +++ b/TrueCraft/RemoteClient.cs @@ -35,13 +35,15 @@ namespace TrueCraft CurrentWindow = InventoryWindow; ItemStaging = ItemStack.EmptyStack; KnownEntities = new List(); + Disconnected = false; } /// /// A list of entities that this client is aware of. /// internal List KnownEntities { get; set; } - + internal bool Disconnected { get; set; } + public NetworkStream NetworkStream { get; set; } public IMinecraftStream MinecraftStream { get; internal set; } public ConcurrentQueue PacketQueue { get; private set; }