From 50521e25866a5f27ca762113e20653d3cd9971e9 Mon Sep 17 00:00:00 2001 From: Tom Leys Date: Wed, 7 Jun 2023 22:55:39 +1200 Subject: [PATCH 1/5] Teleport noobs off Arrivals shuttle to spawn - Estimate arrival time --- .../GameTicking/GameTicker.Spawning.cs | 11 ++- .../Components/ArrivalsShuttleComponent.cs | 4 + .../Shuttles/Systems/ArrivalsSystem.cs | 76 ++++++++++++++++++- .../Components/SpawnPointComponent.cs | 5 ++ .../Locale/en-US/game-ticking/game-ticker.ftl | 1 + 5 files changed, 95 insertions(+), 2 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index ca8186d0b90f..dca41a972e93 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -236,7 +236,16 @@ private void SpawnPlayer(IPlayerSession player, HumanoidCharacterProfile charact // We also want this message last. if (lateJoin && _arrivals.Enabled) { - _chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction")); + var arrival = _arrivals.NextShuttleArrival(); + if (arrival == null) + { + _chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction")); + } + else + { + _chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction-time", + ("time", $"{arrival:mm\\:ss}"))); + } } // We raise this event directed to the mob, but also broadcast it so game rules can do something now. diff --git a/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs b/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs index 1f6b777963c7..663b82e7c88c 100644 --- a/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs +++ b/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs @@ -11,4 +11,8 @@ public sealed class ArrivalsShuttleComponent : Component [DataField("nextTransfer", customTypeSerializer:typeof(TimeOffsetSerializer))] public TimeSpan NextTransfer; + + [DataField("nextArrivalsTime", customTypeSerializer:typeof(TimeOffsetSerializer))] + public TimeSpan NextArrivalsTime; + } diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 50b8d4f5b90d..0b20d00254d0 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -154,6 +154,13 @@ public override void Shutdown() private void OnArrivalsFTL(EntityUid shuttleUid, ArrivalsShuttleComponent component, ref FTLStartedEvent args) { + if (!TryGetArrivals(out EntityUid arrivals) || !TryComp(arrivals, out var arrivalsXform)) + return; + + // Don't do anything here when leaving arrivals. + if (args.FromMapUid == arrivalsXform.MapUid) + return; + // Any mob then yeet them off the shuttle. if (!_cfgManager.GetCVar(CCVars.ArrivalsReturns) && args.FromMapUid != null) { @@ -164,13 +171,22 @@ private void OnArrivalsFTL(EntityUid shuttleUid, ArrivalsShuttleComponent compon DumpChildren(shuttleUid, ref args, pendingEntQuery, arrivalsBlacklistQuery, mobQuery, xformQuery); } + // if (!TryComp(component.Station, out var data)) + // return; + // + // var targetGrid = _station.GetLargestGrid(data); var pendingQuery = AllEntityQuery(); // Clock them in when they FTL while (pendingQuery.MoveNext(out var pUid, out _, out var xform)) { // Cheaper to iterate pending arrivals than all children - if (xform.GridUid != shuttleUid) + if (xform.GridUid == shuttleUid) + { + // Teleport them on to the station. + TeleportToMapSpawn(pUid, component.Station, xform); + } + if (xform.MapUid == arrivalsXform.MapUid) continue; RemCompDeferred(pUid); @@ -241,6 +257,41 @@ private void OnPlayerSpawn(PlayerSpawningEvent ev) } } + private bool TeleportToMapSpawn(EntityUid player, EntityUid stationId, TransformComponent? transform = null) + { + if (!Resolve(player, ref transform)) + return false; + + var points = EntityQuery().ToList(); + _random.Shuffle(points); + + // Find a spawnpoint on the same map as the player is already on now. + foreach (var (spawnPoint, xform) in points) + { + if (spawnPoint.SpawnType != SpawnPointType.LateJoin || _station.GetOwningStation(spawnPoint.Owner, xform) != stationId) + continue; + + // Move the player to the spawnpoint. + transform.Coordinates = xform.Coordinates; + + return true; + } + + // Spawn them at a passenger location + foreach (var (spawnPoint, xform) in points) + { + if (spawnPoint?.Job?.ID != "Passenger" || _station.GetOwningStation(spawnPoint.Owner, xform) != stationId) + continue; + + // Move the player to the spawnpoint. + transform.Coordinates = xform.Coordinates; + + return true; + } + + return false; + } + private void OnShuttleStartup(EntityUid uid, ArrivalsShuttleComponent component, ComponentStartup args) { EnsureComp(uid); @@ -263,6 +314,20 @@ private bool TryGetArrivals(out EntityUid uid) return false; } + public TimeSpan? NextShuttleArrival() + { + var query = EntityQueryEnumerator(); + var time = TimeSpan.MaxValue; + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.NextArrivalsTime < time) + time = comp.NextArrivalsTime; + } + + var duration = _ticker.RoundDuration(); + return (time < duration) ? null : time - _ticker.RoundDuration(); + } + public override void Update(float frameTime) { base.Update(frameTime); @@ -286,11 +351,15 @@ public override void Update(float frameTime) if (comp.NextTransfer > curTime || !TryComp(comp.Station, out var data)) continue; + var tripTime = ShuttleSystem.DefaultTravelTime + ShuttleSystem.DefaultStartupTime ; + // Go back to arrivals source if (xform.MapUid != arrivalsXform.MapUid) { if (arrivals.IsValid()) _shuttles.FTLTravel(uid, shuttle, arrivals, dock: true); + + comp.NextArrivalsTime = _ticker.RoundDuration() + TimeSpan.FromSeconds(tripTime); } // Go to station else @@ -299,6 +368,11 @@ public override void Update(float frameTime) if (targetGrid != null) _shuttles.FTLTravel(uid, shuttle, targetGrid.Value, dock: true); + + // The ArrivalsCooldown includes the trip there, so we only need to add the time taken for + // the trip back. + comp.NextArrivalsTime = _ticker.RoundDuration() + + TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown) + tripTime); } comp.NextTransfer += TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown)); diff --git a/Content.Server/Spawners/Components/SpawnPointComponent.cs b/Content.Server/Spawners/Components/SpawnPointComponent.cs index f520f06f4f39..20d74f4ba948 100644 --- a/Content.Server/Spawners/Components/SpawnPointComponent.cs +++ b/Content.Server/Spawners/Components/SpawnPointComponent.cs @@ -17,6 +17,11 @@ public sealed class SpawnPointComponent : Component public SpawnPointType SpawnType { get; } = SpawnPointType.Unset; public JobPrototype? Job => string.IsNullOrEmpty(_jobId) ? null : _prototypeManager.Index(_jobId); + + public override string ToString() + { + return $"{_jobId} {SpawnType}"; + } } public enum SpawnPointType diff --git a/Resources/Locale/en-US/game-ticking/game-ticker.ftl b/Resources/Locale/en-US/game-ticking/game-ticker.ftl index 5dbeb5088dae..4adacd11123a 100644 --- a/Resources/Locale/en-US/game-ticking/game-ticker.ftl +++ b/Resources/Locale/en-US/game-ticking/game-ticker.ftl @@ -35,3 +35,4 @@ player-leave-message = Player {$name} left. latejoin-arrival-announcement = {$character} ({$job}) has arrived at the station! latejoin-arrival-sender = Station latejoin-arrivals-direction = A shuttle transferring you to your station will arrive shortly. +latejoin-arrivals-direction-time = A shuttle transferring you to your station will arrive in {$time}. From 95b1bc8732efee9dbdbb7da4c2585845818e85a0 Mon Sep 17 00:00:00 2001 From: Tom Leys Date: Wed, 2 Aug 2023 17:27:57 +1200 Subject: [PATCH 2/5] PR changes requested by EmoGarbage --- .../Shuttles/Systems/ArrivalsSystem.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 0b20d00254d0..bb003e641a8d 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -157,8 +157,9 @@ private void OnArrivalsFTL(EntityUid shuttleUid, ArrivalsShuttleComponent compon if (!TryGetArrivals(out EntityUid arrivals) || !TryComp(arrivals, out var arrivalsXform)) return; + var arrivalsMapUid = Transform(arrivals).MapUid; // Don't do anything here when leaving arrivals. - if (args.FromMapUid == arrivalsXform.MapUid) + if (args.FromMapUid == arrivalsMapUid) return; // Any mob then yeet them off the shuttle. @@ -171,24 +172,26 @@ private void OnArrivalsFTL(EntityUid shuttleUid, ArrivalsShuttleComponent compon DumpChildren(shuttleUid, ref args, pendingEntQuery, arrivalsBlacklistQuery, mobQuery, xformQuery); } - // if (!TryComp(component.Station, out var data)) - // return; - // - // var targetGrid = _station.GetLargestGrid(data); var pendingQuery = AllEntityQuery(); - // Clock them in when they FTL + // We're heading from the station back to arrivals (if leaving arrivals, would have returned above). + // Process everyone who holds a PendingClockInComponent + // Note, due to way DumpChildren works, anyone who doesn't have a PendingClockInComponent gets left in space + // and will not warp. This is intended behavior. while (pendingQuery.MoveNext(out var pUid, out _, out var xform)) { - // Cheaper to iterate pending arrivals than all children if (xform.GridUid == shuttleUid) { - // Teleport them on to the station. - TeleportToMapSpawn(pUid, component.Station, xform); + // Warp all players who are still on this shuttle to a spawn point. This doesn't let them return to + // arrivals. It also ensures noobs, slow players or AFK players safely leave the shuttle. + TryTeleportToMapSpawn(pUid, component.Station, xform); } - if (xform.MapUid == arrivalsXform.MapUid) + + // Players who have remained at arrives keep their warp coupon (PendingClockInComponent) for now. + if (xform.MapUid == arrivalsMapUid) continue; + // The player has successfully left arrivals and is also not on the shuttle. Remove their warp coupon. RemCompDeferred(pUid); RemCompDeferred(pUid); } @@ -257,7 +260,7 @@ private void OnPlayerSpawn(PlayerSpawningEvent ev) } } - private bool TeleportToMapSpawn(EntityUid player, EntityUid stationId, TransformComponent? transform = null) + private bool TryTeleportToMapSpawn(EntityUid player, EntityUid stationId, TransformComponent? transform = null) { if (!Resolve(player, ref transform)) return false; From 04d86c17aeb1a56481191229649eadf8af71b7ac Mon Sep 17 00:00:00 2001 From: Tom Leys Date: Thu, 3 Aug 2023 11:00:04 +1200 Subject: [PATCH 3/5] Refactor respawn system to avoid .Owner and .ToList() on query --- .../Shuttles/Systems/ArrivalsSystem.cs | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index bb003e641a8d..307f46f18523 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -154,7 +154,7 @@ public override void Shutdown() private void OnArrivalsFTL(EntityUid shuttleUid, ArrivalsShuttleComponent component, ref FTLStartedEvent args) { - if (!TryGetArrivals(out EntityUid arrivals) || !TryComp(arrivals, out var arrivalsXform)) + if (!TryGetArrivals(out EntityUid arrivals)) return; var arrivalsMapUid = Transform(arrivals).MapUid; @@ -265,34 +265,26 @@ private bool TryTeleportToMapSpawn(EntityUid player, EntityUid stationId, Transf if (!Resolve(player, ref transform)) return false; - var points = EntityQuery().ToList(); - _random.Shuffle(points); + var points = EntityQueryEnumerator(); + var possiblePositions = new List(); // Find a spawnpoint on the same map as the player is already on now. - foreach (var (spawnPoint, xform) in points) + while ( points.MoveNext(out var uid, out var spawnPoint, out var xform)) { - if (spawnPoint.SpawnType != SpawnPointType.LateJoin || _station.GetOwningStation(spawnPoint.Owner, xform) != stationId) + if (spawnPoint.SpawnType != SpawnPointType.LateJoin || _station.GetOwningStation(uid, xform) != stationId) continue; - // Move the player to the spawnpoint. - transform.Coordinates = xform.Coordinates; - - return true; + // Add to list of possible spawn locations + possiblePositions.Add(xform.Coordinates); } - // Spawn them at a passenger location - foreach (var (spawnPoint, xform) in points) - { - if (spawnPoint?.Job?.ID != "Passenger" || _station.GetOwningStation(spawnPoint.Owner, xform) != stationId) - continue; + if (possiblePositions.Count == 0) + return false; - // Move the player to the spawnpoint. - transform.Coordinates = xform.Coordinates; + // Move the player to a random spawnpoint. + _transform.SetCoordinates(player, _random.Pick(possiblePositions)); - return true; - } - - return false; + return true; } private void OnShuttleStartup(EntityUid uid, ArrivalsShuttleComponent component, ComponentStartup args) From 54e49171fcd6460339dad8866a53b6c82fe3aa4d Mon Sep 17 00:00:00 2001 From: Tom Leys Date: Thu, 3 Aug 2023 14:34:46 +1200 Subject: [PATCH 4/5] Fix bugs in teleport refactor --- .../Shuttles/Systems/ArrivalsSystem.cs | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 307f46f18523..3c2e9ccfcef1 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -267,22 +267,39 @@ private bool TryTeleportToMapSpawn(EntityUid player, EntityUid stationId, Transf var points = EntityQueryEnumerator(); var possiblePositions = new List(); + var passengerPositions = new List(); - // Find a spawnpoint on the same map as the player is already on now. + // Find a spawnpoint on the same map as the player is already docked with now. while ( points.MoveNext(out var uid, out var spawnPoint, out var xform)) { - if (spawnPoint.SpawnType != SpawnPointType.LateJoin || _station.GetOwningStation(uid, xform) != stationId) - continue; - - // Add to list of possible spawn locations - possiblePositions.Add(xform.Coordinates); + if (spawnPoint.SpawnType == SpawnPointType.LateJoin && + _station.GetOwningStation(uid, xform) == stationId) + { + // Add to list of possible spawn locations + possiblePositions.Add(xform.Coordinates); + } else if (possiblePositions.Count == 0 && spawnPoint?.Job?.ID == "Passenger" && + _station.GetOwningStation(uid, xform) == stationId) + { + // Add to passenger positions as fallback if there are no possiblePositions + passengerPositions.Add(xform.Coordinates); + } } - if (possiblePositions.Count == 0) - return false; + if (possiblePositions.Count > 0) + { + // Move the player to a random late-join spawnpoint. + _transform.SetCoordinates(player, transform, _random.Pick(possiblePositions)); + } + else + { + if (passengerPositions.Count == 0) + { + return false; + } - // Move the player to a random spawnpoint. - _transform.SetCoordinates(player, _random.Pick(possiblePositions)); + // Move the player to a random passenger spawnpoint. + _transform.SetCoordinates(player, transform, _random.Pick(passengerPositions)); + } return true; } From 3540bf7c8879d8985505d5c837a8e899499a7e35 Mon Sep 17 00:00:00 2001 From: Tom Leys Date: Sat, 12 Aug 2023 09:59:59 +1200 Subject: [PATCH 5/5] Use CurTime rather than RoundDuration, remove Passenger --- .../Shuttles/Systems/ArrivalsSystem.cs | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 3c2e9ccfcef1..874b37a6ea43 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Spawners.Components; using Content.Shared.Tiles; using Robust.Server.GameObjects; +using Robust.Shared.Collections; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.Map; @@ -266,8 +267,7 @@ private bool TryTeleportToMapSpawn(EntityUid player, EntityUid stationId, Transf return false; var points = EntityQueryEnumerator(); - var possiblePositions = new List(); - var passengerPositions = new List(); + var possiblePositions = new ValueList(32); // Find a spawnpoint on the same map as the player is already docked with now. while ( points.MoveNext(out var uid, out var spawnPoint, out var xform)) @@ -277,11 +277,6 @@ private bool TryTeleportToMapSpawn(EntityUid player, EntityUid stationId, Transf { // Add to list of possible spawn locations possiblePositions.Add(xform.Coordinates); - } else if (possiblePositions.Count == 0 && spawnPoint?.Job?.ID == "Passenger" && - _station.GetOwningStation(uid, xform) == stationId) - { - // Add to passenger positions as fallback if there are no possiblePositions - passengerPositions.Add(xform.Coordinates); } } @@ -289,19 +284,10 @@ private bool TryTeleportToMapSpawn(EntityUid player, EntityUid stationId, Transf { // Move the player to a random late-join spawnpoint. _transform.SetCoordinates(player, transform, _random.Pick(possiblePositions)); - } - else - { - if (passengerPositions.Count == 0) - { - return false; - } - - // Move the player to a random passenger spawnpoint. - _transform.SetCoordinates(player, transform, _random.Pick(passengerPositions)); + return true; } - return true; + return false; } private void OnShuttleStartup(EntityUid uid, ArrivalsShuttleComponent component, ComponentStartup args) @@ -336,8 +322,8 @@ private bool TryGetArrivals(out EntityUid uid) time = comp.NextArrivalsTime; } - var duration = _ticker.RoundDuration(); - return (time < duration) ? null : time - _ticker.RoundDuration(); + var duration = _timing.CurTime; + return (time < duration) ? null : time - duration; } public override void Update(float frameTime) @@ -371,7 +357,7 @@ public override void Update(float frameTime) if (arrivals.IsValid()) _shuttles.FTLTravel(uid, shuttle, arrivals, dock: true); - comp.NextArrivalsTime = _ticker.RoundDuration() + TimeSpan.FromSeconds(tripTime); + comp.NextArrivalsTime = _timing.CurTime + TimeSpan.FromSeconds(tripTime); } // Go to station else @@ -383,8 +369,8 @@ public override void Update(float frameTime) // The ArrivalsCooldown includes the trip there, so we only need to add the time taken for // the trip back. - comp.NextArrivalsTime = _ticker.RoundDuration() + - TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown) + tripTime); + comp.NextArrivalsTime = _timing.CurTime + TimeSpan.FromSeconds( + _cfgManager.GetCVar(CCVars.ArrivalsCooldown) + tripTime); } comp.NextTransfer += TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown));