diff --git a/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs index c18e14dee..cb2c213d0 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs @@ -21,6 +21,7 @@ namespace Barotrauma if (wallTarget != null) { Vector2 wallTargetPos = wallTarget.Position; + if (wallTarget.Structure.Submarine != null) wallTargetPos += wallTarget.Structure.Submarine.Position; wallTargetPos.Y = -wallTargetPos.Y; GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Red, false); GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5); diff --git a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs index f295160c6..8dc7700f0 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs @@ -1,14 +1,13 @@ using Barotrauma.Networking; using Barotrauma.Particles; using Barotrauma.Sounds; +using Lidgren.Network; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; -using Microsoft.Xna.Framework.Input; using System.Linq; -using Lidgren.Network; namespace Barotrauma { @@ -19,6 +18,8 @@ namespace Barotrauma private List decals = new List(); private float serverUpdateDelay; + private float remoteWaterVolume, remoteOxygenPercentage; + private List remoteFireSources; private bool networkUpdatePending; private float networkUpdateTimer; @@ -139,6 +140,10 @@ namespace Barotrauma partial void UpdateProjSpecific(float deltaTime, Camera cam) { serverUpdateDelay -= deltaTime; + if (serverUpdateDelay <= 0.0f) + { + ApplyRemoteState(); + } if (networkUpdatePending) { @@ -547,18 +552,18 @@ namespace Barotrauma public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime) { - float newWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; - float newOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); + remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; + remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); bool hasFireSources = message.ReadBoolean(); int fireSourceCount = 0; - List newFireSources = new List(); + remoteFireSources = new List(); if (hasFireSources) { fireSourceCount = message.ReadRangedInteger(0, 16); for (int i = 0; i < fireSourceCount; i++) { - newFireSources.Add(new Vector3( + remoteFireSources.Add(new Vector3( MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), message.ReadRangedSingle(0.0f, 1.0f, 8))); @@ -567,41 +572,6 @@ namespace Barotrauma if (serverUpdateDelay > 0.0f) { return; } - WaterVolume = newWaterVolume; - OxygenPercentage = newOxygenPercentage; - - for (int i = 0; i < fireSourceCount; i++) - { - Vector2 pos = new Vector2( - rect.X + rect.Width * newFireSources[i].X, - rect.Y - rect.Height + (rect.Height * newFireSources[i].Y)); - float size = newFireSources[i].Z * rect.Width; - - var newFire = i < FireSources.Count ? - FireSources[i] : - new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true); - newFire.Position = pos; - newFire.Size = new Vector2(size, newFire.Size.Y); - - //ignore if the fire wasn't added to this room (invalid position)? - if (!FireSources.Contains(newFire)) - { - newFire.Remove(); - continue; - } - } - - for (int i = FireSources.Count - 1; i >= fireSourceCount; i--) - { - FireSources[i].Remove(); - if (i < FireSources.Count) - { - FireSources.RemoveAt(i); - } - } - - if (serverUpdateDelay > 0.0f) { return; } - ApplyRemoteState(); } diff --git a/Barotrauma/BarotraumaServer/Source/Map/Hull.cs b/Barotrauma/BarotraumaServer/Source/Map/Hull.cs index 907ba6f45..3b10a7a24 100644 --- a/Barotrauma/BarotraumaServer/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaServer/Source/Map/Hull.cs @@ -20,6 +20,16 @@ namespace Barotrauma partial void UpdateProjSpecific(float deltaTime, Camera cam) { if (IdFreed) { return; } + + //don't create updates if all clients are very far from the hull + float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance; + if (!GameMain.Server.ConnectedClients.Any(c => + c.Character != null && + Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr)) + { + return; + } + //update client hulls if the amount of water has changed by >10% //or if oxygen percentage has changed by 5% if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f || @@ -31,8 +41,8 @@ namespace Barotrauma GameMain.NetworkMember.CreateEntityEvent(this); lastSentVolume = waterVolume; lastSentOxygen = OxygenPercentage; - sendUpdateTimer = NetworkUpdateInterval; - } + sendUpdateTimer = NetConfig.HullUpdateInterval; + } } } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index d7c3f4ea0..519d8d800 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -346,6 +346,12 @@ namespace Barotrauma.Networking Write(msg, eventsToSync, out sentEvents, client); } + foreach (NetEntityEvent entityEvent in sentEvents) + { + (entityEvent as ServerEntityEvent).Sent = true; + client.EntityEventLastSent[entityEvent.ID] = NetTime.Now; + } + foreach (NetEntityEvent entityEvent in sentEvents) { (entityEvent as ServerEntityEvent).Sent = true; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index 75fabb3e9..76ab560eb 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -447,6 +447,10 @@ namespace Barotrauma { attackWorldPos = wallTarget.Position; attackSimPos = ConvertUnits.ToSimUnits(attackWorldPos); + if (Character.Submarine == null && wallTarget.Structure.Submarine != null) + { + attackWorldPos += wallTarget.Structure.Submarine.Position; + } } else if (SelectedAiTarget.Entity is Character c) { @@ -467,6 +471,15 @@ namespace Barotrauma // Take the sub position into account in the sim pos if (Character.Submarine == null && SelectedAiTarget.Entity.Submarine != null) + { + attackSimPos += SelectedAiTarget.Entity.Submarine.SimPosition; + } + else if (Character.Submarine != null && SelectedAiTarget.Entity.Submarine == null) + { + attackSimPos -= Character.Submarine.SimPosition; + } + + if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater) { Character.AnimController.TargetDir = Character.WorldPosition.X < attackWorldPos.X ? Direction.Right : Direction.Left; } @@ -477,24 +490,30 @@ namespace Barotrauma if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex)) { WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex); - Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true); - if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime)) + Hull targetHull = section.gap?.FlowTargetHull; + if (targetHull != null && !section.gap.IsRoomToRoom) { - return; - } - } - else if (SelectedAiTarget.Entity is Structure wall) - { - for (int i = 0; i < wall.Sections.Length; i++) - { - WallSection section = wall.Sections[i]; - if (CanPassThroughHole(wall, i) && section?.gap != null) + Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true); + if (wallTarget.Structure.IsHorizontal) { - if (SteerThroughGap(wall, section, section.gap.WorldPosition, deltaTime)) - { - return; - } + targetPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2; } + else + { + targetPos.X = targetHull.WorldRect.Center.X; + } + + latchOntoAI?.DeattachFromBody(); + Character.AnimController.ReleaseStuckLimbs(); + if (steeringManager is IndoorsSteeringManager) + { + steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetPos - Character.WorldPosition)); + } + else + { + steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetPos)); + } + return; } } else if (SelectedAiTarget.Entity is Item i) @@ -509,8 +528,6 @@ namespace Barotrauma if (Character.WorldPosition.Y < door.Item.WorldRect.Y && Character.WorldPosition.Y > door.Item.WorldRect.Y - door.Item.Rect.Height) { velocity.Y = 0; - latchOntoAI?.DeattachFromBody(); - Character.AnimController.ReleaseStuckLimbs(); steeringManager.SteeringManual(deltaTime, velocity); return; } @@ -520,8 +537,6 @@ namespace Barotrauma if (Character.WorldPosition.X < door.Item.WorldRect.X && Character.WorldPosition.X > door.Item.WorldRect.Right) { velocity.X = 0; - latchOntoAI?.DeattachFromBody(); - Character.AnimController.ReleaseStuckLimbs(); steeringManager.SteeringManual(deltaTime, velocity); return; } @@ -687,34 +702,6 @@ namespace Barotrauma } } - private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime) - { - Hull targetHull = section.gap?.FlowTargetHull; - if (targetHull != null) // && !section.gap.IsRoomToRoom - { - if (wall.IsHorizontal) - { - targetWorldPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2; - } - else - { - targetWorldPos.X = targetHull.WorldRect.Center.X; - } - latchOntoAI?.DeattachFromBody(); - Character.AnimController.ReleaseStuckLimbs(); - if (steeringManager is IndoorsSteeringManager) - { - steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition)); - } - else - { - steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos)); - } - return true; - } - return false; - } - private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null) { AttackContext currentContext = Character.GetAttackContext(); @@ -738,6 +725,11 @@ namespace Barotrauma { wallTarget = null; + if (Character.AnimController.CurrentHull != null) + { + return; + } + //check if there's a wall between the target and the Character Vector2 rayStart = SimPosition; Vector2 rayEnd = SelectedAiTarget.SimPosition; @@ -745,7 +737,7 @@ namespace Barotrauma if (offset) { - rayStart -= SelectedAiTarget.Entity.Submarine.SimPosition; + rayStart -= ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position); } Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true); @@ -793,12 +785,8 @@ namespace Barotrauma sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X; } - latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, sectionPos, attachTargetNormal); - if (wall.Submarine != null) - { - sectionPos += wall.Submarine.Position; - } wallTarget = new WallTarget(sectionPos, wall, sectionIndex); + latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal); } } @@ -1014,27 +1002,14 @@ namespace Barotrauma else if (target.Entity is Structure s) { targetingTag = "wall"; - if (aggressiveBoarding) + if (character.CurrentHull == null && aggressiveBoarding) { - // Ignore walls when inside. - valueModifier = character.CurrentHull == null ? 2 : 0; - if (valueModifier > 0) + valueModifier = s.HasBody ? 2 : 0; + foreach (var section in s.Sections) { - // Ignore structures that doesn't have a body (not walls) - valueModifier *= s.HasBody ? 1 : 0; - } - for (int i = 0; i < s.Sections.Length; i++) - { - var section = s.Sections[i]; - if (CanPassThroughHole(s, i)) + if (section.gap != null) { - // Ignore walls that can be passed through - valueModifier = 0; - break; - } - else if (section.gap != null) - { - // up to 100% priority increase for every gap in the wall + // up to 100% more priority for every gap in the wall valueModifier *= 1 + section.gap.Open; } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs index 2d07409d1..0c1d42432 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs @@ -520,10 +520,9 @@ namespace Barotrauma // Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example. wasHit = true; } - else if (damageTarget is Structure wall && structureBody != null && - (structureBody.UserData is Structure || (structureBody.UserData is Submarine sub && sub == wall.Submarine))) + else if (damageTarget is Structure && structureBody?.UserData is Structure) { - // If the attack is aimed to a structure (wall) and hits a structure or the sub, it's successful + // If the attack is aimed to a structure and hits a structure, it's successful wasHit = true; } else diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs index d5778c9cc..ffe7cf9d3 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs @@ -208,7 +208,7 @@ namespace Barotrauma DockingPort myPort = null, outPostPort = null; foreach (DockingPort port in DockingPort.List) { - if (port.IsHorizontal) { continue; } + if (port.IsHorizontal || port.Docked) { continue; } if (port.Item.Submarine == level.StartOutpost) { outPostPort = port; diff --git a/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs b/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs index 83ea002c0..c252c399a 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs @@ -175,12 +175,11 @@ namespace Barotrauma LimitSize(); UpdateProjSpecific(growModifier); - -#if CLIENT - if (GameMain.Client != null) return; -#endif - - if (size.X < 1.0f) Remove(); + + if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)) + { + Remove(); + } } partial void UpdateProjSpecific(float growModifier); @@ -293,10 +292,6 @@ namespace Barotrauma //evaporate some of the water hull.WaterVolume -= extinguishAmount; -#if CLIENT - if (GameMain.Client != null) return; -#endif - if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)) { Remove(); @@ -325,12 +320,11 @@ namespace Barotrauma size.X -= extinguishAmount; hull.WaterVolume -= extinguishAmount; - -#if CLIENT - if (GameMain.Client != null) return; -#endif - - if (size.X < 1.0f) Remove(); + + if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)) + { + Remove(); + } } public void Extinguish(float deltaTime, float amount, Vector2 worldPosition) diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs index 6f537c2c7..0bb9df3e9 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs @@ -1522,14 +1522,40 @@ namespace Barotrauma outpost.MakeOutpost(); Point? minSize = null; + DockingPort subPort = null; if (Submarine.MainSub != null) { Point subSize = Submarine.MainSub.GetDockedBorders().Size; Point outpostSize = outpost.GetDockedBorders().Size; minSize = new Point(Math.Max(subSize.X, outpostSize.X), subSize.Y + outpostSize.Y); + + float closestDistance = float.MaxValue; + foreach (DockingPort port in DockingPort.List) + { + if (port.IsHorizontal || port.Docked) { continue; } + if (port.Item.Submarine != Submarine.MainSub) { continue; } + //the submarine port has to be at the top of the sub + if (port.Item.WorldPosition.Y < Submarine.MainSub.WorldPosition.Y) { continue; } + float dist = Math.Abs(port.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X); + if (dist < closestDistance) + { + subPort = port; + closestDistance = dist; + } + } } - outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize)); + float subDockingPortOffset = subPort == null ? 0.0f : subPort.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X; + //don't try to compensate if the port is very far from the sub's center of mass + if (Math.Abs(subDockingPortOffset) > 2000.0f) + { + subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -2000.0f, 2000.0f); + string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Name + ", dist: " + subDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible."; + DebugConsole.NewMessage(warningMsg, Color.Orange); + GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg); + } + + outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset)); if ((i == 0) == !Mirrored) { StartOutpost = outpost; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs index 83033e604..1c3ec2339 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs @@ -432,12 +432,16 @@ namespace Barotrauma { if (ForceFluctuationStrength > 0.0f) { - forceFluctuationTimer += deltaTime; - if (forceFluctuationTimer > ForceFluctuationInterval) + //no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers + if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any()) { - NeedsNetworkSyncing = true; - currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f); - forceFluctuationTimer = 0.0f; + forceFluctuationTimer += deltaTime; + if (forceFluctuationTimer > ForceFluctuationInterval) + { + NeedsNetworkSyncing = true; + currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f); + forceFluctuationTimer = 0.0f; + } } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index 4e71afd3b..66456de83 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -494,7 +494,7 @@ namespace Barotrauma } } - public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null) + public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null, float subDockingPortOffset = 0.0f) { Rectangle dockedBorders = GetDockedBorders(); Vector2 diffFromDockedBorders = @@ -542,17 +542,17 @@ namespace Barotrauma else if (minX < 0) { //no wall found at the left side, spawn to the left from the right-side wall - spawnPos.X = maxX - minWidth - 100.0f; + spawnPos.X = maxX - minWidth - 100.0f + subDockingPortOffset; } else if (maxX > Level.Loaded.Size.X) { //no wall found at right side, spawn to the right from the left-side wall - spawnPos.X = minX + minWidth + 100.0f; + spawnPos.X = minX + minWidth + 100.0f + subDockingPortOffset; } else { //walls found at both sides, use their midpoint - spawnPos.X = (minX + maxX) / 2; + spawnPos.X = (minX + maxX) / 2 + subDockingPortOffset; } spawnPos.Y = Math.Min(spawnPos.Y, Level.Loaded.Size.Y - dockedBorders.Height / 2 - 10); diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs index 2e2d01dc0..74b6afcb2 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs @@ -35,8 +35,9 @@ namespace Barotrauma.Networking public const float DeleteDisconnectedTime = 20.0f; public const float ItemConditionUpdateInterval = 0.15f; - public const float LevelObjectUpdateInterval = 0.5f; + public const float HullUpdateInterval = 0.5f; + public const float HullUpdateDistance = 20000.0f; public const int MaxEventPacketsPerUpdate = 4;