From 8d8620047de67e558e82cba296b192c8382a7752 Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 14 Mar 2017 22:33:33 +0200 Subject: [PATCH] Fixed clients who leave a server and re-join becoming desynced and not being kicked by the server. The clients didn't reset their ChatMessage.LastID, which caused an exception to be thrown when clamping the chatmsg ID server-side, preventing the server from updating the lastRecvEntityEventID of the client later in the method. The server wouldn't kick the client, because ServerEntityEventManager wouldn't handle cases where a client is waiting for an event that doesn't exist anymore. --- Subsurface/Source/Networking/GameClient.cs | 1 + Subsurface/Source/Networking/GameServer.cs | 5 +++-- .../NetEntityEvent/ServerEntityEventManager.cs | 17 ++++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 4585383ac..16f9d5121 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -99,6 +99,7 @@ namespace Barotrauma.Networking otherClients = new List(); + ChatMessage.LastID = 0; GameMain.NetLobbyScreen = new NetLobbyScreen(); } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 1be5e1019..6f3314422 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -712,9 +712,10 @@ namespace Barotrauma.Networking if (lastRecvEntityEventID > lastEntityEventID) DebugConsole.ThrowError("client.lastRecvEntityEventID > lastEntityEventID"); #endif + + if (NetIdUtils.IdMoreRecent(lastRecvChatMsgID, c.lastRecvChatMsgID)) c.lastRecvChatMsgID = lastRecvChatMsgID; + if (NetIdUtils.IdMoreRecent(c.lastRecvChatMsgID, c.lastChatMsgQueueID)) c.lastRecvChatMsgID = c.lastChatMsgQueueID; - c.lastRecvChatMsgID = NetIdUtils.Clamp(c.lastRecvChatMsgID, lastRecvChatMsgID, c.lastChatMsgQueueID); - if (NetIdUtils.IdMoreRecent(lastRecvEntitySpawnID, c.lastRecvEntitySpawnID)) c.lastRecvEntitySpawnID = lastRecvEntitySpawnID; if (NetIdUtils.IdMoreRecent(c.lastRecvEntitySpawnID, lastEntitySpawnID)) c.lastRecvEntitySpawnID = lastEntitySpawnID; diff --git a/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index a7a0b419e..f07a0a1b3 100644 --- a/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -72,8 +72,11 @@ namespace Barotrauma.Networking var newEvent = new ServerEntityEvent(entity, (UInt16)(ID + 1)); if (extraData != null) newEvent.SetData(extraData); - - events.RemoveAll(e => NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll+1),e.ID)); //remove events that have been sent to all clients, they are redundant now + + //remove events that have been sent to all clients, they are redundant now + //keep at least one event in the list (lastSentToAll == e.ID) so we can use it to keep track of the latest ID + events.RemoveAll(e => NetIdUtils.IdMoreRecent(lastSentToAll, e.ID)); + for (int i = events.Count - 1; i >= 0; i--) { //we already have an identical event that's waiting to be sent @@ -139,6 +142,14 @@ namespace Barotrauma.Networking if (toKick != null) toKick.ForEach(c => server.DisconnectClient(c, "", "You have been disconnected because of excessive desync")); } + + if (events.Count > 0) + { + //the client is waiting for an event that we don't have anymore + //(the ID they're expecting is smaller than the ID of the first event in our list) + List toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.lastRecvEntityEventID+1))); + if (toKick != null) toKick.ForEach(c => server.DisconnectClient(c, "", "You have been disconnected because of excessive desync")); + } } var timedOutClients = clients.FindAll(c => c.inGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut); @@ -229,7 +240,7 @@ namespace Barotrauma.Networking { startIndex--; } - + for (int i = startIndex; i < eventList.Count; i++) { //find the first event that hasn't been sent in 1.5 * roundtriptime or at all