From bf87006bc369f11678932c616d1a4e39f75a185f Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Wed, 12 Sep 2018 15:22:52 +0300 Subject: [PATCH] Attempt to fix items dropping client-side when moving them from inventory another (#558). The clients delay applying the received remote state of an inventory they just put an item inside, because otherwise the inventory may revert to an old state if items are moved in rapid succession. I think the problem was that the delay was not applied to the inventory the item is _taken from_, so it was possible that the clients applied an old delayed state even though an item had just been moved out of the inventory. Another problem was that the server didn't create an event for the inventory the item was removed from, so if there were any wrong items in a client-side inventory, they would just be dropped instead of being moved back to the correct inventory. --- .../Source/Items/Inventory.cs | 43 ++++++++++++++++--- .../Source/Items/ItemInventory.cs | 1 + 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs b/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs index 9ffc74290..037a80876 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs @@ -20,7 +20,7 @@ namespace Barotrauma public bool Locked; private ushort[] receivedItemIDs; - private float syncItemsDelay; + protected float syncItemsDelay; private CoroutineHandle syncItemsCoroutine; public Inventory(Entity owner, int capacity, Vector2? centerPos = null, int slotsPerRow=5) @@ -116,6 +116,8 @@ namespace Barotrauma { if (Owner == null) return; + Inventory prevInventory = item.ParentInventory; + if (removeItem) { item.Drop(user); @@ -137,6 +139,8 @@ namespace Barotrauma if (createNetworkEvent) { CreateNetworkEvent(); + //also delay syncing the inventory the item was inside + if (prevInventory != null && prevInventory != this) prevInventory.syncItemsDelay = 1.0f; } } @@ -148,7 +152,8 @@ namespace Barotrauma } #if CLIENT else if (GameMain.Client != null) - { + { + syncItemsDelay = 1.0f; GameMain.Client.CreateEntityEvent(Owner as IClientSerializable, new object[] { NetEntityEvent.Type.InventoryState }); } #endif @@ -189,8 +194,6 @@ namespace Barotrauma public void ClientWrite(NetBuffer msg, object[] extraData = null) { ServerWrite(msg, null); - - syncItemsDelay = 1.0f; } public void ServerRead(ClientNetObject type, NetBuffer msg, Barotrauma.Networking.Client c) @@ -203,18 +206,40 @@ namespace Barotrauma newItemIDs[i] = msg.ReadUInt16(); } - if (c == null || c.Character == null || !c.Character.CanAccessInventory(this)) return; + if (c == null || c.Character == null) return; + + if (!c.Character.CanAccessInventory(this)) + { + //create a network event to correct the client's inventory state + //otherwise they may have an item in their inventory they shouldn't have been able to pick up, + //and receiving an event for that inventory later will cause the item to be dropped + CreateNetworkEvent(); + for (int i = 0; i < capacity; i++) + { + var item = Entity.FindEntityByID(newItemIDs[i]) as Item; + if (item == null) continue; + if (item.ParentInventory != null && item.ParentInventory != this) + { + item.ParentInventory.CreateNetworkEvent(); + } + } + return; + } + + List prevItemInventories = new List(Items.Select(i => i?.ParentInventory)); for (int i = 0; i < capacity; i++) { - if (newItemIDs[i] == 0 || (Entity.FindEntityByID(newItemIDs[i]) as Item != Items[i])) + Item newItem = newItemIDs[i] == 0 ? null : Entity.FindEntityByID(newItemIDs[i]) as Item; + prevItemInventories.Add(newItem?.ParentInventory); + + if (newItemIDs[i] == 0 || (newItem != Items[i])) { if (Items[i] != null) Items[i].Drop(); System.Diagnostics.Debug.Assert(Items[i] == null); } } - for (int i = 0; i < capacity; i++) { if (newItemIDs[i] > 0) @@ -234,6 +259,10 @@ namespace Barotrauma } CreateNetworkEvent(); + foreach (Inventory prevInventory in prevItemInventories.Distinct()) + { + if (prevInventory != this) prevInventory?.CreateNetworkEvent(); + } foreach (Item item in Items.Distinct()) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs b/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs index eb6039bc6..71e12115b 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs @@ -101,6 +101,7 @@ namespace Barotrauma #if CLIENT else if (GameMain.Client != null) { + syncItemsDelay = 1.0f; GameMain.Client.CreateEntityEvent(Owner as IClientSerializable, new object[] { NetEntityEvent.Type.InventoryState, componentIndex }); } #endif