From 0233579e37c6dcb4248ef72f6e6ce52f289c96fa Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 21 Oct 2015 01:39:00 +0300 Subject: [PATCH] ItemInventories don't have own ID's anymore but rely on owner ID, relaying reliablemessages through server --- Subsurface/Source/Characters/AICharacter.cs | 8 +- Subsurface/Source/Characters/Character.cs | 13 ++- Subsurface/Source/Items/CharacterInventory.cs | 14 +-- .../Source/Items/Components/ItemContainer.cs | 2 +- Subsurface/Source/Items/Inventory.cs | 27 ++++-- Subsurface/Source/Items/Item.cs | 9 ++ Subsurface/Source/Items/ItemInventory.cs | 4 +- Subsurface/Source/Networking/GameClient.cs | 2 - Subsurface/Source/Networking/GameServer.cs | 86 +++++++++++------- Subsurface/Source/Networking/NetworkEvent.cs | 2 +- .../Source/Networking/ReliableSender.cs | 22 ++--- Subsurface_Solution.v12.suo | Bin 781824 -> 799232 bytes 12 files changed, 118 insertions(+), 71 deletions(-) diff --git a/Subsurface/Source/Characters/AICharacter.cs b/Subsurface/Source/Characters/AICharacter.cs index 69dd89a79..442662e78 100644 --- a/Subsurface/Source/Characters/AICharacter.cs +++ b/Subsurface/Source/Characters/AICharacter.cs @@ -95,8 +95,8 @@ namespace Barotrauma message.Write(LargeUpdateTimer <= 0); message.Write(AnimController.TargetDir == Direction.Right); - message.Write(AnimController.TargetMovement.X); - message.Write(AnimController.TargetMovement.Y); + message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.X, -10.0f, 10.0f), -10.0f, 10.0f, 16); + message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.Y, -10.0f, 10.0f), -10.0f, 10.0f, 16); if (LargeUpdateTimer <= 0) { @@ -165,8 +165,8 @@ namespace Barotrauma try { targetDir = message.ReadBoolean(); - targetMovement.X = message.ReadFloat(); - targetMovement.Y = message.ReadFloat(); + targetMovement.X = message.ReadRangedSingle(-10.0f, 10.0f, 8); + targetMovement.Y = message.ReadRangedSingle(-10.0f, 10.0f, 8); } catch diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 98fc9dfe8..3248a7289 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -1100,6 +1100,11 @@ namespace Barotrauma { return true; } + else if (type == NetworkEventType.InventoryUpdate) + { + if (inventory == null) return false; + return inventory.FillNetworkData(NetworkEventType.InventoryUpdate, message, data); + } var hasInputs = (GetInputState(InputType.Left) || @@ -1229,6 +1234,12 @@ namespace Barotrauma } return; } + else if (type == NetworkEventType.InventoryUpdate) + { + if (inventory == null) return; + inventory.ReadNetworkData(NetworkEventType.InventoryUpdate, message); + return; + } bool actionKeyState = false; bool secondaryKeyState = false; @@ -1393,8 +1404,6 @@ namespace Barotrauma if (GameMain.Client!=null && GameMain.Client.Character == this) GameMain.Client.Character = null; - if (inventory != null) inventory.Remove(); - if (aiTarget != null) aiTarget.Remove(); diff --git a/Subsurface/Source/Items/CharacterInventory.cs b/Subsurface/Source/Items/CharacterInventory.cs index 6a17ab096..e17b11c50 100644 --- a/Subsurface/Source/Items/CharacterInventory.cs +++ b/Subsurface/Source/Items/CharacterInventory.cs @@ -25,15 +25,14 @@ namespace Barotrauma private Vector2[] slotPositions; public CharacterInventory(int capacity, Character character) - : base(capacity) + : base(character, capacity) { this.character = character; if (icons == null) icons = TextureLoader.FromFile("Content/UI/inventoryIcons.png"); slotPositions = new Vector2[limbSlots.Length]; - - + int rectWidth = 40, rectHeight = 40; int spacing = 10; for (int i = 0; i < slotPositions.Length; i++) @@ -123,16 +122,19 @@ namespace Barotrauma if (allowedSlots.HasFlag(limbSlots[i]) && items[i]!=null) return false; } + bool placed = false; for (int i = 0; i < capacity; i++) { if (allowedSlots.HasFlag(limbSlots[i]) && items[i] == null) { - PutItem(item, i, createNetworkEvent); + PutItem(item, i, createNetworkEvent, !placed); item.Equip(character); - return true; + placed = true; } } + if (placed) return true; + if (allowedSlots.HasFlag(LimbSlot.BothHands)) TryPutItem(item, 3, createNetworkEvent); return false; @@ -158,7 +160,7 @@ namespace Barotrauma Inventory otherInventory = items[i].inventory; if (otherInventory!=null) { - new Networking.NetworkEvent(Networking.NetworkEventType.InventoryUpdate, otherInventory.ID, true); + new Networking.NetworkEvent(Networking.NetworkEventType.InventoryUpdate, otherInventory.Owner.ID, true); } combined = true; diff --git a/Subsurface/Source/Items/Components/ItemContainer.cs b/Subsurface/Source/Items/Components/ItemContainer.cs index 4abc5e8b5..0bb746595 100644 --- a/Subsurface/Source/Items/Components/ItemContainer.cs +++ b/Subsurface/Source/Items/Components/ItemContainer.cs @@ -89,7 +89,7 @@ namespace Barotrauma.Items.Components public ItemContainer(Item item, XElement element) : base (item, element) { - inventory = new ItemInventory(this, capacity, hudPos, slotsPerRow); + inventory = new ItemInventory(item, this, capacity, hudPos, slotsPerRow); containableItems = new List(); foreach (XElement subElement in element.Elements()) diff --git a/Subsurface/Source/Items/Inventory.cs b/Subsurface/Source/Items/Inventory.cs index 70df9f8f4..4f34771ac 100644 --- a/Subsurface/Source/Items/Inventory.cs +++ b/Subsurface/Source/Items/Inventory.cs @@ -8,11 +8,13 @@ using System; namespace Barotrauma { - class Inventory : Entity + class Inventory { public static Item draggingItem; public static Item doubleClickedItem; + public readonly Entity Owner; + private int slotsPerRow; public int SlotsPerRow @@ -39,10 +41,12 @@ namespace Barotrauma public Item[] items; - public Inventory(int capacity, Vector2? centerPos = null, int slotsPerRow=5) + public Inventory(Entity owner, int capacity, Vector2? centerPos = null, int slotsPerRow=5) { this.capacity = capacity; + this.Owner = owner; + this.slotsPerRow = slotsPerRow; items = new Item[capacity]; @@ -95,6 +99,7 @@ namespace Barotrauma public virtual bool TryPutItem(Item item, int i, bool createNetworkEvent = true) { + if (Owner == null) return false; if (CanBePut(item,i)) { PutItem(item, i, createNetworkEvent); @@ -108,6 +113,8 @@ namespace Barotrauma protected void PutItem(Item item, int i, bool createNetworkEvent, bool removeItem = true) { + if (Owner == null) return; + if (item.inventory != null && removeItem) { item.Drop(); @@ -121,7 +128,7 @@ namespace Barotrauma item.body.Enabled = false; } - if (createNetworkEvent) new NetworkEvent(NetworkEventType.InventoryUpdate, ID, true); + if (createNetworkEvent) new NetworkEvent(NetworkEventType.InventoryUpdate, Owner.ID, true); } public void RemoveItem(Item item) @@ -136,7 +143,6 @@ namespace Barotrauma protected virtual void DropItem(Item item) { - item.Drop(null, false); return; } @@ -185,8 +191,11 @@ namespace Barotrauma } else { - int[] data = { draggingItem.ID, -1 }; - new NetworkEvent(NetworkEventType.InventoryUpdate, ID, true, data); + if (Owner!=null) + { + int[] data = { draggingItem.ID, -1 }; + new NetworkEvent(NetworkEventType.InventoryUpdate, Owner.ID, true, data); + } DropItem(draggingItem); } @@ -279,7 +288,7 @@ namespace Barotrauma spriteBatch.DrawString(GUI.Font, (int)item.Condition + " %", new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), Color.Red); } - public override bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + public bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { for (int i = 0; i(); + if (itemContainer == null || itemContainer.inventory == null) return false; + return itemContainer.inventory.FillNetworkData(NetworkEventType.InventoryUpdate, message, data); case NetworkEventType.UpdateComponent: int componentIndex = (int)data; @@ -1233,6 +1237,11 @@ namespace Barotrauma SetTransform(newSimPos, body.Rotation); Drop(null, false); break; + case NetworkEventType.InventoryUpdate: + var itemContainer = GetComponent(); + if (itemContainer == null || itemContainer.inventory == null) return; + itemContainer.inventory.ReadNetworkData(NetworkEventType.DropItem, message); + break; case NetworkEventType.UpdateComponent: int componentIndex = message.ReadByte(); if (componentIndex < 0 || componentIndex > components.Count - 1) return; diff --git a/Subsurface/Source/Items/ItemInventory.cs b/Subsurface/Source/Items/ItemInventory.cs index af904fcf0..c15ba1478 100644 --- a/Subsurface/Source/Items/ItemInventory.cs +++ b/Subsurface/Source/Items/ItemInventory.cs @@ -7,8 +7,8 @@ namespace Barotrauma { ItemContainer container; - public ItemInventory(ItemContainer container, int capacity, Vector2? centerPos = null, int slotsPerRow = 5) - : base(capacity, centerPos, slotsPerRow) + public ItemInventory(Item owner, ItemContainer container, int capacity, Vector2? centerPos = null, int slotsPerRow = 5) + : base(owner, capacity, centerPos, slotsPerRow) { this.container = container; } diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index a7120cbaf..69131d260 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -649,7 +649,6 @@ namespace Barotrauma.Networking string newName = inc.ReadString(); int ID = inc.ReadInt32(); bool isFemale = inc.ReadBoolean(); - int inventoryID = inc.ReadInt32(); int headSpriteID = inc.ReadInt32(); @@ -683,7 +682,6 @@ namespace Barotrauma.Networking new Character(ch, closestWaypoint, !isMyCharacter); character.ID = ID; - character.Inventory.ID = inventoryID; character.GiveJobItems(closestWaypoint); diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 71d779827..cf492b216 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -282,10 +282,10 @@ namespace Barotrauma.Networking { ReadMessage(inc); } - catch + catch (Exception e) { #if DEBUG - DebugConsole.ThrowError("Failed to read incoming message"); + DebugConsole.ThrowError("Failed to read incoming message", e); #endif continue; @@ -440,10 +440,13 @@ namespace Barotrauma.Networking return; } + bool isReliable = false; if (packetType == (byte)PacketTypes.ReliableMessage) { if (!dataSender.ReliableChannel.CheckMessage(inc)) return; packetType = inc.ReadByte(); + + isReliable = true; } switch (packetType) @@ -452,21 +455,40 @@ namespace Barotrauma.Networking if (!gameStarted) break; if (!NetworkEvent.ReadData(inc)) break; - outmsg = server.CreateMessage(); - outmsg.Write(inc); - - List recipients = new List(); - - foreach (Client client in connectedClients) - { - if (client.Connection == inc.SenderConnection) continue; - if (!client.inGame) continue; - - recipients.Add(client.Connection); - } - + List recipients = connectedClients.FindAll(c => c.Connection != inc.SenderConnection && c.inGame); if (recipients.Count == 0) break; - server.SendMessage(outmsg, recipients, inc.DeliveryMethod, 0); + + //foreach (Client client in connectedClients) + //{ + // if (client.Connection == inc.SenderConnection) continue; + // if (!client.inGame) continue; + + // recipients.Add(client.Connection); + //} + + if (isReliable) + { + Debug.WriteLine("receiver reliable networkevent"); + foreach (Client c in recipients) + { + var reliableMessage = c.ReliableChannel.CreateMessage(); + inc.Position = 8+16; + byte[] messageBytes = inc.ReadBytes(inc.LengthBytes-3); + reliableMessage.InnerMessage.Write(messageBytes); + + c.ReliableChannel.SendMessage(reliableMessage, c.Connection); + } + } + else + { + outmsg = server.CreateMessage(); + outmsg.Write(inc); + + List recipientConnections = new List(); + foreach (Client c in recipients) recipientConnections.Add(c.Connection); + + server.SendMessage(outmsg, recipientConnections, inc.DeliveryMethod, 0); + } break; case (byte)PacketTypes.Chatmessage: @@ -628,36 +650,35 @@ namespace Barotrauma.Networking if (recipients.Count == 0) return; + + foreach (NetworkEvent networkEvent in NetworkEvent.events) { - Entity e = Entity.FindEntityByID(networkEvent.ID); - if (e == null) continue; + NetOutgoingMessage message = server.CreateMessage(); + message.Write((byte)PacketTypes.NetworkEvent); + //if (!networkEvent.IsClient) continue; + if (!networkEvent.FillData(message)) + { + continue; + } + + //Entity e = Entity.FindEntityByID(networkEvent.ID); + //if (e == null) continue; if (networkEvent.IsImportant) { foreach (Client c in recipients) { ReliableMessage reliableMessage = c.ReliableChannel.CreateMessage(); - reliableMessage.InnerMessage.Write((byte)PacketTypes.NetworkEvent); - - if (!networkEvent.FillData(reliableMessage.InnerMessage)) - { - break; - } + message.Position = 0; + reliableMessage.InnerMessage.Write(message.ReadBytes(message.LengthBytes)); c.ReliableChannel.SendMessage(reliableMessage, c.Connection); } } else { - NetOutgoingMessage message = server.CreateMessage(); - message.Write((byte)PacketTypes.NetworkEvent); - //if (!networkEvent.IsClient) continue; - - if (!networkEvent.FillData(message)) - { - continue; - } + if (server.ConnectionsCount>0) { @@ -1050,7 +1071,6 @@ namespace Barotrauma.Networking message.Write(name); message.Write(character.ID); message.Write(character.Info.Gender == Gender.Female); - message.Write(character.Inventory.ID); message.Write(character.Info.HeadSpriteId); diff --git a/Subsurface/Source/Networking/NetworkEvent.cs b/Subsurface/Source/Networking/NetworkEvent.cs index 0f97eeea5..d511149a2 100644 --- a/Subsurface/Source/Networking/NetworkEvent.cs +++ b/Subsurface/Source/Networking/NetworkEvent.cs @@ -129,7 +129,7 @@ namespace Barotrauma.Networking return false; } - System.Diagnostics.Debug.WriteLine("Networkevent entity: "+e.ToString()); + //System.Diagnostics.Debug.WriteLine("Networkevent entity: "+e.ToString()); //System.Diagnostics.Debug.WriteLine("new message: " + eventType +" - "+e); try diff --git a/Subsurface/Source/Networking/ReliableSender.cs b/Subsurface/Source/Networking/ReliableSender.cs index ba210d44d..290e32742 100644 --- a/Subsurface/Source/Networking/ReliableSender.cs +++ b/Subsurface/Source/Networking/ReliableSender.cs @@ -79,16 +79,14 @@ namespace Barotrauma.Networking.ReliableMessages public ReliableMessage CreateMessage() { - if (messageCount == ushort.MaxValue) messageCount = 0; - messageCount++; + ushort messageID = (messageCount==ushort.MaxValue) ? (ushort)0 : (ushort)(messageCount + 1); NetOutgoingMessage message = sender.CreateMessage(); - var reliableMessage = new ReliableMessage(message, messageCount); - messageBuffer.Add(reliableMessage.ID, reliableMessage); + var reliableMessage = new ReliableMessage(message, messageID); message.Write((byte)PacketTypes.ReliableMessage); - message.Write(messageCount); + message.Write(messageID); int bufferSize=100; if (messageBuffer.Count>bufferSize) @@ -117,10 +115,7 @@ namespace Barotrauma.Networking.ReliableMessages messageBuffer.Remove(i); if (i == ushort.MaxValue) break; Debug.WriteLine("removing message " + i); - } - - - + } } return reliableMessage; @@ -133,6 +128,11 @@ namespace Barotrauma.Networking.ReliableMessages ackInterval = 0.0f; ackTimer = connection.AverageRoundtripTime; + messageBuffer.Add(message.ID, message); + + if (messageCount == ushort.MaxValue) messageCount = 0; + messageCount++; + message.SaveInnerMessage(); sender.SendMessage(message.InnerMessage, connection, NetDeliveryMethod.Unreliable, 0); @@ -172,7 +172,7 @@ namespace Barotrauma.Networking.ReliableMessages if (ackTimer > 0.0f) return; - Debug.WriteLine("Sending ack message: "+messageCount); + //Debug.WriteLine("Sending ack message: "+messageCount); NetOutgoingMessage message = sender.CreateMessage(); message.Write((byte)PacketTypes.Ack); @@ -337,7 +337,7 @@ namespace Barotrauma.Networking.ReliableMessages //id matches, all good if (messageId == lastMessageID) { - Debug.WriteLine("Received ack message: " + messageId + ", all good"); + //Debug.WriteLine("Received ack message: " + messageId + ", all good"); return; } diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 0d1731dae59b36d9f1872340385a2b0f1003efeb..20d8381f592f9c8f360e1fdfd36d9ba48e7829b7 100644 GIT binary patch delta 13150 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 M3J!;w2@D4I-}9|jJpcdz delta 12816 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 O%n3sTKb$WmvHu5!-#Fg@