diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs index b1871cfe0..7e8fdc706 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs @@ -1,4 +1,6 @@ -using Barotrauma.Particles; +using Barotrauma.Networking; +using Barotrauma.Particles; +using Lidgren.Network; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.Generic; @@ -112,8 +114,7 @@ namespace Barotrauma.Items.Components System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts"); foreach (GUIComponent c in GuiFrame.GetChild(0).Children) { - Skill skill = c.UserData as Skill; - if (skill == null) continue; + if (!(c.UserData is Skill skill)) continue; GUITextBlock textBlock = (GUITextBlock)c; if (character.GetSkillLevel(skill.Identifier) < skill.Level) @@ -126,5 +127,15 @@ namespace Barotrauma.Items.Components } } } + + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) + { + deteriorationTimer = msg.ReadSingle(); + } + + public void ClientWrite(NetBuffer msg, object[] extraData = null) + { + //no need to write anything, just letting the server know we started repairing + } } } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs index f7451440a..d79c682de 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs @@ -81,11 +81,10 @@ namespace Barotrauma.Networking for (int i = startIndex; i < events.Count; i++) { - //find the first event that hasn't been sent in 1.5 * roundtriptime or at all - float lastSent = 0; - eventLastSent.TryGetValue(events[i].ID, out lastSent); + //find the first event that hasn't been sent in roundtriptime or at all + eventLastSent.TryGetValue(events[i].ID, out float lastSent); - if (lastSent > NetTime.Now - serverConnection.AverageRoundtripTime * 1.5f) + if (lastSent > NetTime.Now - serverConnection.AverageRoundtripTime) { continue; } @@ -93,14 +92,7 @@ namespace Barotrauma.Networking eventsToSync.AddRange(events.GetRange(i, events.Count - i)); break; } - if (eventsToSync.Count == 0) return; - - //too many events for one packet - if (eventsToSync.Count > MaxEventsPerWrite) - { - eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite); - } - if (eventsToSync.Count == 0) return; + if (eventsToSync.Count == 0) { return; } foreach (NetEntityEvent entityEvent in eventsToSync) { @@ -108,7 +100,7 @@ namespace Barotrauma.Networking } msg.Write((byte)ClientNetObject.ENTITY_STATE); - Write(msg, eventsToSync); + Write(msg, eventsToSync, out _); } private UInt16? firstNewID; diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs index 04ca33c0d..7bf36f0f8 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs @@ -1,9 +1,5 @@ using Barotrauma.Networking; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Lidgren.Network; namespace Barotrauma.Items.Components { @@ -14,5 +10,16 @@ namespace Barotrauma.Items.Components //let the clients know the initial deterioration delay item.CreateServerEvent(this); } + + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) + { + if (c.Character == null) return; + StartRepairing(c.Character); + } + + public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) + { + msg.Write(deteriorationTimer); + } } } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/Client.cs b/Barotrauma/BarotraumaServer/Source/Networking/Client.cs index 78ff44d7a..28e291e39 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/Client.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/Client.cs @@ -46,7 +46,7 @@ namespace Barotrauma.Networking //when was a specific entity event last sent to the client // key = event id, value = NetTime.Now when sending - public readonly Dictionary EntityEventLastSent = new Dictionary(); + public readonly Dictionary EntityEventLastSent = new Dictionary(); //when was a position update for a given entity last sent to the client // key = entity id, value = NetTime.Now when sending diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index 4ea71904f..e8d6bbc1b 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -1201,7 +1201,8 @@ namespace Barotrauma.Networking ClientWriteLobby(c); if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && - NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave)) + NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave) && + c.Connection != OwnerConnection) //no need to send saves if the client is playing on the same machine { //already sent an up-to-date campaign save if (c.LastCampaignSaveSendTime != null && campaign.LastSaveID == c.LastCampaignSaveSendTime.First) @@ -1335,10 +1336,6 @@ namespace Barotrauma.Networking WriteClientList(c, outmsg); clientListBytes = outmsg.LengthBytes - clientListBytes; - int eventManagerBytes = outmsg.LengthBytes; - entityEventManager.Write(c, outmsg, out List sentEvents); - eventManagerBytes = outmsg.LengthBytes - eventManagerBytes; - int chatMessageBytes = outmsg.LengthBytes; WriteChatMessages(outmsg, c); chatMessageBytes = outmsg.LengthBytes - chatMessageBytes; @@ -1387,25 +1384,55 @@ namespace Barotrauma.Networking errorMsg += " Client list size: " + clientListBytes + " bytes\n" + " Chat message size: " + chatMessageBytes + " bytes\n" + - " Event size: " + eventManagerBytes + " bytes\n" + " Position update size: " + positionUpdateBytes + " bytes\n\n"; - - if (sentEvents != null && sentEvents.Count > 0) - { - errorMsg += "Sent events: \n"; - foreach (var entityEvent in sentEvents) - { - errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n"; - } - } - DebugConsole.ThrowError(errorMsg); - GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); } CompressOutgoingMessage(outmsg); - server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable); + + //--------------------------------------------------------------------------- + + for (int i = 0; i < MaxEventPacketsPerUpdate; i++) + { + outmsg = server.CreateMessage(); + outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME); + outmsg.Write((float)NetTime.Now); + + int eventManagerBytes = outmsg.LengthBytes; + entityEventManager.Write(c, outmsg, out List sentEvents); + eventManagerBytes = outmsg.LengthBytes - eventManagerBytes; + + if (sentEvents.Count == 0) + { + break; + } + + outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE); + + if (outmsg.LengthBytes > NetPeerConfiguration.MaximumTransmissionUnit) + { + string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + NetPeerConfiguration.MaximumTransmissionUnit + ")\n"; + errorMsg += + " Event size: " + eventManagerBytes + " bytes\n"; + + if (sentEvents != null && sentEvents.Count > 0) + { + errorMsg += "Sent events: \n"; + foreach (var entityEvent in sentEvents) + { + errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n"; + } + } + + DebugConsole.ThrowError(errorMsg); + GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + } + + CompressOutgoingMessage(outmsg); + server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable); + } } private void WriteClientList(Client c, NetOutgoingMessage outmsg) diff --git a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index 275f94e51..c256d9ffa 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -303,22 +303,28 @@ namespace Barotrauma.Networking } //too many events for one packet - if (eventsToSync.Count > MaxEventsPerWrite) + if (eventsToSync.Count > 200) { - if (eventsToSync.Count > MaxEventsPerWrite * 3 && !client.NeedsMidRoundSync) + if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync) { - Color color = eventsToSync.Count > MaxEventsPerWrite * 20 ? Color.Red : Color.Orange; - if (eventsToSync.Count < MaxEventsPerWrite * 5) { color = Color.Yellow; } - DebugConsole.NewMessage("WARNING: event count very high: " + eventsToSync.Count + "/" + MaxEventsPerWrite, color); + Color color = eventsToSync.Count > 500 ? Color.Red : Color.Orange; + if (eventsToSync.Count < 300) { color = Color.Yellow; } + string warningMsg = "WARNING: event count very high: " + eventsToSync.Count; + + var sortedEvents = eventsToSync.GroupBy(e => e.Entity.ToString()) + .Select(e => new { Value = e.Key, Count = e.Count() }) + .OrderByDescending(e => e.Count); + + int count = 1; + foreach (var sortedEvent in sortedEvents) + { + warningMsg += "\n" + count + ". " + (sortedEvent.Value?.ToString() ?? "null") + " x" + sortedEvent.Count; + count++; + if (count > 3) { break; } + } + + DebugConsole.NewMessage(warningMsg, color); } - - eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite); - } - - foreach (NetEntityEvent entityEvent in eventsToSync) - { - (entityEvent as ServerEntityEvent).Sent = true; - client.EntityEventLastSent[entityEvent.ID] = (float)NetTime.Now; } if (client.NeedsMidRoundSync) @@ -327,14 +333,19 @@ namespace Barotrauma.Networking msg.Write(client.UnreceivedEntityEventCount); msg.Write(client.FirstNewEventID); - Write(msg, eventsToSync, client); + Write(msg, eventsToSync, out sentEvents, client); } else { msg.Write((byte)ServerNetObject.ENTITY_EVENT); - Write(msg, eventsToSync, client); + Write(msg, eventsToSync, out sentEvents, client); + } + + foreach (NetEntityEvent entityEvent in sentEvents) + { + (entityEvent as ServerEntityEvent).Sent = true; + client.EntityEventLastSent[entityEvent.ID] = NetTime.Now; } - sentEvents = eventsToSync; } /// @@ -351,17 +362,17 @@ namespace Barotrauma.Networking //find the index of the first event the client hasn't received int startIndex = eventList.Count; while (startIndex > 0 && - NetIdUtils.IdMoreRecent(eventList[startIndex - 1].ID,client.LastRecvEntityEventID)) + NetIdUtils.IdMoreRecent(eventList[startIndex - 1].ID, client.LastRecvEntityEventID)) { 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 - client.EntityEventLastSent.TryGetValue(eventList[i].ID, out float lastSent); + //find the first event that hasn't been sent in roundtriptime or at all + client.EntityEventLastSent.TryGetValue(eventList[i].ID, out double lastSent); - float minInterval = Math.Max(client.Connection.AverageRoundtripTime * 1.5f, (float)server.UpdateInterval.TotalSeconds * 2); + float minInterval = Math.Max(client.Connection.AverageRoundtripTime, (float)server.UpdateInterval.TotalSeconds * 2); if (lastSent > NetTime.Now - Math.Min(minInterval, 0.5f)) { @@ -387,7 +398,7 @@ namespace Barotrauma.Networking } else { - double midRoundSyncTimeOut = uniqueEvents.Count / MaxEventsPerWrite * server.UpdateInterval.TotalSeconds; + double midRoundSyncTimeOut = uniqueEvents.Count / 100 * server.UpdateInterval.TotalSeconds; midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f); client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs index dff106cfb..5d6f70a83 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs @@ -460,6 +460,7 @@ namespace Barotrauma { affliction.Strength = 0.0f; } + CalculateVitality(); } private void AddLimbAffliction(Limb limb, Affliction newAffliction) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs index 3f33c4df2..8d3baec86 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs @@ -12,8 +12,6 @@ namespace Barotrauma.Items.Components public static float SkillIncreaseMultiplier = 0.4f; private string header; - - private float fixDurationLowSkill, fixDurationHighSkill; private float deteriorationTimer; @@ -52,17 +50,20 @@ namespace Barotrauma.Items.Components set; } - /*private float repairProgress; - public float RepairProgress + [Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with insufficient skill levels.")] + public float FixDurationLowSkill { - get { return repairProgress; } - set - { - repairProgress = MathHelper.Clamp(value, 0.0f, 1.0f); - if (repairProgress >= 1.0f && currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None; - } - }*/ - + get; + set; + } + + [Serialize(10.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with sufficient skill levels.")] + public float FixDurationHighSkill + { + get; + set; + } + private Character currentFixer; public Character CurrentFixer { @@ -83,8 +84,6 @@ namespace Barotrauma.Items.Components this.item = item; header = element.GetAttributeString("name", ""); - fixDurationLowSkill = element.GetAttributeFloat("fixdurationlowskill", 100.0f); - fixDurationHighSkill = element.GetAttributeFloat("fixdurationhighskill", 5.0f); InitProjSpecific(element); } @@ -160,7 +159,7 @@ namespace Barotrauma.Items.Components } bool wasBroken = !item.IsFullCondition; - float fixDuration = MathHelper.Lerp(fixDurationLowSkill, fixDurationHighSkill, successFactor); + float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor); if (fixDuration <= 0.0f) { item.Condition = item.MaxCondition; @@ -186,26 +185,5 @@ namespace Barotrauma.Items.Components { character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f)); } - - public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) - { - msg.Write(deteriorationTimer); - } - - public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) - { - deteriorationTimer = msg.ReadSingle(); - } - - public void ClientWrite(NetBuffer msg, object[] extraData = null) - { - //no need to write anything, just letting the server know we started repairing - } - - public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) - { - if (c.Character == null) return; - StartRepairing(c.Character); - } } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 9bc8b0e87..32cb6f3be 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -59,8 +59,10 @@ namespace Barotrauma public PhysicsBody body; public readonly XElement StaticBodyConfig; - + private float lastSentCondition; + private float sendConditionUpdateTimer; + private bool conditionUpdatePending; private float condition; @@ -272,12 +274,11 @@ namespace Barotrauma SetActiveSprite(); - if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && lastSentCondition != condition) + if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition)) { if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health) { - GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); - lastSentCondition = condition; + conditionUpdatePending = true; } } } @@ -994,6 +995,21 @@ namespace Barotrauma aiTarget.SightRange -= deltaTime * 1000.0f; aiTarget.SoundRange -= deltaTime * 1000.0f; } + + if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) + { + sendConditionUpdateTimer -= deltaTime; + if (conditionUpdatePending) + { + if (sendConditionUpdateTimer <= 0.0f) + { + GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); + lastSentCondition = condition; + sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval; + conditionUpdatePending = false; + } + } + } ApplyStatusEffects(ActionType.Always, deltaTime, null); diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs index d96da098c..3874415b6 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs @@ -15,7 +15,6 @@ namespace Barotrauma public Vector3 Position; public float NetworkUpdateTimer; - public const float NetworkUpdateInterval = 0.2f; public float Scale; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs index 40469cea9..b75965a40 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs @@ -343,7 +343,7 @@ namespace Barotrauma { GameMain.NetworkMember.CreateEntityEvent(this, new object[] { obj }); obj.NeedsNetworkSyncing = false; - obj.NetworkUpdateTimer = LevelObject.NetworkUpdateInterval; + obj.NetworkUpdateTimer = NetConfig.LevelObjectUpdateInterval; } } diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs index d7a36e28c..2e2d01dc0 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs @@ -32,11 +32,13 @@ namespace Barotrauma.Networking public const float HighPrioCharacterPositionUpdateInterval = 0.0f; public const float LowPrioCharacterPositionUpdateInterval = 1.0f; - //how much the physics body of an item has to move until the server - //send a position update to clients (in sim units) - public const float ItemPosUpdateDistance = 2.0f; - - public const float DeleteDisconnectedTime = 10.0f; + public const float DeleteDisconnectedTime = 20.0f; + + public const float ItemConditionUpdateInterval = 0.15f; + + public const float LevelObjectUpdateInterval = 0.5f; + + public const int MaxEventPacketsPerUpdate = 4; /// /// Interpolates the positional error of a physics body towards zero. diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs index d47462e41..e88ae3b71 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs @@ -7,16 +7,17 @@ namespace Barotrauma.Networking abstract class NetEntityEventManager { public const int MaxEventBufferLength = 1024; - public const int MaxEventsPerWrite = 64; /// /// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager /// - protected void Write(NetOutgoingMessage msg, List eventsToSync, Client recipient = null) + protected void Write(NetOutgoingMessage msg, List eventsToSync, out List sentEvents, Client recipient = null) { //write into a temporary buffer so we can write the number of events before the actual data NetBuffer tempBuffer = new NetBuffer(); + sentEvents = new List(); + int eventCount = 0; foreach (NetEntityEvent e in eventsToSync) { @@ -43,7 +44,7 @@ namespace Barotrauma.Networking continue; } - if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > NetPeerConfiguration.kDefaultMTU - 20) + if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > MaxEventBufferLength) { //no more room in this packet break; @@ -76,6 +77,7 @@ namespace Barotrauma.Networking tempBuffer.Write((byte)tempEventBuffer.LengthBytes); tempBuffer.Write(tempEventBuffer); tempBuffer.WritePadBits(); + sentEvents.Add(e); } eventCount++;