From 5a28c16cae47d4180b9993de052d8cb91c0804b2 Mon Sep 17 00:00:00 2001 From: Acruid Date: Fri, 11 Feb 2022 20:54:03 -0800 Subject: [PATCH] Map Pausing Fixes (#2520) --- Robust.Server/Console/Commands/MapCommands.cs | 13 +- .../Console/Commands/TestbedCommand.cs | 5 +- Robust.Server/Maps/MapLoader.cs | 25 +- .../Components/IgnorePauseComponent.cs | 11 +- .../Components/MetaDataComponent.cs | 6 +- .../Transform/TransformComponent.cs | 16 +- Robust.Shared/GameObjects/EntityManager.cs | 3 +- Robust.Shared/Map/GridId.cs | 36 ++- Robust.Shared/Map/IMapManager.cs | 3 +- .../MapManager.Pause.cs} | 118 ++++---- Robust.Shared/Map/MapManager.cs | 8 +- Robust.Shared/SharedIoC.cs | 4 +- Robust.Shared/Timing/IPauseManager.cs | 5 + .../Server/RobustServerSimulation.cs | 2 +- .../Shared/Map/MapGrid_Tests.cs | 99 +++---- .../Shared/Map/MapPauseTests.cs | 272 ++++++++++++++++++ 16 files changed, 484 insertions(+), 142 deletions(-) rename Robust.Shared/{Timing/PauseManager.cs => Map/MapManager.Pause.cs} (56%) create mode 100644 Robust.UnitTesting/Shared/Map/MapPauseTests.cs diff --git a/Robust.Server/Console/Commands/MapCommands.cs b/Robust.Server/Console/Commands/MapCommands.cs index 1045def1b..5c98c487b 100644 --- a/Robust.Server/Console/Commands/MapCommands.cs +++ b/Robust.Server/Console/Commands/MapCommands.cs @@ -29,14 +29,13 @@ namespace Robust.Server.Console.Commands var mapId = new MapId(int.Parse(args[0])); var mapMgr = IoCManager.Resolve(); - var pauseMgr = IoCManager.Resolve(); if (!mapMgr.MapExists(mapId)) { mapMgr.CreateMap(mapId); if (args.Length >= 2 && args[1] == "false") { - pauseMgr.AddUninitializedMap(mapId); + mapMgr.AddUninitializedMap(mapId); } shell.WriteLine($"Map with ID {mapId} created."); @@ -318,7 +317,6 @@ namespace Robust.Server.Console.Commands } var mapManager = IoCManager.Resolve(); - var pauseManager = IoCManager.Resolve(); var arg = args[0]; var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture)); @@ -329,13 +327,13 @@ namespace Robust.Server.Console.Commands return; } - if (pauseManager.IsMapInitialized(mapId)) + if (mapManager.IsMapInitialized(mapId)) { shell.WriteError("Map is already initialized!"); return; } - pauseManager.DoMapInitialize(mapId); + mapManager.DoMapInitialize(mapId); } } @@ -348,15 +346,14 @@ namespace Robust.Server.Console.Commands public void Execute(IConsoleShell shell, string argStr, string[] args) { var mapManager = IoCManager.Resolve(); - var pauseManager = IoCManager.Resolve(); var msg = new StringBuilder(); foreach (var mapId in mapManager.GetAllMapIds().OrderBy(id => id.Value)) { msg.AppendFormat("{0}: init: {1}, paused: {2}, ent: {3}, grids: {4}\n", - mapId, pauseManager.IsMapInitialized(mapId), - pauseManager.IsMapPaused(mapId), + mapId, mapManager.IsMapInitialized(mapId), + mapManager.IsMapPaused(mapId), string.Join(",", mapManager.GetAllMapGrids(mapId).Select(grid => grid.Index)), mapManager.GetMapEntityId(mapId)); } diff --git a/Robust.Server/Console/Commands/TestbedCommand.cs b/Robust.Server/Console/Commands/TestbedCommand.cs index c583bd1cf..ef55e84b9 100644 --- a/Robust.Server/Console/Commands/TestbedCommand.cs +++ b/Robust.Server/Console/Commands/TestbedCommand.cs @@ -109,9 +109,8 @@ namespace Robust.Server.Console.Commands private void SetupPlayer(MapId mapId, IConsoleShell shell, IPlayerSession? player, IMapManager mapManager) { if (mapId == MapId.Nullspace) return; - var pauseManager = IoCManager.Resolve(); - pauseManager.SetMapPaused(mapId, false); - var mapUid = IoCManager.Resolve().GetMapEntityIdOrThrow(mapId); + mapManager.SetMapPaused(mapId, false); + var mapUid = mapManager.GetMapEntityIdOrThrow(mapId); IoCManager.Resolve().GetComponent(mapUid).Gravity = new Vector2(0, -9.8f); return; diff --git a/Robust.Server/Maps/MapLoader.cs b/Robust.Server/Maps/MapLoader.cs index 617777207..f19f2b437 100644 --- a/Robust.Server/Maps/MapLoader.cs +++ b/Robust.Server/Maps/MapLoader.cs @@ -24,7 +24,6 @@ using Robust.Shared.Serialization.Markdown.Mapping; using Robust.Shared.Serialization.Markdown.Validation; using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Interfaces; -using Robust.Shared.Timing; using Robust.Shared.Utility; using YamlDotNet.Core; using YamlDotNet.RepresentationModel; @@ -44,7 +43,6 @@ namespace Robust.Server.Maps [Dependency] private readonly IMapManagerInternal _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly IServerEntityManagerInternal _serverEntityManager = default!; - [Dependency] private readonly IPauseManager _pauseManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public event Action? LoadedMapData; @@ -54,7 +52,7 @@ namespace Robust.Server.Maps { var grid = _mapManager.GetGrid(gridId); - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, _prototypeManager); + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _prototypeManager); context.RegisterGrid(grid); var root = context.Serialize(); var document = new YamlDocument(root); @@ -100,7 +98,7 @@ namespace Robust.Server.Maps throw new InvalidDataException("Cannot instance map with multiple grids as blueprint."); } - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _prototypeManager, (YamlMappingNode) data.RootNode, mapId, options); context.Deserialize(); grid = context.Grids[0]; @@ -120,7 +118,7 @@ namespace Robust.Server.Maps _serverEntityManager.GetComponent(entity).EntityLifeStage = EntityLifeStage.MapInitialized; } } - else if (_pauseManager.IsMapInitialized(mapId)) + else if (_mapManager.IsMapInitialized(mapId)) { foreach (var entity in context.Entities) { @@ -128,7 +126,7 @@ namespace Robust.Server.Maps } } - if (_pauseManager.IsMapPaused(mapId)) + if (_mapManager.IsMapPaused(mapId)) { foreach (var entity in context.Entities) { @@ -141,7 +139,7 @@ namespace Robust.Server.Maps public void SaveMap(MapId mapId, string yamlPath) { Logger.InfoS("map", $"Saving map {mapId} to {yamlPath}"); - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, _prototypeManager); + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _prototypeManager); foreach (var grid in _mapManager.GetAllMapGrids(mapId)) { context.RegisterGrid(grid); @@ -207,7 +205,7 @@ namespace Robust.Server.Maps LoadedMapData?.Invoke(data.Stream, resPath.ToString()); - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _prototypeManager, (YamlMappingNode) data.RootNode, mapId, options); context.Deserialize(); @@ -226,7 +224,6 @@ namespace Robust.Server.Maps private readonly IMapManagerInternal _mapManager; private readonly ITileDefinitionManager _tileDefinitionManager; private readonly IServerEntityManagerInternal _serverEntityManager; - private readonly IPauseManager _pauseManager; private readonly IPrototypeManager _prototypeManager; private readonly MapLoadOptions? _loadOptions; @@ -260,12 +257,11 @@ namespace Robust.Server.Maps public bool MapIsPostInit { get; private set; } public MapContext(IMapManagerInternal maps, ITileDefinitionManager tileDefs, - IServerEntityManagerInternal entities, IPauseManager pauseManager, IPrototypeManager prototypeManager) + IServerEntityManagerInternal entities, IPrototypeManager prototypeManager) { _mapManager = maps; _tileDefinitionManager = tileDefs; _serverEntityManager = entities; - _pauseManager = pauseManager; _prototypeManager = prototypeManager; RootNode = new YamlMappingNode(); @@ -283,13 +279,12 @@ namespace Robust.Server.Maps public MapContext(IMapManagerInternal maps, ITileDefinitionManager tileDefs, IServerEntityManagerInternal entities, - IPauseManager pauseManager, IPrototypeManager prototypeManager, + IPrototypeManager prototypeManager, YamlMappingNode node, MapId targetMapId, MapLoadOptions options) { _mapManager = maps; _tileDefinitionManager = tileDefs; _serverEntityManager = entities; - _pauseManager = pauseManager; _loadOptions = options; RootNode = node; @@ -612,7 +607,7 @@ namespace Robust.Server.Maps if (!MapIsPostInit) { - _pauseManager.AddUninitializedMap(TargetMap); + _mapManager.AddUninitializedMap(TargetMap); } } } @@ -719,7 +714,7 @@ namespace Robust.Server.Maps var isPostInit = false; foreach (var grid in Grids) { - if (_pauseManager.IsMapInitialized(grid.ParentMapId)) + if (_mapManager.IsMapInitialized(grid.ParentMapId)) { isPostInit = true; break; diff --git a/Robust.Shared/GameObjects/Components/IgnorePauseComponent.cs b/Robust.Shared/GameObjects/Components/IgnorePauseComponent.cs index f1f2ffc24..7ac2456a5 100644 --- a/Robust.Shared/GameObjects/Components/IgnorePauseComponent.cs +++ b/Robust.Shared/GameObjects/Components/IgnorePauseComponent.cs @@ -1,25 +1,24 @@ using Robust.Shared.IoC; -using Robust.Shared.Timing; +using Robust.Shared.Map; namespace Robust.Shared.GameObjects { [RegisterComponent] public sealed class IgnorePauseComponent : Component { - [Dependency] private readonly IEntityManager _entMan = default!; - protected override void OnAdd() { base.OnAdd(); - _entMan.GetComponent(Owner).EntityPaused = false; + IoCManager.Resolve().GetComponent(Owner).EntityPaused = false; } protected override void OnRemove() { base.OnRemove(); - if (IoCManager.Resolve().IsMapPaused(_entMan.GetComponent(Owner).MapID)) + var entMan = IoCManager.Resolve(); + if (IoCManager.Resolve().IsMapPaused(entMan.GetComponent(Owner).MapID)) { - _entMan.GetComponent(Owner).EntityPaused = true; + entMan.GetComponent(Owner).EntityPaused = true; } } } diff --git a/Robust.Shared/GameObjects/Components/MetaDataComponent.cs b/Robust.Shared/GameObjects/Components/MetaDataComponent.cs index 09a213bc2..8d5d0f224 100644 --- a/Robust.Shared/GameObjects/Components/MetaDataComponent.cs +++ b/Robust.Shared/GameObjects/Components/MetaDataComponent.cs @@ -147,9 +147,11 @@ namespace Robust.Shared.GameObjects get => _entityPaused; set { + if (_entityPaused == value) + return; + var entMan = IoCManager.Resolve(); - - if (_entityPaused == value || value && entMan.HasComponent(Owner)) + if (value && entMan.HasComponent(Owner)) return; _entityPaused = value; diff --git a/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs b/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs index cb4cfbb1c..bdb551b2c 100644 --- a/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs +++ b/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs @@ -728,17 +728,27 @@ namespace Robust.Shared.GameObjects var oldMapId = MapID; + //Set Paused state + var mapPaused = _mapManager.IsMapPaused(newMapId); + var metaData = _entMan.GetComponent(Owner); + metaData.EntityPaused = mapPaused; + MapID = newMapId; MapIdChanged(oldMapId); - UpdateChildMapIdsRecursive(MapID, _entMan); + UpdateChildMapIdsRecursive(MapID, _entMan, mapPaused); } - private void UpdateChildMapIdsRecursive(MapId newMapId, IEntityManager entMan) + private void UpdateChildMapIdsRecursive(MapId newMapId, IEntityManager entMan, bool mapPaused) { var xforms = _entMan.GetEntityQuery(); + var metaEnts = _entMan.GetEntityQuery(); foreach (var child in _children) { + //Set Paused state + var metaData = metaEnts.GetComponent(child); + metaData.EntityPaused = mapPaused; + var concrete = xforms.GetComponent(child); var old = concrete.MapID; @@ -747,7 +757,7 @@ namespace Robust.Shared.GameObjects if (concrete.ChildCount != 0) { - concrete.UpdateChildMapIdsRecursive(newMapId, entMan); + concrete.UpdateChildMapIdsRecursive(newMapId, entMan, mapPaused); } } } diff --git a/Robust.Shared/GameObjects/EntityManager.cs b/Robust.Shared/GameObjects/EntityManager.cs index 34e7a501d..035802cda 100644 --- a/Robust.Shared/GameObjects/EntityManager.cs +++ b/Robust.Shared/GameObjects/EntityManager.cs @@ -20,7 +20,6 @@ namespace Robust.Shared.GameObjects [IoC.Dependency] protected readonly IEntitySystemManager EntitySystemManager = default!; [IoC.Dependency] private readonly IMapManager _mapManager = default!; [IoC.Dependency] private readonly IGameTiming _gameTiming = default!; - [IoC.Dependency] private readonly IPauseManager _pauseManager = default!; #endregion Dependencies @@ -438,7 +437,7 @@ namespace Robust.Shared.GameObjects StartEntity(entity); // If the map we're initializing the entity on is initialized, run map init on it. - if (_pauseManager.IsMapInitialized(mapId)) + if (_mapManager.IsMapInitialized(mapId)) entity.RunMapInit(); } catch (Exception e) diff --git a/Robust.Shared/Map/GridId.cs b/Robust.Shared/Map/GridId.cs index 06e1b7819..b9efb2952 100644 --- a/Robust.Shared/Map/GridId.cs +++ b/Robust.Shared/Map/GridId.cs @@ -1,4 +1,6 @@ -using System; +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Serialization; namespace Robust.Shared.Map @@ -13,6 +15,14 @@ namespace Robust.Shared.Map internal readonly int Value; + /// + /// Constructs a new instance of . + /// + /// + /// This should NOT be used in regular code, and is only public for special/legacy + /// cases. Generally you should only use this for parsing a GridId in console commands + /// and immediately check if the grid actually exists in the . + /// public GridId(int value) { Value = value; @@ -57,6 +67,30 @@ namespace Robust.Shared.Map return self.Value; } + /// + /// is an alias of the that + /// holds the , so it can be implicitly converted. + /// + public static implicit operator EntityUid(GridId self) + { + // If this throws, you are either using an unallocated gridId, + // or using it after the grid was freed. Both of these are bugs. + return IoCManager.Resolve().GetGridEuid(self); + } + + /// + /// is an alias of the that + /// holds the . + /// + public static implicit operator GridId(EntityUid euid) + { + // If this throws, you are using an EntityUid that isn't a grid. + // This would raise the question, "Why does your code think this entity is a grid?". + // Grid-ness is defined by the entity having an IMapGridComponent, + // was the component removed without you knowing? + return IoCManager.Resolve().GetGridComp(euid).GridIndex; + } + public override string ToString() { return Value.ToString(); diff --git a/Robust.Shared/Map/IMapManager.cs b/Robust.Shared/Map/IMapManager.cs index dead9d039..85d27509f 100644 --- a/Robust.Shared/Map/IMapManager.cs +++ b/Robust.Shared/Map/IMapManager.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameObjects; using Robust.Shared.Maths; +using Robust.Shared.Timing; namespace Robust.Shared.Map { /// /// This manages all of the grids in the world. /// - public interface IMapManager + public interface IMapManager : IPauseManager { /// /// The default that is always available. Equivalent to SS13 Null space. diff --git a/Robust.Shared/Timing/PauseManager.cs b/Robust.Shared/Map/MapManager.Pause.cs similarity index 56% rename from Robust.Shared/Timing/PauseManager.cs rename to Robust.Shared/Map/MapManager.Pause.cs index 599421cd3..8d34fc6db 100644 --- a/Robust.Shared/Timing/PauseManager.cs +++ b/Robust.Shared/Map/MapManager.Pause.cs @@ -2,101 +2,121 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using Robust.Shared.Console; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Log; -using Robust.Shared.Map; using Robust.Shared.ViewVariables; -namespace Robust.Shared.Timing +namespace Robust.Shared.Map { - internal sealed class PauseManager : IPauseManager, IPostInjectInit + internal partial class MapManager { - [Dependency] private readonly IConsoleHost _conhost = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IEntityLookup _entityLookup = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - [ViewVariables] private readonly HashSet _pausedMaps = new(); [ViewVariables] private readonly HashSet _unInitializedMaps = new(); + /// public void SetMapPaused(MapId mapId, bool paused) { + if(!MapExists(mapId)) + throw new ArgumentException("That map does not exist."); + if (paused) { _pausedMaps.Add(mapId); - - foreach (var entity in _entityLookup.GetEntitiesInMap(mapId)) - { - _entityManager.GetComponent(entity).EntityPaused = true; - } } else { _pausedMaps.Remove(mapId); + } - foreach (var entity in _entityLookup.GetEntitiesInMap(mapId)) - { - _entityManager.GetComponent(entity).EntityPaused = false; - } + var mapEnt = GetMapEntityId(mapId); + var xformQuery = EntityManager.GetEntityQuery(); + var metaQuery = EntityManager.GetEntityQuery(); + + RecursiveSetPaused(mapEnt, paused, in xformQuery, in metaQuery); + } + + private static void RecursiveSetPaused(EntityUid entity, bool paused, + in EntityQuery xformQuery, + in EntityQuery metaQuery) + { + metaQuery.GetComponent(entity).EntityPaused = paused; + + foreach (var child in xformQuery.GetComponent(entity)._children) + { + RecursiveSetPaused(child, paused, in xformQuery, in metaQuery); } } + /// public void DoMapInitialize(MapId mapId) { + if(!MapExists(mapId)) + throw new ArgumentException("That map does not exist."); + if (IsMapInitialized(mapId)) throw new ArgumentException("That map is already initialized."); _unInitializedMaps.Remove(mapId); - foreach (var entity in IoCManager.Resolve().GetEntitiesInMap(mapId).ToArray()) - { - entity.RunMapInit(); + var mapEnt = GetMapEntityId(mapId); + var xformQuery = EntityManager.GetEntityQuery(); + var metaQuery = EntityManager.GetEntityQuery(); - // MapInit could have deleted this entity. - if(_entityManager.TryGetComponent(entity, out MetaDataComponent? meta)) - meta.EntityPaused = false; + RecursiveDoMapInit(mapEnt, in xformQuery, in metaQuery); + } + + private static void RecursiveDoMapInit(EntityUid entity, + in EntityQuery xformQuery, + in EntityQuery metaQuery) + { + // RunMapInit can modify the TransformTree + // ToArray caches deleted euids, we check here if they still exist. + if(!metaQuery.TryGetComponent(entity, out var meta)) + return; + + entity.RunMapInit(); + meta.EntityPaused = false; + + foreach (var child in xformQuery.GetComponent(entity)._children.ToArray()) + { + RecursiveDoMapInit(child, in xformQuery, in metaQuery); } } + /// public void DoGridMapInitialize(IMapGrid grid) { - DoGridMapInitialize(grid.Index); + // NOP } + /// public void DoGridMapInitialize(GridId gridId) { - var mapId = _mapManager.GetGrid(gridId).ParentMapId; - - foreach (var entity in _entityLookup.GetEntitiesInMap(mapId)) - { - if (_entityManager.GetComponent(entity).GridID != gridId) - continue; - - entity.RunMapInit(); - _entityManager.GetComponent(entity).EntityPaused = false; - } + // NOP } + /// public void AddUninitializedMap(MapId mapId) { _unInitializedMaps.Add(mapId); } + /// public bool IsMapPaused(MapId mapId) { return _pausedMaps.Contains(mapId) || _unInitializedMaps.Contains(mapId); } + /// public bool IsGridPaused(IMapGrid grid) { return IsMapPaused(grid.ParentMapId); } + /// public bool IsGridPaused(GridId gridId) { - if (_mapManager.TryGetGrid(gridId, out var grid)) + if (TryGetGrid(gridId, out var grid)) { return IsGridPaused(grid); } @@ -105,15 +125,18 @@ namespace Robust.Shared.Timing return true; } + /// public bool IsMapInitialized(MapId mapId) { return !_unInitializedMaps.Contains(mapId); } - /// - public void PostInject() + /// + /// Initializes the map pausing system. + /// + private void InitializeMapPausing() { - _mapManager.MapDestroyed += (_, args) => + MapDestroyed += (_, args) => { _pausedMaps.Remove(args.Map); _unInitializedMaps.Add(args.Map); @@ -130,10 +153,9 @@ namespace Robust.Shared.Timing return; } - string? arg = args[0]; - var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture)); + var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); - if (!_mapManager.MapExists(mapId)) + if (!MapExists(mapId)) { shell.WriteError("That map does not exist."); return; @@ -147,10 +169,9 @@ namespace Robust.Shared.Timing "querymappaused ", (shell, _, args) => { - string? arg = args[0]; - var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture)); + var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); - if (!_mapManager.MapExists(mapId)) + if (!MapExists(mapId)) { shell.WriteError("That map does not exist."); return; @@ -170,10 +191,9 @@ namespace Robust.Shared.Timing return; } - string? arg = args[0]; - var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture)); + var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); - if (!_mapManager.MapExists(mapId)) + if (!MapExists(mapId)) { shell.WriteLine("That map does not exist."); return; diff --git a/Robust.Shared/Map/MapManager.cs b/Robust.Shared/Map/MapManager.cs index 756094c4d..f2d02f127 100644 --- a/Robust.Shared/Map/MapManager.cs +++ b/Robust.Shared/Map/MapManager.cs @@ -1,3 +1,4 @@ +using Robust.Shared.Console; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; @@ -7,20 +8,25 @@ using Robust.Shared.Utility; namespace Robust.Shared.Map; /// +[Virtual] internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber { [field: Dependency] public IGameTiming GameTiming { get; } = default!; [field: Dependency] public IEntityManager EntityManager { get; } = default!; + + [Dependency] private readonly IConsoleHost _conhost = default!; + [Dependency] private readonly IEntityLookup _entityLookup = default!; /// public void Initialize() { - InitializeGridTrees(); #if DEBUG DebugTools.Assert(!_dbgGuardInit); DebugTools.Assert(!_dbgGuardRunning); _dbgGuardInit = true; #endif + InitializeGridTrees(); + InitializeMapPausing(); } /// diff --git a/Robust.Shared/SharedIoC.cs b/Robust.Shared/SharedIoC.cs index 537b49654..996d9e9d2 100644 --- a/Robust.Shared/SharedIoC.cs +++ b/Robust.Shared/SharedIoC.cs @@ -1,4 +1,4 @@ -using Robust.Shared.Asynchronous; +using Robust.Shared.Asynchronous; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; using Robust.Shared.Exceptions; @@ -32,7 +32,7 @@ namespace Robust.Shared IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Robust.Shared/Timing/IPauseManager.cs b/Robust.Shared/Timing/IPauseManager.cs index a898ec6e0..b8a13491b 100644 --- a/Robust.Shared/Timing/IPauseManager.cs +++ b/Robust.Shared/Timing/IPauseManager.cs @@ -1,15 +1,20 @@ +using System; using JetBrains.Annotations; using Robust.Shared.Map; namespace Robust.Shared.Timing { + [Obsolete("Use the same functions on IMapManager.")] public interface IPauseManager { void SetMapPaused(MapId mapId, bool paused); void DoMapInitialize(MapId mapId); + [Obsolete("This function does nothing, per-grid pausing isn't a thing anymore.")] void DoGridMapInitialize(GridId gridId); + + [Obsolete("This function does nothing, per-grid pausing isn't a thing anymore.")] void DoGridMapInitialize(IMapGrid grid); void AddUninitializedMap(MapId mapId); diff --git a/Robust.UnitTesting/Server/RobustServerSimulation.cs b/Robust.UnitTesting/Server/RobustServerSimulation.cs index 2962644f6..382004e1d 100644 --- a/Robust.UnitTesting/Server/RobustServerSimulation.cs +++ b/Robust.UnitTesting/Server/RobustServerSimulation.cs @@ -207,7 +207,7 @@ namespace Robust.UnitTesting.Server container.Register(); container.Register(); container.Register(); - container.RegisterInstance(new Mock().Object); // TODO: get timing working similar to RobustIntegrationTest + container.Register(); container.Register(); _diFactory?.Invoke(container); diff --git a/Robust.UnitTesting/Shared/Map/MapGrid_Tests.cs b/Robust.UnitTesting/Shared/Map/MapGrid_Tests.cs index 476ea1dd9..f74511ca3 100644 --- a/Robust.UnitTesting/Shared/Map/MapGrid_Tests.cs +++ b/Robust.UnitTesting/Shared/Map/MapGrid_Tests.cs @@ -8,44 +8,37 @@ using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Physics; -using Robust.Shared.Physics.Broadphase; -using MapGrid = Robust.Shared.Map.MapGrid; +using Robust.UnitTesting.Server; namespace Robust.UnitTesting.Shared.Map { [TestFixture, TestOf(typeof(MapGrid))] - sealed class MapGrid_Tests : RobustUnitTest + sealed class MapGrid_Tests { - protected override void OverrideIoC() + private static ISimulation SimulationFactory() { - base.OverrideIoC(); + var sim = RobustServerSimulation + .NewSimulation() + .InitializeInstance(); - var mock = new Mock(); - var broady = new BroadPhaseSystem(); - var physics = new PhysicsSystem(); - mock.Setup(m => m.GetEntitySystem()).Returns(broady); - mock.Setup(m => m.GetEntitySystem()).Returns(physics); - - IoCManager.RegisterInstance(mock.Object, true); - } - - [OneTimeSetUp] - public void Setup() - { - IoCManager.Resolve().GenerateNetIds(); + return sim; } [Test] public void GetTileRefCoords() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); + grid.SetTile(new Vector2i(-9, -1), new Tile(1, 2)); var result = grid.GetTileRef(new Vector2i(-9, -1)); Assert.That(grid.ChunkCount, Is.EqualTo(1)); Assert.That(grid.GetMapChunks().Keys.ToList()[0], Is.EqualTo(new Vector2i(-2, -1))); - Assert.That(result, Is.EqualTo(new TileRef(new MapId(5), new GridId(1), new Vector2i(-9,-1), new Tile(1, 2)))); + Assert.That(result, Is.EqualTo(new TileRef(mapId, grid.Index, new Vector2i(-9,-1), new Tile(1, 2)))); } /// @@ -54,7 +47,11 @@ namespace Robust.UnitTesting.Shared.Map [Test] public void BoundsExpansion() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); + grid.WorldPosition = new Vector2(3, 5); grid.SetTile(new Vector2i(-1, -2), new Tile(1)); grid.SetTile(new Vector2i(1, 2), new Tile(1)); @@ -74,7 +71,11 @@ namespace Robust.UnitTesting.Shared.Map [Test] public void BoundsContract() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); + grid.WorldPosition = new Vector2(3, 5); grid.SetTile(new Vector2i(-1, -2), new Tile(1)); grid.SetTile(new Vector2i(1, 2), new Tile(1)); @@ -93,7 +94,10 @@ namespace Robust.UnitTesting.Shared.Map [Test] public void GridTileToChunkIndices() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); var result = grid.GridTileToChunkIndices(new Vector2i(-9, -1)); @@ -106,7 +110,10 @@ namespace Robust.UnitTesting.Shared.Map [Test] public void ToLocalCentered() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); var result = grid.GridTileToLocal(new Vector2i(0, 0)).Position; @@ -117,7 +124,10 @@ namespace Robust.UnitTesting.Shared.Map [Test] public void TryGetTileRefNoTile() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); var foundTile = grid.TryGetTileRef(new Vector2i(-9, -1), out var tileRef); @@ -129,7 +139,11 @@ namespace Robust.UnitTesting.Shared.Map [Test] public void TryGetTileRefTileExists() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); + grid.SetTile(new Vector2i(-9, -1), new Tile(1, 2)); var foundTile = grid.TryGetTileRef(new Vector2i(-9, -1), out var tileRef); @@ -137,13 +151,17 @@ namespace Robust.UnitTesting.Shared.Map Assert.That(foundTile, Is.True); Assert.That(grid.ChunkCount, Is.EqualTo(1)); Assert.That(grid.GetMapChunks().Keys.ToList()[0], Is.EqualTo(new Vector2i(-2, -1))); - Assert.That(tileRef, Is.EqualTo(new TileRef(new MapId(5), new GridId(1), new Vector2i(-9, -1), new Tile(1, 2)))); + Assert.That(tileRef, Is.EqualTo(new TileRef(mapId, grid.Index, new Vector2i(-9, -1), new Tile(1, 2)))); } [Test] public void PointCollidesWithGrid() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); + grid.SetTile(new Vector2i(19, 23), new Tile(1)); var result = grid.CollidesWithGrid(new Vector2i(19, 23)); @@ -154,31 +172,16 @@ namespace Robust.UnitTesting.Shared.Map [Test] public void PointNotCollideWithGrid() { - var grid = MapGridFactory(new GridId(1)); + var sim = SimulationFactory(); + var mapMan = sim.Resolve(); + var mapId = mapMan.CreateMap(); + var grid = (IMapGridInternal)mapMan.CreateGrid(mapId, null, 8); + grid.SetTile(new Vector2i(19, 23), new Tile(1)); var result = grid.CollidesWithGrid(new Vector2i(19, 24)); Assert.That(result, Is.False); } - - private static IMapGridInternal MapGridFactory(GridId id) - { - var mapId = new MapId(5); - var mapMan = IoCManager.Resolve(); - - if(mapMan.MapExists(mapId)) - mapMan.DeleteMap(mapId); - - mapMan.CreateMap(mapId); - - if(mapMan.GridExists(id)) - mapMan.DeleteGrid(id); - - var newGrid = mapMan.CreateGrid(mapId, id, 8); - newGrid.WorldPosition = new Vector2(3, 5); - - return (IMapGridInternal)newGrid; - } } } diff --git a/Robust.UnitTesting/Shared/Map/MapPauseTests.cs b/Robust.UnitTesting/Shared/Map/MapPauseTests.cs new file mode 100644 index 000000000..bbc796402 --- /dev/null +++ b/Robust.UnitTesting/Shared/Map/MapPauseTests.cs @@ -0,0 +1,272 @@ +using System; +using System.Linq; +using NUnit.Framework; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.UnitTesting.Server; + +// ReSharper disable AccessToStaticMemberViaDerivedType + +namespace Robust.UnitTesting.Shared.Map; + +[TestFixture] +internal sealed class MapPauseTests +{ + private static ISimulation SimulationFactory() + { + var sim = RobustServerSimulation + .NewSimulation() + .RegisterComponents(factory => factory.RegisterClass()) + .InitializeInstance(); + + return sim; + } + + /// + /// When an entity is on a paused map, it does not get returned by an EntityQuery. + /// + [Test] + public void Paused_NotIncluded_NotInQuery() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, true); + + entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + var query = entMan.EntityQuery(false).ToList(); + + // 0 ents, map and the spawned one are not returned + Assert.That(query.Count, Is.EqualTo(0)); + } + + /// + /// When an entity is on an unpaused map, it is returned by an EntityQuery. + /// + [Test] + public void UnPaused_NotIncluded_InQuery() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, false); + + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + var query = entMan.EntityQuery(false).ToList(); + + // 2 ents, map and the spawned one + Assert.That(query.Count, Is.EqualTo(2)); + } + + /// + /// When an entity is on a paused map, it is get returned by an EntityQuery when included. + /// + [Test] + public void Paused_Included_InQuery() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, true); + + entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + var query = entMan.EntityQuery(true).ToList(); + + // 2 ents, map and the spawned one are returned because includePaused + Assert.That(query.Count, Is.EqualTo(2)); + } + + /// + /// A new child entity added to a paused map will be created paused. + /// + [Test] + public void Paused_AddEntity_IsPaused() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, true); + + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + var metaData = entMan.GetComponent(newEnt); + Assert.That(metaData.EntityPaused, Is.True); + } + + /// + /// A new child entity added to an unpaused map will be created unpaused. + /// + [Test] + public void UnPaused_AddEntity_IsNotPaused() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, false); + + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + var metaData = entMan.GetComponent(newEnt); + Assert.That(metaData.EntityPaused, Is.False); + } + + /// + /// When a new grid is added to a paused map, the grid becomes paused. + /// + [Test] + public void Paused_AddGrid_GridPaused() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, true); + + // act + var newGrid = mapMan.CreateGrid(mapId); + + // assert + Assert.That(mapMan.IsMapPaused(mapId), Is.True); + Assert.That(mapMan.IsGridPaused(newGrid.GridEntityId), Is.True); + + var metaData = entMan.GetComponent(newGrid.GridEntityId); + Assert.That(metaData.EntityPaused, Is.True); + } + + /// + /// When a tree of entities are teleported from a paused map + /// to an unpaused map, all of the entities in the tree are unpaused. + /// + [Test] + public void Paused_TeleportBetweenMaps_Unpaused() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var map1 = mapMan.CreateMap(); + mapMan.SetMapPaused(map1, true); + + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, map1)); + var xform = entMan.GetComponent(newEnt); + + var map2 = mapMan.CreateMap(); + mapMan.SetMapPaused(map2, false); + + // Act + xform.ParentUid = mapMan.GetMapEntityId(map2); + + var metaData = entMan.GetComponent(newEnt); + Assert.That(metaData.EntityPaused, Is.False); + } + + /// + /// When a tree of entities are teleported from an unpaused map + /// to a paused map, all of the entitites in the tree are paused. + /// + [Test] + public void Unpaused_TeleportBetweenMaps_IsPaused() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + // arrange + var map1 = mapMan.CreateMap(); + mapMan.SetMapPaused(map1, false); + + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, map1)); + var xform = entMan.GetComponent(newEnt); + + var map2 = mapMan.CreateMap(); + mapMan.SetMapPaused(map2, true); + + // Act + xform.ParentUid = mapMan.GetMapEntityId(map2); + + var metaData = entMan.GetComponent(newEnt); + Assert.That(metaData.EntityPaused, Is.True); + } + + /// + /// When a paused map is unpaused, all of the entities on the map are unpaused. + /// + [Test] + public void Paused_UnpauseMap_UnpausedEntities() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, true); + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + mapMan.SetMapPaused(mapId, false); + + var metaData = entMan.GetComponent(newEnt); + Assert.That(metaData.EntityPaused, Is.False); + } + + /// + /// When an unpaused map is paused, all of the entities on the map are paused. + /// + [Test] + public void Unpaused_PauseMap_PausedEntities() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, false); + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + mapMan.SetMapPaused(mapId, true); + + var metaData = entMan.GetComponent(newEnt); + Assert.That(metaData.EntityPaused, Is.True); + } + + /// + /// An entity that has set IgnorePause will not be paused when the map is paused. + /// + [Test] + public void IgnorePause_PauseMap_NotPaused() + { + var sim = SimulationFactory(); + var entMan = sim.Resolve(); + var mapMan = sim.Resolve(); + + var mapId = mapMan.CreateMap(); + mapMan.SetMapPaused(mapId, false); + var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + + entMan.AddComponent(newEnt); + mapMan.SetMapPaused(mapId, true); + + var metaData = entMan.GetComponent(newEnt); + Assert.That(metaData.EntityPaused, Is.False); + } +}