diff --git a/Resources/Locale/en-US/commands.ftl b/Resources/Locale/en-US/commands.ftl index 4d4e3264618..fcafc42eedf 100644 --- a/Resources/Locale/en-US/commands.ftl +++ b/Resources/Locale/en-US/commands.ftl @@ -306,6 +306,15 @@ cmd-addmap-help = Usage: {$command} [pre-init] cmd-rmmap-desc = Removes a map from the world. You cannot remove nullspace. cmd-rmmap-help = Usage: {$command} +cmd-pausemap-desc = Pauses a map, pausing all simulation processing on it. +cmd-pausemap-help = Usage: pausemap + +cmd-unpausemap-desc = Unpauses a map, resuming all simulation processing on it. +cmd-unpausemap-help = Usage: unpausemap + +cmd-querymappaused-desc = Check whether a map is paused or not. +cmd-querymappaused-help = Usage: querymappaused + cmd-savegrid-desc = Serializes a grid to disk. cmd-savegrid-help = Usage: {$command} diff --git a/Robust.Server/GameStates/PvsSystem.Chunks.cs b/Robust.Server/GameStates/PvsSystem.Chunks.cs index 99466b1f6b7..a270c7410b7 100644 --- a/Robust.Server/GameStates/PvsSystem.Chunks.cs +++ b/Robust.Server/GameStates/PvsSystem.Chunks.cs @@ -133,7 +133,7 @@ private void GetVisibleChunks(Entity eye, _grids.Clear(); var rangeVec = new Vector2(range, range); var box = new Box2(viewPos - rangeVec, viewPos + rangeVec); - _mapManager.FindGridsIntersecting(map, box, ref _grids, approx: true, includeMap: false); + _maps.FindGridsIntersecting(map, box, ref _grids, approx: true, includeMap: false); foreach (var (grid, _) in _grids) { diff --git a/Robust.Server/GameStates/PvsSystem.cs b/Robust.Server/GameStates/PvsSystem.cs index 2095023f220..02615e9ea73 100644 --- a/Robust.Server/GameStates/PvsSystem.cs +++ b/Robust.Server/GameStates/PvsSystem.cs @@ -28,7 +28,6 @@ namespace Robust.Server.GameStates; internal sealed partial class PvsSystem : EntitySystem { [Dependency] private IConfigurationManager _configManager = default!; - [Dependency] private INetworkedMapManager _mapManager = default!; [Dependency] private IServerEntityNetworkManager _netEntMan = default!; [Dependency] private IPlayerManager _playerManager = default!; [Dependency] private IParallelManager _parallelManager = default!; @@ -40,6 +39,7 @@ internal sealed partial class PvsSystem : EntitySystem [Dependency] private IParallelManagerInternal _parallelMgr = default!; [Dependency] private PvsOverrideSystem _pvsOverride = default!; [Dependency] private IServerReplayRecordingManager _replay = default!; + [Dependency] private SharedMapSystem _maps = default!; // TODO make this a cvar. Make it in terms of seconds and tie it to tick rate? // Main issue is that I CBF figuring out the logic for handling it changing mid-game. @@ -288,7 +288,7 @@ private void CullDeletionHistory(GameTick oldestAck) { using var _ = Histogram.WithLabels("Cull History").NewTimer(); CullDeletionHistoryUntil(oldestAck); - _mapManager.CullDeletionHistory(oldestAck); + _maps.CullDeletionHistory(oldestAck); } private void GetEntityStates(PvsSession session) diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.Queries.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.Queries.cs new file mode 100644 index 00000000000..ae3165b0543 --- /dev/null +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.Queries.cs @@ -0,0 +1,613 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Map.Enumerators; +using Robust.Shared.Maths; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Shapes; +using Transform = Robust.Shared.Physics.Transform; + +namespace Robust.Shared.GameObjects; + +public abstract partial class SharedMapSystem +{ + + /// + /// Whether and its extended family should only approximately check for intersection by default. + /// + public const bool Approximate = false; + + /// + /// Whether and its extended family should also check the map itself by default. + /// + public const bool IncludeMap = true; + + #region TryFindGridAt + + /// + /// Attempts to find a grid which overlaps with a given position on a given map. + /// If the map is itself a grid and there is no other grid overlapping with the given position this will return the map itself as such a grid. + /// + /// The uid of the map to search for a valid grid. + /// The exact position within and relative to the map to search for a valid grid. + /// Returns the uid of the grid found, if any. + /// Returns the component of the grid found, if any. + /// True if a grid overlapping with the given position within the given map was found, or false otherwise. + public bool TryFindGridAt(EntityUid mapEnt, Vector2 worldPos, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid) + { + var rangeVec = new Vector2(0.2f, 0.2f); + + // Need to enlarge the AABB by at least the grid shrinkage size. + var aabb = new Box2(worldPos - rangeVec, worldPos + rangeVec); + + uid = EntityUid.Invalid; + grid = null; + var state = (uid, grid, worldPos, this, _transform); + + FindGridsIntersecting(mapEnt, aabb, ref state, static (EntityUid iUid, MapGridComponent iGrid, ref ( + EntityUid uid, + MapGridComponent? grid, + Vector2 worldPos, + SharedMapSystem mapSystem, + SharedTransformSystem xformSystem) tuple) => + { + // Turn the worldPos into a localPos and work out the relevant chunk we need to check + // This is much faster than iterating over every chunk individually. + // (though now we need some extra calcs up front). + + // Doesn't use WorldBounds because it's just an AABB. + var matrix = tuple.xformSystem.GetInvWorldMatrix(iUid); + var localPos = Vector2.Transform(tuple.worldPos, matrix); + + // NOTE: + // If you change this to use fixtures instead (i.e. if you want half-tiles) then you need to make sure + // you account for the fact that fixtures are shrunk slightly! + var chunkIndices = GetChunkIndices(localPos, iGrid.ChunkSize); + + if (!iGrid.Chunks.TryGetValue(chunkIndices, out var chunk)) + return true; + + var chunkRelative = GetChunkRelative(localPos, iGrid.ChunkSize); + var chunkTile = chunk.GetTile(chunkRelative); + + if (chunkTile.IsEmpty) + return true; + + tuple.uid = iUid; + tuple.grid = iGrid; + return false; + }, approx: true, includeMap: false); + + if (state.grid == null && _gridQuery.TryGetComponent(mapEnt, out var mapGrid)) + { + uid = mapEnt; + grid = mapGrid; + return true; + } + + uid = state.uid; + grid = state.grid; + return grid != null; + } + + /// + /// The id of the map to search for a valid grid. + public bool TryFindGridAt(MapId mapId, Vector2 worldPos, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid) + { + if (TryGetMap(mapId, out var map)) + return TryFindGridAt(map.Value, worldPos, out uid, out grid); + + uid = default; + grid = null; + return false; + } + + /// + /// The map position to search for a valid grid. + public bool TryFindGridAt(MapCoordinates mapCoordinates, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid) + { + return TryFindGridAt(mapCoordinates.MapId, mapCoordinates.Position, out uid, out grid); + } + + #endregion + + #region MapId + + /// + /// Adds every grid on the specified map which intersects the given region to the provided collection. + /// + /// The shape of the region to check. + /// The transform, relative to the map, of the region to check. + public void FindGridsIntersecting( + MapId mapId, + TShape shape, + Transform transform, + ref List> grids, + bool approx = Approximate, + bool includeMap = IncludeMap) where TShape : IPhysShape + { + if (TryGetMap(mapId, out var mapEnt)) + FindGridsIntersecting(mapEnt.Value, shape, transform, ref grids, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// + /// The shape of the region to check. + /// The transform, relative to the map, of the region to check. + public void FindGridsIntersecting( + MapId mapId, + TShape shape, + Transform transform, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) where TShape : IPhysShape + { + if (TryGetMap(mapId, out var mapEnt)) + FindGridsIntersecting(mapEnt.Value, shape, transform, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// Allows providing some additional to pass to the callback when it is invoked. + /// + /// The shape of the region to check. + /// The transform, relative to the map, of the region to check. + public void FindGridsIntersecting( + MapId mapId, + TShape shape, + Transform transform, + ref TState state, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) where TShape : IPhysShape + { + if (TryGetMap(mapId, out var mapEnt)) + FindGridsIntersecting(mapEnt.Value, shape, transform, ref state, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// + public void FindGridsIntersecting( + MapId mapId, + Box2 worldAABB, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + if (TryGetMap(mapId, out var mapEnt)) + FindGridsIntersecting(mapEnt.Value, worldAABB, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// Allows providing some additional to pass to the callback when it is invoked. + /// + public void FindGridsIntersecting( + MapId mapId, + Box2 worldAABB, + ref TState state, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + if (TryGetMap(mapId, out var map)) + FindGridsIntersecting(map.Value, worldAABB, ref state, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Adds every grid on the specified map which intersects the given region to the provided collection. + /// + public void FindGridsIntersecting( + MapId mapId, + Box2 worldAABB, + ref List> grids, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + if (TryGetMap(mapId, out var map)) + FindGridsIntersecting(map.Value, worldAABB, ref grids, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// + public void FindGridsIntersecting( + MapId mapId, + Box2Rotated worldBounds, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + if (TryGetMap(mapId, out var mapEnt)) + FindGridsIntersecting(mapEnt.Value, worldBounds, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// Allows providing some additional to pass to the callback when it is invoked. + /// + public void FindGridsIntersecting( + MapId mapId, + Box2Rotated worldBounds, + ref TState state, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + if (TryGetMap(mapId, out var mapEnt)) + FindGridsIntersecting(mapEnt.Value, worldBounds, ref state, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Adds every grid on the specified map which intersects the given region to the provided collection. + /// + public void FindGridsIntersecting( + MapId mapId, + Box2Rotated worldBounds, + ref List> grids, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + if (TryGetMap(mapId, out var mapEnt)) + FindGridsIntersecting(mapEnt.Value, worldBounds, ref grids, approx: approx, includeMap: includeMap); + } + + #endregion + + #region EntityUid + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// + /// The shape of the region to check. + /// The transform, relative to the map, of the region to check. + public void FindGridsIntersecting( + EntityUid mapEnt, + TShape shape, + Transform transform, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) where TShape : IPhysShape + { + FindGridsIntersecting(mapEnt, shape, shape.ComputeAABB(transform, 0), transform, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// Allows providing some additional to pass to the callback when it is invoked. + /// + /// The shape of the region to check. + /// The transform, relative to the map, of the region to check. + public void FindGridsIntersecting( + EntityUid mapEnt, + TShape shape, + Transform transform, + ref TState state, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) where TShape : IPhysShape + { + FindGridsIntersecting(mapEnt, shape, shape.ComputeAABB(transform, 0), transform, ref state, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Adds every grid on the specified map which intersects the given region to the provided list. + /// + /// The shape of the region to check. + /// The transform, relative to the map, of the region to check. + public void FindGridsIntersecting( + EntityUid mapEnt, + TShape shape, + Transform transform, + ref List> grids, + bool approx = Approximate, + bool includeMap = IncludeMap) where TShape : IPhysShape + { + FindGridsIntersecting(mapEnt, shape, shape.ComputeAABB(transform, 0), transform, ref grids, approx: approx, includeMap: includeMap); + } + + /// + /// Adds every grid on the specified map which intersects the given regions to the provided collection. + /// + /// A set of regions to check. + /// The transform, relative to the map, of the regions to check. + public void FindGridsIntersecting( + EntityUid mapEnt, + List shapes, + Transform transform, + ref List> entities, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + foreach (var shape in shapes) + { + FindGridsIntersecting(mapEnt, shape, transform, ref entities, approx: approx, includeMap: includeMap); + } + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// + public void FindGridsIntersecting( + EntityUid mapEnt, + Box2 worldAABB, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + var shape = new SlimPolygon(worldAABB); + FindGridsIntersecting(mapEnt, shape, worldAABB, Robust.Shared.Physics.Transform.Empty, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// Allows providing some additional to pass to the callback when it is invoked. + /// + public void FindGridsIntersecting( + EntityUid mapEnt, + Box2 worldAABB, + ref TState state, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + var shape = new SlimPolygon(worldAABB); + FindGridsIntersecting(mapEnt, shape, worldAABB, Robust.Shared.Physics.Transform.Empty, ref state, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Adds every grid on the specified map which intersects the given regions to the provided list. + /// + public void FindGridsIntersecting( + EntityUid mapEnt, + Box2 worldAABB, + ref List> grids, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + var shape = new SlimPolygon(worldAABB); + FindGridsIntersecting(mapEnt, shape, worldAABB, Robust.Shared.Physics.Transform.Empty, ref grids, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// + public void FindGridsIntersecting( + EntityUid mapEnt, + Box2Rotated worldBounds, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + var shape = new SlimPolygon(worldBounds); + FindGridsIntersecting(mapEnt, shape, Robust.Shared.Physics.Transform.Empty, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// Allows providing some additional to pass to the callback when it is invoked. + /// + public void FindGridsIntersecting( + EntityUid mapEnt, + Box2Rotated worldBounds, + ref TState state, + GridCallback callback, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + var shape = new SlimPolygon(worldBounds); + FindGridsIntersecting(mapEnt, shape, Robust.Shared.Physics.Transform.Empty, ref state, callback, approx: approx, includeMap: includeMap); + } + + /// + /// Adds every grid on the specified map which intersects the given regions to the provided list. + /// + public void FindGridsIntersecting( + EntityUid mapEnt, + Box2Rotated worldBounds, + ref List> grids, + bool approx = Approximate, + bool includeMap = IncludeMap) + { + var shape = new SlimPolygon(worldBounds); + FindGridsIntersecting(mapEnt, shape, Robust.Shared.Physics.Transform.Empty, ref grids, approx: approx, includeMap: includeMap); + } + + #endregion + + /// + /// Enumerates all of the grids located on a given map. + /// + public IEnumerable> GetAllGrids(MapId mapId) + { + var query = AllEntityQuery(); + while (query.MoveNext(out var uid, out var grid, out var xform)) + { + if (xform.MapID != mapId) + continue; + + yield return (uid, grid); + } + } + + /// + /// This version only provides the component without the uid and should not be used. + /// + /// + [Obsolete("use GetAllGrids instead")] + public IEnumerable GetAllMapGrids(MapId mapId) + { + var query = AllEntityQuery(); + while (query.MoveNext(out var grid, out var xform)) + { + if (xform.MapID == mapId) + yield return grid; + } + } + + /// + /// Adds every grid on the specified map which intersects the given regions to the provided list. + /// + /// The shape of the region to check. + /// The world-local axis aligned bounding box of the region to check. + /// The transform, relative to the map, of the region to check. + [Access(typeof(MapManager), Other = AccessPermissions.None)] + public void FindGridsIntersecting( + EntityUid mapEnt, + TShape shape, + Box2 worldAABB, + Transform transform, + ref List> grids, + bool approx, + bool includeMap) where TShape : IPhysShape + { + var state = grids; + FindGridsIntersecting(mapEnt, shape, worldAABB, transform, ref state, + static (EntityUid uid, MapGridComponent grid, ref List> state) => + { + state.Add((uid, grid)); + return true; + }, + approx, includeMap + ); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// + /// The shape of the region to check. + /// The world-local axis aligned bounding box of the region to check. + /// The transform, relative to the map, of the region to check. + private void FindGridsIntersecting( + EntityUid mapEnt, + TShape shape, + Box2 worldAABB, + Transform transform, + GridCallback callback, + bool approx, + bool includeMap) where TShape : IPhysShape + { + var state = callback; + FindGridsIntersecting(mapEnt, shape, worldAABB, transform, ref state, + static (EntityUid uid, MapGridComponent grid, ref GridCallback state) => state.Invoke(uid, grid), + approx, includeMap + ); + } + + /// + /// Invokes the provided callback on every grid on the specified map which intersect the given region. + /// Allows providing some additional to pass to the callback when it is invoked. + /// + /// The shape of the region to check. + /// The world-local axis aligned bounding box of the region to check. + /// The transform, relative to the map, of the region to check. + private void FindGridsIntersecting( + EntityUid mapEnt, + TShape shape, + Box2 worldAABB, + Transform transform, + ref TState state, + GridCallback callback, + bool approx, + bool includeMap) where TShape : IPhysShape + { + if (!_gridTreeQuery.TryGetComponent(mapEnt, out var gridTree)) + return; + + if (includeMap && _gridQuery.TryGetComponent(mapEnt, out var mapGrid)) + { + callback(mapEnt, mapGrid, ref state); + } + + var gridState = new GridQueryState( + callback, + state, + worldAABB, + shape, + transform, + gridTree.Tree, + this, + _transform, + approx); + + gridTree.Tree.Query(ref gridState, static (ref GridQueryState state, DynamicTree.Proxy proxy) => + { + // Even for approximate we'll check if any chunks roughly overlap. + var data = state.Tree.GetUserData(proxy); + var gridInvMatrix = state.TransformSystem.GetInvWorldMatrix(data.Uid); + var localAABB = gridInvMatrix.TransformBox(state.WorldAABB); + + var overlappingChunks = state.MapSystem.GetLocalMapChunks(data.Uid, data.Grid, localAABB); + + if (state.Approximate) + { + if (!overlappingChunks.MoveNext(out _)) + return true; + } + else if (!state.MapSystem.IsIntersecting(overlappingChunks, state.Shape, state.Transform, (data.Uid, data.Fixtures))) + { + return true; + } + + var callbackState = state.State; + var result = state.Callback(data.Uid, data.Grid, ref callbackState); + state.State = callbackState; + + return result; + }, worldAABB); + + // By-ref things + state = gridState.State; + } + + /// + /// Tests whether any of a collection of grid chunks intersect with a given region. + /// + private bool IsIntersecting( + ChunkEnumerator enumerator, + TShape shape, + Transform shapeTransform, + Entity grid) where TShape : IPhysShape + { + var gridTransform = _physics.GetPhysicsTransform(grid); + + while (enumerator.MoveNext(out var chunk)) + { + foreach (var id in chunk.Fixtures) + { + var fixture = grid.Comp.Fixtures[id]; + + for (var j = 0; j < fixture.Shape.ChildCount; j++) + { + if (_manifolds.TestOverlap(shape, 0, fixture.Shape, j, shapeTransform, gridTransform)) + { + return true; + } + } + } + } + + return false; + } + + private record struct GridQueryState( + GridCallback Callback, + TState State, + Box2 WorldAABB, + TShape Shape, + Transform Transform, + B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree, + SharedMapSystem MapSystem, + SharedTransformSystem TransformSystem, + bool Approximate + ) where TShape : IPhysShape; +} + +public delegate bool GridCallback(EntityUid gridUid, MapGridComponent gridComp); +public delegate bool GridCallback(EntityUid gridUid, MapGridComponent gridComp, ref TState state); diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs index 6c21c94534d..2e4c328a8e7 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs @@ -21,6 +21,52 @@ namespace Robust.Shared.GameObjects; public abstract partial class SharedMapSystem { + #region CreateGrid + + /// + /// Creates a new grid entity on a given map. + /// + public Entity CreateGridEntity(MapId mapId, GridCreateOptions? options = null) + { + return CreateGridEntity(GetMap(mapId), options); + } + + /// + /// Creates a new grid entity on a given map. + /// + public Entity CreateGridEntity(EntityUid mapEnt, GridCreateOptions? options = null) + { + options ??= GridCreateOptions.Default; + return CreateGridInternal(mapEnt, options.Value); + } + + protected Entity CreateGridInternal(EntityUid mapEnt, GridCreateOptions options) + { + var gridEnt = EntityManager.CreateEntityUninitialized(null); + + var grid = EnsureComp(gridEnt); + grid.ChunkSize = options.ChunkSize; + + Log.Debug("Binding new grid {gridEnt}"); + + //TODO: This is a hack to get TransformComponent.MapId working before entity states + //are applied. After they are applied the parent may be different, but the MapId will + //be the same. This causes TransformComponent.ParentUid of a grid to be unsafe to + //use in transform states anytime before the state parent is properly set. + _transform.SetParent(gridEnt, mapEnt); + + var meta = _metaQuery.GetComponent(gridEnt); + EntityManager.System().SetEntityName(gridEnt, $"grid", meta); + EntityManager.InitializeComponents(gridEnt, meta); + EntityManager.StartComponents(gridEnt); + // Note that this does not actually map-initialize the grid entity, even if the map its being spawn on has already been initialized. + // I don't know whether that is intentional or not. + + return (gridEnt, grid); + } + + #endregion + #region Chunk helpers [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -322,7 +368,7 @@ private void ApplyChunkData( var gridIndices = deletedChunk.ChunkTileToGridTile((x, y)); var newTileRef = new TileRef(uid, gridIndices, Tile.Empty); - _mapInternal.RaiseOnTileChanged(gridEnt, newTileRef, oldTile, index); + RaiseOnTileChanged(gridEnt, newTileRef, oldTile, index); } } @@ -484,6 +530,20 @@ private void GetFullState(EntityUid uid, MapGridComponent component, ref Compone #endif } + /// + /// Prunes tracked grid chunk deletions older than some given game tick. + /// + public void CullDeletionHistory(GameTick upToTick) + { + var query = AllEntityQuery(); + + while (query.MoveNext(out var grid)) + { + var chunks = grid.ChunkDeletionHistory; + chunks.RemoveAll(t => t.tick < upToTick); + } + } + private void OnGridAdd(EntityUid uid, MapGridComponent component, ComponentAdd args) { var msg = new GridAddEvent(uid); @@ -846,7 +906,7 @@ public void SetTiles(EntityUid uid, MapGridComponent grid, List<(Vector2i GridIn // Suppress sending out events for each tile changed // We're going to send them all out together at the end - MapManager.SuppressOnTileChanged = true; + SuppressOnTileChanged = true; foreach (var (gridIndices, tile) in tiles) { @@ -883,7 +943,7 @@ public void SetTiles(EntityUid uid, MapGridComponent grid, List<(Vector2i GridIn RegenerateCollision(uid, grid, modified); // Back to normal - MapManager.SuppressOnTileChanged = false; + SuppressOnTileChanged = false; } public TilesEnumerator GetLocalTilesEnumerator(EntityUid uid, MapGridComponent grid, Box2 aabb, @@ -1631,10 +1691,10 @@ private void OnTileModified(EntityUid uid, MapGridComponent grid, MapChunk mapCh // The map serializer currently sets tiles of unbound grids as part of the deserialization process // It properly sets SuppressOnTileChanged so that the event isn't spammed for every tile on the grid. // ParentMapId is not able to be accessed on unbound grids, so we can't even call this function for unbound grids. - if (!MapManager.SuppressOnTileChanged) + if (!SuppressOnTileChanged) { var newTileRef = new TileRef(uid, gridTile, newTile); - _mapInternal.RaiseOnTileChanged((uid, grid), newTileRef, oldTile, mapChunk.Indices); + RaiseOnTileChanged((uid, grid), newTileRef, oldTile, mapChunk.Indices); } if (shapeChanged && !mapChunk.SuppressCollisionRegeneration) @@ -1643,6 +1703,18 @@ private void OnTileModified(EntityUid uid, MapGridComponent grid, MapChunk mapCh } } + /// + /// Raises on the provided grid unless is set. + /// + internal void RaiseOnTileChanged(Entity entity, TileRef tileRef, Tile oldTile, Vector2i chunk) + { + if (SuppressOnTileChanged) + return; + + var ev = new TileChangedEvent(entity, tileRef, oldTile, chunk); + EntityManager.EventBus.RaiseLocalEvent(entity.Owner, ref ev, true); + } + /// /// Iterates the local tiles of the specified data. /// @@ -1738,3 +1810,12 @@ public bool MoveNext(out TileRef tile) } } } + +/// +/// Additional parameters used when creating a new grid entity. +/// +/// The number of tiles long/wide the grids chunks should be. +public record struct GridCreateOptions(ushort ChunkSize) +{ + public readonly static GridCreateOptions Default = new(ChunkSize: 16); +} diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs index 2f99eb9c7ae..94486e8077e 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs @@ -8,6 +8,7 @@ using Robust.Shared.Maths; using Robust.Shared.Network; using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision; using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -22,6 +23,7 @@ public abstract partial class SharedMapSystem : EntitySystem [Dependency] private ITileDefinitionManager _tileMan = default!; [Dependency] private IGameTiming _timing = default!; [Dependency] protected IMapManager MapManager = default!; + [Dependency] private IManifoldManager _manifolds = default!; [Dependency] private IMapManagerInternal _mapInternal = default!; [Dependency] private INetManager _netManager = default!; [Dependency] private FixtureSystem _fixtures = default!; @@ -34,9 +36,18 @@ public abstract partial class SharedMapSystem : EntitySystem private EntityQuery _gridQuery; private EntityQuery _metaQuery; private EntityQuery _xformQuery; + [Dependency] EntityQuery _gridTreeQuery; internal Dictionary Maps { get; } = new(); + /// + /// If set, this prevents the from being raised when modifying grids. + /// + /// + /// Useful if you want to create a new grid, delete an existing grid, or bulk-modify tiles and don't want to spam ten billion individual tile-changed events. + /// + internal bool SuppressOnTileChanged { get; set; } + /// /// This hashset is used to try prevent MapId re-use. This is mainly for auto-assigned map ids. /// Loading a map with a specific id (e.g., the various mapping commands) may still result in an id being diff --git a/Robust.Shared/Map/Commands/MapPausingCommands.cs b/Robust.Shared/Map/Commands/MapPausingCommands.cs new file mode 100644 index 00000000000..2b74ac3c15a --- /dev/null +++ b/Robust.Shared/Map/Commands/MapPausingCommands.cs @@ -0,0 +1,90 @@ +using System.Globalization; +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Robust.Shared.Map.Commands; + +/// +/// Pauses a given map, halting all entity processing on it. +/// +public sealed partial class PauseMapCommand : LocalizedEntityCommands +{ + [Dependency] SharedMapSystem _mapSystem = default!; + public override string Command => "pausemap"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError("Need to supply a valid MapId"); + return; + } + + var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); + + if (!_mapSystem.MapExists(mapId)) + { + shell.WriteError("That map does not exist."); + return; + } + + _mapSystem.SetPaused(mapId, true); + } +} + +/// +/// Unpauses a given map, resuming all entity processing on it. +/// +public sealed partial class UnpauseMapCommand : LocalizedEntityCommands +{ + [Dependency] SharedMapSystem _mapSystem = default!; + public override string Command => "unpausemap"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError("Need to supply a valid MapId"); + return; + } + + var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); + + if (!_mapSystem.MapExists(mapId)) + { + shell.WriteError("That map does not exist."); + return; + } + + _mapSystem.SetPaused(mapId, false); + } +} + +/// +/// Checks whether a given map is currently paused. +/// +public sealed partial class QueryMapPausedCommand : LocalizedEntityCommands +{ + [Dependency] SharedMapSystem _mapSystem = default!; + public override string Command => "querymappaused"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError("Need to supply a valid MapId"); + return; + } + + var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); + + if (!_mapSystem.MapExists(mapId)) + { + shell.WriteError("That map does not exist."); + return; + } + + shell.WriteLine(_mapSystem.IsPaused(mapId).ToString()); + } +} diff --git a/Robust.Shared/Map/IMapManager.cs b/Robust.Shared/Map/IMapManager.cs index d0aaea4a5a4..fad26a165af 100644 --- a/Robust.Shared/Map/IMapManager.cs +++ b/Robust.Shared/Map/IMapManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Numerics; -using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Maths; @@ -11,23 +10,20 @@ namespace Robust.Shared.Map { - public delegate bool GridCallback(EntityUid uid, MapGridComponent grid); - - public delegate bool GridCallback(EntityUid uid, MapGridComponent grid, ref TState state); - /// /// This manages all the grids and maps in the world. Largely superseded by . /// [NotContentImplementable] public interface IMapManager { - public const bool Approximate = false; - public const bool IncludeMap = true; + public const bool Approximate = SharedMapSystem.Approximate; + public const bool IncludeMap = SharedMapSystem.IncludeMap; /// /// Should the OnTileChanged event be suppressed? This is useful for initially loading the map /// so that you don't spam an event for each of the million station tiles. /// + [Obsolete("use SharedMapSystem")] bool SuppressOnTileChanged { get; set; } /// @@ -70,41 +66,56 @@ public interface IMapManager void DeleteMap(MapId mapId); // ReSharper disable once MethodOverloadWithOptionalParameter + [Obsolete("Use MapSystem.CreateGridEntity(...).Comp")] MapGridComponent CreateGrid(MapId currentMapId, ushort chunkSize = 16); + [Obsolete("Use MapSystem.CreateGridEntity(...).Comp")] MapGridComponent CreateGrid(MapId currentMapId, in GridCreateOptions options); + [Obsolete("Use MapSystem.CreateGridEntity(...).Comp")] MapGridComponent CreateGrid(MapId currentMapId); + [Obsolete("Use MapSystem")] Entity CreateGridEntity(MapId currentMapId, GridCreateOptions? options = null); + [Obsolete("Use MapSystem")] Entity CreateGridEntity(EntityUid map, GridCreateOptions? options = null); + [Obsolete("Use MapSystem")] IEnumerable GetAllMapGrids(MapId mapId); + [Obsolete("Use MapSystem")] IEnumerable> GetAllGrids(MapId mapId); #region MapId + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, T shape, Transform transform, ref List> grids, bool approx = Approximate, bool includeMap = IncludeMap) where T : IPhysShape; + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, T shape, Transform transform, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap) where T : IPhysShape; + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, Box2 worldAABB, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, Box2 worldAABB, ref TState state, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, Box2 worldAABB, ref List> grids, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, ref TState state, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, ref List> grids, bool approx = Approximate, bool includeMap = IncludeMap); @@ -112,38 +123,48 @@ public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, ref List #region MapEnt + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, T shape, Transform transform, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap) where T : IPhysShape; + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, T shape, Transform transform, ref TState state, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap) where T : IPhysShape; /// /// Returns true if any grids overlap the specified shapes. /// + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, List shapes, Transform transform, ref List> entities, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, T shape, Transform transform, ref List> grids, bool approx = Approximate, bool includeMap = IncludeMap) where T : IPhysShape; + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, ref TState state, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, ref List> grids, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, ref TState state, GridCallback callback, bool approx = Approximate, bool includeMap = IncludeMap); + [Obsolete("Use MapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, ref List> grids, bool approx = Approximate, bool includeMap = IncludeMap); @@ -152,6 +173,7 @@ public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, #region TryFindGridAt + [Obsolete("Use MapSystem")] public bool TryFindGridAt( EntityUid mapEnt, Vector2 worldPos, @@ -161,12 +183,14 @@ public bool TryFindGridAt( /// /// Attempts to find the map grid under the map location. /// + [Obsolete("Use MapSystem")] public bool TryFindGridAt(MapId mapId, Vector2 worldPos, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid); /// /// Attempts to find the map grid under the map location. /// + [Obsolete("Use MapSystem")] public bool TryFindGridAt(MapCoordinates mapCoordinates, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid); @@ -231,16 +255,5 @@ public IEnumerable FindGridsIntersecting(MapId mapId, Box2Rota [Obsolete("Use MapSystem")] bool IsMapInitialized(MapId mapId); - - } - - public struct GridCreateOptions - { - public static readonly GridCreateOptions Default = new() - { - ChunkSize = 16 - }; - - public ushort ChunkSize; } } diff --git a/Robust.Shared/Map/IMapManagerInternal.cs b/Robust.Shared/Map/IMapManagerInternal.cs index e6a5b5b3637..c2b19a20f21 100644 --- a/Robust.Shared/Map/IMapManagerInternal.cs +++ b/Robust.Shared/Map/IMapManagerInternal.cs @@ -1,3 +1,4 @@ +using System; using Robust.Shared.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Maths; @@ -5,6 +6,7 @@ namespace Robust.Shared.Map { /// + [Obsolete] internal interface IMapManagerInternal : IMapManager { /// @@ -12,6 +14,7 @@ internal interface IMapManagerInternal : IMapManager /// /// A reference to the new tile. /// The old tile that got replaced. + [Obsolete("use SharedMapSystem")] void RaiseOnTileChanged(Entity entity, TileRef tileRef, Tile oldTile, Vector2i chunk); } } diff --git a/Robust.Shared/Map/MapManager.GridCollection.cs b/Robust.Shared/Map/MapManager.GridCollection.cs index cf23be33c1c..75502418251 100644 --- a/Robust.Shared/Map/MapManager.GridCollection.cs +++ b/Robust.Shared/Map/MapManager.GridCollection.cs @@ -1,42 +1,42 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Utility; -// All the obsolete warnings about GridId are probably useless here. -#pragma warning disable CS0618 - namespace Robust.Shared.Map; internal partial class MapManager { // ReSharper disable once MethodOverloadWithOptionalParameter + [Obsolete("use SharedMapSystem.CreateGridEntity(...).Comp")] public MapGridComponent CreateGrid(MapId currentMapId, ushort chunkSize = 16) { - return CreateGrid(GetMapEntityIdOrThrow(currentMapId), chunkSize, default); + return CreateGridEntity(currentMapId, options: GridCreateOptions.Default with { ChunkSize = chunkSize }).Comp; } + [Obsolete("use SharedMapSystem.CreateGridEntity(...).Comp")] public MapGridComponent CreateGrid(MapId currentMapId, in GridCreateOptions options) { - return CreateGrid(GetMapEntityIdOrThrow(currentMapId), options.ChunkSize, default); + return CreateGridEntity(currentMapId, options: options).Comp; } + [Obsolete("use SharedMapSystem.CreateGridEntity(...).Comp")] public MapGridComponent CreateGrid(MapId currentMapId) { - return CreateGrid(currentMapId, GridCreateOptions.Default); + return CreateGridEntity(currentMapId, options: GridCreateOptions.Default).Comp; } + [Obsolete("use SharedMapSystem.CreateGridEntity")] public Entity CreateGridEntity(MapId currentMapId, GridCreateOptions? options = null) { - return CreateGridEntity(GetMapEntityIdOrThrow(currentMapId), options); + return MapSystem.CreateGridEntity(currentMapId, options: options); } + [Obsolete("use SharedMapSystem.CreateGridEntity")] public Entity CreateGridEntity(EntityUid map, GridCreateOptions? options = null) { - options ??= GridCreateOptions.Default; - return CreateGrid(map, options.Value.ChunkSize, default); + return MapSystem.CreateGridEntity(map, options: options); } [Obsolete("Use HasComponent(uid)")] @@ -45,28 +45,19 @@ public bool IsGrid(EntityUid uid) return EntityManager.HasComponent(uid); } + [Obsolete("use SharedMapSystem.GetAllMapGrids")] public IEnumerable GetAllMapGrids(MapId mapId) { - var query = EntityManager.AllEntityQueryEnumerator(); - while (query.MoveNext(out var grid, out var xform)) - { - if (xform.MapID == mapId) - yield return grid; - } + return MapSystem.GetAllMapGrids(mapId); } + [Obsolete("use SharedMapSystem.GetAllGrids")] public IEnumerable> GetAllGrids(MapId mapId) { - var query = EntityManager.AllEntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var grid, out var xform)) - { - if (xform.MapID != mapId) - continue; - - yield return (uid, grid); - } + return MapSystem.GetAllGrids(mapId); } + [Obsolete("just delete the grid entity")] public virtual void DeleteGrid(EntityUid euid) { // Possible the grid was already deleted / is invalid @@ -89,44 +80,21 @@ public virtual void DeleteGrid(EntityUid euid) } /// - public bool SuppressOnTileChanged { get; set; } + [Obsolete("use SharedMapSystem.SuppressOnTileChanged")] + public bool SuppressOnTileChanged + { + get => MapSystem.SuppressOnTileChanged; + set { MapSystem.SuppressOnTileChanged = value; } + } /// /// Raises the OnTileChanged event. /// /// A reference to the new tile. /// The old tile that got replaced. + [Obsolete("use SharedMapSystem.RaiseOnTileChanged")] void IMapManagerInternal.RaiseOnTileChanged(Entity entity, TileRef tileRef, Tile oldTile, Vector2i chunk) { - if (SuppressOnTileChanged) - return; - - var ev = new TileChangedEvent(entity, tileRef, oldTile, chunk); - EntityManager.EventBus.RaiseLocalEvent(entity.Owner, ref ev, true); - } - - protected Entity CreateGrid(EntityUid map, ushort chunkSize, EntityUid forcedGridEuid) - { - var gridEnt = EntityManager.CreateEntityUninitialized(null, forcedGridEuid); - - var grid = EntityManager.AddComponent(gridEnt); - grid.ChunkSize = chunkSize; - - _sawmill.Debug($"Binding new grid {gridEnt}"); - - //TODO: This is a hack to get TransformComponent.MapId working before entity states - //are applied. After they are applied the parent may be different, but the MapId will - //be the same. This causes TransformComponent.ParentUid of a grid to be unsafe to - //use in transform states anytime before the state parent is properly set. - EntityManager.GetComponent(gridEnt).AttachParent(map); - - var meta = EntityManager.GetComponent(gridEnt); - EntityManager.System().SetEntityName(gridEnt, $"grid", meta); - EntityManager.InitializeComponents(gridEnt, meta); - EntityManager.StartComponents(gridEnt); - // Note that this does not actually map-initialize the grid entity, even if the map its being spawn on has already been initialized. - // I don't know whether that is intentional or not. - - return (gridEnt, grid); + MapSystem.RaiseOnTileChanged(entity, tileRef, oldTile, chunk); } } diff --git a/Robust.Shared/Map/MapManager.MapCollection.cs b/Robust.Shared/Map/MapManager.MapCollection.cs index e8a5f44e181..3810652f9a4 100644 --- a/Robust.Shared/Map/MapManager.MapCollection.cs +++ b/Robust.Shared/Map/MapManager.MapCollection.cs @@ -30,7 +30,7 @@ internal partial class MapManager /// public virtual void DeleteMap(MapId mapId) { - _mapSystem.DeleteMap(mapId); + MapSystem.DeleteMap(mapId); } /// @@ -38,24 +38,24 @@ public MapId CreateMap(MapId? mapId = null) { if (mapId != null) { - _mapSystem.CreateMap(mapId.Value); + MapSystem.CreateMap(mapId.Value); return mapId.Value; } - _mapSystem.CreateMap(out var map); + MapSystem.CreateMap(out var map); return map; } /// public bool MapExists([NotNullWhen(true)] MapId? mapId) { - return _mapSystem.MapExists(mapId); + return MapSystem.MapExists(mapId); } /// public EntityUid GetMapEntityId(MapId mapId) { - return _mapSystem.GetMapOrInvalid(mapId); + return MapSystem.GetMapOrInvalid(mapId); } /// @@ -63,18 +63,18 @@ public EntityUid GetMapEntityId(MapId mapId) /// public EntityUid GetMapEntityIdOrThrow(MapId mapId) { - return _mapSystem.GetMap(mapId); + return MapSystem.GetMap(mapId); } public bool TryGetMap([NotNullWhen(true)] MapId? mapId, [NotNullWhen(true)] out EntityUid? uid) { - return _mapSystem.TryGetMap(mapId, out uid); + return MapSystem.TryGetMap(mapId, out uid); } /// public IEnumerable GetAllMapIds() { - return _mapSystem.GetAllMapIds(); + return MapSystem.GetAllMapIds(); } /// diff --git a/Robust.Shared/Map/MapManager.Pause.cs b/Robust.Shared/Map/MapManager.Pause.cs index d4fb8f58976..35415537c4a 100644 --- a/Robust.Shared/Map/MapManager.Pause.cs +++ b/Robust.Shared/Map/MapManager.Pause.cs @@ -7,100 +7,34 @@ internal partial class MapManager { public void SetMapPaused(MapId mapId, bool paused) { - _mapSystem.SetPaused(mapId, paused); + MapSystem.SetPaused(mapId, paused); } public void SetMapPaused(EntityUid uid, bool paused) { - _mapSystem.SetPaused(uid, paused); + MapSystem.SetPaused(uid, paused); } public void DoMapInitialize(MapId mapId) { - _mapSystem.InitializeMap(mapId); + MapSystem.InitializeMap(mapId); } public bool IsMapInitialized(MapId mapId) { - return _mapSystem.IsInitialized(mapId); + return MapSystem.IsInitialized(mapId); } /// public bool IsMapPaused(MapId mapId) { - return _mapSystem.IsPaused(mapId); + return MapSystem.IsPaused(mapId); } /// public bool IsMapPaused(EntityUid uid) { - return _mapSystem.IsPaused(uid); - } - - /// - /// Initializes the map pausing system. - /// - private void InitializeMapPausing() - { - _conhost.RegisterCommand("pausemap", - "Pauses a map, pausing all simulation processing on it.", - "pausemap ", - (shell, _, args) => - { - if (args.Length != 1) - { - shell.WriteError("Need to supply a valid MapId"); - return; - } - - var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); - - if (!MapExists(mapId)) - { - shell.WriteError("That map does not exist."); - return; - } - - SetMapPaused(mapId, true); - }); - - _conhost.RegisterCommand("querymappaused", - "Check whether a map is paused or not.", - "querymappaused ", - (shell, _, args) => - { - var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); - - if (!MapExists(mapId)) - { - shell.WriteError("That map does not exist."); - return; - } - - shell.WriteLine(_mapSystem.IsPaused(mapId).ToString()); - }); - - _conhost.RegisterCommand("unpausemap", - "unpauses a map, resuming all simulation processing on it.", - "Usage: unpausemap ", - (shell, _, args) => - { - if (args.Length != 1) - { - shell.WriteLine("Need to supply a valid MapId"); - return; - } - - var mapId = new MapId(int.Parse(args[0], CultureInfo.InvariantCulture)); - - if (!MapExists(mapId)) - { - shell.WriteLine("That map does not exist."); - return; - } - - SetMapPaused(mapId, false); - }); + return MapSystem.IsPaused(uid); } } } diff --git a/Robust.Shared/Map/MapManager.Queries.cs b/Robust.Shared/Map/MapManager.Queries.cs index f37a545525f..8916fa76d84 100644 --- a/Robust.Shared/Map/MapManager.Queries.cs +++ b/Robust.Shared/Map/MapManager.Queries.cs @@ -4,102 +4,74 @@ using System.Numerics; using Robust.Shared.GameObjects; using Robust.Shared.Map.Components; -using Robust.Shared.Map.Enumerators; using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; -using Robust.Shared.Physics.Shapes; namespace Robust.Shared.Map; internal partial class MapManager { - private bool IsIntersecting( - ChunkEnumerator enumerator, - T shape, - Transform shapeTransform, - Entity grid) where T : IPhysShape - { - var gridTransform = _physics.GetPhysicsTransform(grid); - - while (enumerator.MoveNext(out var chunk)) - { - foreach (var id in chunk.Fixtures) - { - var fixture = grid.Comp.Fixtures[id]; - - for (var j = 0; j < fixture.Shape.ChildCount; j++) - { - if (_manifolds.TestOverlap(shape, 0, fixture.Shape, j, shapeTransform, gridTransform)) - { - return true; - } - } - } - } - - return false; - } - - #region MapId + #region MapId [Obsolete] + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, T shape, Transform transform, ref List> grids, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) where T : IPhysShape { - if (_mapSystem.TryGetMap(mapId, out var mapEnt)) - FindGridsIntersecting(mapEnt.Value, shape, transform, ref grids, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, shape, transform, ref grids, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, T shape, Transform transform, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) where T : IPhysShape { - if (_mapSystem.TryGetMap(mapId, out var mapEnt)) - FindGridsIntersecting(mapEnt.Value, shape, transform, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, shape, transform, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, Box2 worldAABB, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - if (_mapSystem.TryGetMap(mapId, out var mapEnt)) - FindGridsIntersecting(mapEnt.Value, worldAABB, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, worldAABB, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, Box2 worldAABB, ref TState state, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - if (_mapSystem.TryGetMap(mapId, out var map)) - FindGridsIntersecting(map.Value, worldAABB, ref state, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, worldAABB, ref state, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, Box2 worldAABB, ref List> grids, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - if (_mapSystem.TryGetMap(mapId, out var map)) - FindGridsIntersecting(map.Value, worldAABB, ref grids, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, worldAABB, ref grids, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - if (_mapSystem.TryGetMap(mapId, out var mapEnt)) - FindGridsIntersecting(mapEnt.Value, worldBounds, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, worldBounds, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, ref TState state, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - if (_mapSystem.TryGetMap(mapId, out var mapEnt)) - FindGridsIntersecting(mapEnt.Value, worldBounds, ref state, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, worldBounds, ref state, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, ref List> grids, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - if (_mapSystem.TryGetMap(mapId, out var mapEnt)) - FindGridsIntersecting(mapEnt.Value, worldBounds, ref grids, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapId, worldBounds, ref grids, approx: approx, includeMap: includeMap); } #endregion - #region MapEnt + #region MapEnt [Obsolete] + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting( EntityUid mapEnt, T shape, @@ -108,19 +80,10 @@ public void FindGridsIntersecting( bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) where T : IPhysShape { - FindGridsIntersecting(mapEnt, shape, shape.ComputeAABB(transform, 0), transform, callback, approx: approx, includeMap: includeMap); - } - - private void FindGridsIntersecting(EntityUid mapEnt, T shape, Box2 worldAABB, Transform transform, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) where T : IPhysShape - { - // This is here so we don't double up on code. - var state = callback; - - FindGridsIntersecting(mapEnt, shape, worldAABB, transform, ref state, - static (EntityUid uid, MapGridComponent grid, ref GridCallback state) => state.Invoke(uid, grid), - approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, shape, transform, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting( EntityUid mapEnt, T shape, @@ -130,236 +93,103 @@ public void FindGridsIntersecting( bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) where T : IPhysShape { - FindGridsIntersecting(mapEnt, shape, shape.ComputeAABB(transform, 0), transform, ref state, callback, approx: approx, includeMap: includeMap); - } - - private void FindGridsIntersecting( - EntityUid mapEnt, - T shape, - Box2 worldAABB, - Transform transform, - ref TState state, - GridCallback callback, - bool approx = IMapManager.Approximate, - bool includeMap = IMapManager.IncludeMap) where T : IPhysShape - { - if (!_gridTreeQuery.TryGetComponent(mapEnt, out var gridTree)) - return; - - if (includeMap && _gridQuery.TryGetComponent(mapEnt, out var mapGrid)) - { - callback(mapEnt, mapGrid, ref state); - } - - var gridState = new GridQueryState( - callback, - state, - worldAABB, - shape, - transform, - gridTree.Tree, - _mapSystem, - this, - _transformSystem, - approx); - - gridTree.Tree.Query(ref gridState, static (ref GridQueryState state, DynamicTree.Proxy proxy) => - { - // Even for approximate we'll check if any chunks roughly overlap. - var data = state.Tree.GetUserData(proxy); - var gridInvMatrix = state.TransformSystem.GetInvWorldMatrix(data.Uid); - var localAABB = gridInvMatrix.TransformBox(state.WorldAABB); - - var overlappingChunks = state.MapSystem.GetLocalMapChunks(data.Uid, data.Grid, localAABB); - - if (state.Approximate) - { - if (!overlappingChunks.MoveNext(out _)) - return true; - } - else if (!state.MapManager.IsIntersecting(overlappingChunks, state.Shape, state.Transform, (data.Uid, data.Fixtures))) - { - return true; - } - - var callbackState = state.State; - var result = state.Callback(data.Uid, data.Grid, ref callbackState); - state.State = callbackState; - - return result; - }, worldAABB); - - // By-ref things - state = gridState.State; + MapSystem.FindGridsIntersecting(mapEnt, shape, transform, ref state, callback, approx: approx, includeMap: includeMap); } /// /// Returns true if any grids overlap the specified shapes. /// + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, List shapes, Transform transform, ref List> entities, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - foreach (var shape in shapes) - { - FindGridsIntersecting(mapEnt, shape, shape.ComputeAABB(transform, 0), transform, ref entities, approx: approx, includeMap: includeMap); - } + MapSystem.FindGridsIntersecting(mapEnt, shapes, transform, ref entities, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, T shape, Transform transform, ref List> grids, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) where T : IPhysShape { - FindGridsIntersecting(mapEnt, shape, shape.ComputeAABB(transform, 0), transform, ref grids, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, shape, transform, ref grids, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, T shape, Box2 worldAABB, Transform transform, ref List> grids, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) where T : IPhysShape { - var state = grids; - - FindGridsIntersecting(mapEnt, shape, worldAABB, transform, ref state, - static (EntityUid uid, MapGridComponent grid, ref List> list) => - { - list.Add((uid, grid)); - return true; - }, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, shape, worldAABB, transform, ref grids, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - var polygon = new SlimPolygon(worldAABB); - FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, worldAABB, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, ref TState state, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - var polygon = new SlimPolygon(worldAABB); - FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, ref state, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, worldAABB, ref state, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, ref List> grids, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - var polygon = new SlimPolygon(worldAABB); - FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, ref grids, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, worldAABB, ref grids, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - var polygon = new SlimPolygon(worldBounds); - FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, worldBounds, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, ref TState state, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - var polygon = new SlimPolygon(worldBounds); - FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, ref state, callback, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, worldBounds, ref state, callback, approx: approx, includeMap: includeMap); } + [Obsolete("use SharedMapSystem")] public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, ref List> grids, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap) { - var polygon = new SlimPolygon(worldBounds); - FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, ref grids, approx: approx, includeMap: includeMap); + MapSystem.FindGridsIntersecting(mapEnt, worldBounds, ref grids, approx: approx, includeMap: includeMap); } #endregion #region TryFindGridAt + [Obsolete("use SharedMapSystem")] public bool TryFindGridAt( EntityUid mapEnt, Vector2 worldPos, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid) { - var rangeVec = new Vector2(0.2f, 0.2f); - - // Need to enlarge the AABB by at least the grid shrinkage size. - var aabb = new Box2(worldPos - rangeVec, worldPos + rangeVec); - - uid = EntityUid.Invalid; - grid = null; - var state = (uid, grid, worldPos, _mapSystem, _transformSystem); - - FindGridsIntersecting(mapEnt, aabb, ref state, static (EntityUid iUid, MapGridComponent iGrid, ref ( - EntityUid uid, - MapGridComponent? grid, - Vector2 worldPos, - SharedMapSystem mapSystem, - SharedTransformSystem xformSystem) tuple) => - { - // Turn the worldPos into a localPos and work out the relevant chunk we need to check - // This is much faster than iterating over every chunk individually. - // (though now we need some extra calcs up front). - - // Doesn't use WorldBounds because it's just an AABB. - var matrix = tuple.xformSystem.GetInvWorldMatrix(iUid); - var localPos = Vector2.Transform(tuple.worldPos, matrix); - - // NOTE: - // If you change this to use fixtures instead (i.e. if you want half-tiles) then you need to make sure - // you account for the fact that fixtures are shrunk slightly! - var chunkIndices = SharedMapSystem.GetChunkIndices(localPos, iGrid.ChunkSize); - - if (!iGrid.Chunks.TryGetValue(chunkIndices, out var chunk)) - return true; - - var chunkRelative = SharedMapSystem.GetChunkRelative(localPos, iGrid.ChunkSize); - var chunkTile = chunk.GetTile(chunkRelative); - - if (chunkTile.IsEmpty) - return true; - - tuple.uid = iUid; - tuple.grid = iGrid; - return false; - }, approx: true, includeMap: false); - - if (state.grid == null && _gridQuery.TryGetComponent(mapEnt, out var mapGrid)) - { - uid = mapEnt; - grid = mapGrid; - return true; - } - - uid = state.uid; - grid = state.grid; - return grid != null; + return MapSystem.TryFindGridAt(mapEnt, worldPos, out uid, out grid); } /// /// Attempts to find the map grid under the map location. /// + [Obsolete("use SharedMapSystem")] public bool TryFindGridAt(MapId mapId, Vector2 worldPos, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid) { - if (_mapSystem.TryGetMap(mapId, out var map)) - return TryFindGridAt(map.Value, worldPos, out uid, out grid); - - uid = default; - grid = null; - return false; + return MapSystem.TryFindGridAt(mapId, worldPos, out uid, out grid); } /// /// Attempts to find the map grid under the map location. /// + [Obsolete("use SharedMapSystem")] public bool TryFindGridAt(MapCoordinates mapCoordinates, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid) { - return TryFindGridAt(mapCoordinates.MapId, mapCoordinates.Position, out uid, out grid); + return MapSystem.TryFindGridAt(mapCoordinates, out uid, out grid); } #endregion - - private record struct GridQueryState( - GridCallback Callback, - TState State, - Box2 WorldAABB, - T Shape, - Transform Transform, - B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree, - SharedMapSystem MapSystem, - MapManager MapManager, - SharedTransformSystem TransformSystem, - bool Approximate); } diff --git a/Robust.Shared/Map/MapManager.cs b/Robust.Shared/Map/MapManager.cs index c5e853c8b63..3bfd5f3e3ce 100644 --- a/Robust.Shared/Map/MapManager.cs +++ b/Robust.Shared/Map/MapManager.cs @@ -1,11 +1,7 @@ -using Robust.Shared.Console; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Collision; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; namespace Robust.Shared.Map; @@ -13,36 +9,23 @@ namespace Robust.Shared.Map; [Virtual] internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber { - [Dependency] public IGameTiming GameTiming = default!; [Dependency] public IEntityManager EntityManager = default!; - [Dependency] private IManifoldManager _manifolds = default!; [Dependency] private ILogManager _logManager = default!; - [Dependency] private IConsoleHost _conhost = default!; private ISawmill _sawmill = default!; - private SharedMapSystem _mapSystem = default!; - private SharedPhysicsSystem _physics = default!; - private SharedTransformSystem _transformSystem = default!; - - private EntityQuery _gridTreeQuery; - private EntityQuery _gridQuery; + protected SharedMapSystem MapSystem = default!; /// public void Initialize() { - _gridTreeQuery = EntityManager.GetEntityQuery(); - _gridQuery = EntityManager.GetEntityQuery(); - InitializeMapPausing(); _sawmill = _logManager.GetSawmill("system.map"); } /// public void Startup() { - _physics = EntityManager.System(); - _transformSystem = EntityManager.System(); - _mapSystem = EntityManager.System(); + MapSystem = EntityManager.System(); _sawmill.Debug("Starting..."); } diff --git a/Robust.Shared/Map/NetworkedMapManager.cs b/Robust.Shared/Map/NetworkedMapManager.cs index 683916bc46d..fdfbfadf781 100644 --- a/Robust.Shared/Map/NetworkedMapManager.cs +++ b/Robust.Shared/Map/NetworkedMapManager.cs @@ -1,23 +1,21 @@ -using Robust.Shared.Map.Components; +using System; using Robust.Shared.Timing; namespace Robust.Shared.Map; +[Obsolete] internal interface INetworkedMapManager : IMapManagerInternal { + [Obsolete] void CullDeletionHistory(GameTick upToTick); } +[Obsolete] internal sealed class NetworkedMapManager : MapManager, INetworkedMapManager { + [Obsolete] public void CullDeletionHistory(GameTick upToTick) { - var query = EntityManager.AllEntityQueryEnumerator(); - - while (query.MoveNext(out var grid)) - { - var chunks = grid.ChunkDeletionHistory; - chunks.RemoveAll(t => t.tick < upToTick); - } + MapSystem.CullDeletionHistory(upToTick); } }