From 0301457a8f11e53c360892e8e6fb2f4edd84fd63 Mon Sep 17 00:00:00 2001 From: Regalis Date: Sun, 9 Apr 2017 19:00:33 +0300 Subject: [PATCH] A fix to occasional "unknown object header" errors. If an entity had been removed and it's ID taken by some other entity, clients who hadn't received a message about the removal and the new entity would end up reading ENTITY_POSITION messages for the new entity incorrectly. Reading an incorrect number of bits from the message would also prevent the clients from reading the rest of the data in the packet properly. Now the server doesn't send position updates to clients during midround syncing (because there's no guarantee they know about the entity yet). Clients also force the read position to the correct place after reading a position update in case something goes wrong when reading the msg. --- .../Source/Characters/CharacterNetworking.cs | 11 ++- Subsurface/Source/Networking/GameClient.cs | 13 ++-- Subsurface/Source/Networking/GameServer.cs | 67 ++++++++++--------- 3 files changed, 47 insertions(+), 44 deletions(-) diff --git a/Subsurface/Source/Characters/CharacterNetworking.cs b/Subsurface/Source/Characters/CharacterNetworking.cs index 176666535..dd2e18618 100644 --- a/Subsurface/Source/Characters/CharacterNetworking.cs +++ b/Subsurface/Source/Characters/CharacterNetworking.cs @@ -425,11 +425,9 @@ namespace Barotrauma tempBuffer.Write(aiming); tempBuffer.Write(use); - if (AnimController.Limbs.Any(l => l != null && l.attack != null)) - { - tempBuffer.Write(attack); - } - + bool hasAttackLimb = AnimController.Limbs.Any(l => l != null && l.attack != null); + tempBuffer.Write(hasAttackLimb); + if (hasAttackLimb) tempBuffer.Write(attack); if (aiming) { @@ -491,7 +489,8 @@ namespace Barotrauma keys[(int)InputType.Use].Held = useInput; keys[(int)InputType.Use].SetState(false, useInput); - if (AnimController.Limbs.Any(l => l != null && l.attack != null)) + bool hasAttackLimb = msg.ReadBoolean(); + if (hasAttackLimb) { bool attackInput = msg.ReadBoolean(); keys[(int)InputType.Attack].Held = attackInput; diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 63d03c439..e1d015833 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -921,18 +921,17 @@ namespace Barotrauma.Networking UInt16 id = inc.ReadUInt16(); byte msgLength = inc.ReadByte(); + long msgEndPos = inc.Position + msgLength * 8; + var entity = Entity.FindEntityByID(id) as IServerSerializable; - if (entity == null) - { - //skip through the rest of the message - inc.Position += msgLength * 8; - } - else + if (entity != null) { entity.ClientRead(objHeader, inc, sendingTime); } - inc.ReadPadBits(); + //force to the correct position in case the entity doesn't exist + //or the message wasn't read correctly for whatever reason + inc.Position = msgEndPos; break; case ServerNetObject.ENTITY_EVENT: case ServerNetObject.ENTITY_EVENT_INITIAL: diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 7f8569503..554ada652 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -703,12 +703,12 @@ namespace Barotrauma.Networking #if DEBUG //client thinks they've received a msg we haven't sent yet (corrupted packet, msg read/written incorrectly?) if (NetIdUtils.IdMoreRecent(lastRecvChatMsgID, c.lastChatMsgQueueID)) - DebugConsole.ThrowError("client.lastRecvChatMsgID > lastChatMsgQueueID"); - + DebugConsole.ThrowError("client.lastRecvChatMsgID > lastChatMsgQueueID (" + c.lastRecvChatMsgID + " > " + c.lastChatMsgQueueID + ")"); + if (lastRecvEntityEventID > lastEntityEventID) - DebugConsole.ThrowError("client.lastRecvEntityEventID > lastEntityEventID"); + DebugConsole.ThrowError("client.lastRecvEntityEventID > lastEntityEventID (" + c.lastRecvEntityEventID + " > " + lastEntityEventID + ")"); #endif - + if (NetIdUtils.IdMoreRecent(lastRecvChatMsgID, c.lastRecvChatMsgID)) c.lastRecvChatMsgID = lastRecvChatMsgID; if (NetIdUtils.IdMoreRecent(c.lastRecvChatMsgID, c.lastChatMsgQueueID)) c.lastRecvChatMsgID = c.lastChatMsgQueueID; @@ -833,40 +833,45 @@ namespace Barotrauma.Networking cMsg.ServerWrite(outmsg, c); } - foreach (Character character in Character.CharacterList) + //don't send position updates to characters who are still midround syncing + //characters or items spawned mid-round don't necessarily exist at the client's end yet + if (!c.NeedsMidRoundSync) { - if (!character.Enabled) continue; - - if (c.Character != null && - Vector2.DistanceSquared(character.WorldPosition, c.Character.WorldPosition) >= - NetConfig.CharacterIgnoreDistance * NetConfig.CharacterIgnoreDistance) + foreach (Character character in Character.CharacterList) { - continue; + if (!character.Enabled) continue; + + if (c.Character != null && + Vector2.DistanceSquared(character.WorldPosition, c.Character.WorldPosition) >= + NetConfig.CharacterIgnoreDistance * NetConfig.CharacterIgnoreDistance) + { + continue; + } + + outmsg.Write((byte)ServerNetObject.ENTITY_POSITION); + character.ServerWrite(outmsg, c); + outmsg.WritePadBits(); } - outmsg.Write((byte)ServerNetObject.ENTITY_POSITION); - character.ServerWrite(outmsg, c); - outmsg.WritePadBits(); - } + foreach (Submarine sub in Submarine.Loaded) + { + //if docked to a sub with a smaller ID, don't send an update + // (= update is only sent for the docked sub that has the smallest ID, doesn't matter if it's the main sub or a shuttle) + if (sub.DockedTo.Any(s => s.ID < sub.ID)) continue; - foreach (Submarine sub in Submarine.Loaded) - { - //if docked to a sub with a smaller ID, don't send an update - // (= update is only sent for the docked sub that has the smallest ID, doesn't matter if it's the main sub or a shuttle) - if (sub.DockedTo.Any(s => s.ID < sub.ID)) continue; + outmsg.Write((byte)ServerNetObject.ENTITY_POSITION); + sub.ServerWrite(outmsg, c); + outmsg.WritePadBits(); + } - outmsg.Write((byte)ServerNetObject.ENTITY_POSITION); - sub.ServerWrite(outmsg, c); - outmsg.WritePadBits(); - } + foreach (Item item in Item.ItemList) + { + if (!item.NeedsPositionUpdate) continue; - foreach (Item item in Item.ItemList) - { - if (!item.NeedsPositionUpdate) continue; - - outmsg.Write((byte)ServerNetObject.ENTITY_POSITION); - item.ServerWritePosition(outmsg, c); - outmsg.WritePadBits(); + outmsg.Write((byte)ServerNetObject.ENTITY_POSITION); + item.ServerWritePosition(outmsg, c); + outmsg.WritePadBits(); + } } entityEventManager.Write(c, outmsg);