Files
RobustToolbox/Robust.Shared/Map/MapManager.cs
Pieter-Jan Briers f4b0b69cbb Map Init & Map Loading improvements. (#801)
* MapInit v1, so people can criticize my code.

* Map init v1.

* Improve LocalPlayer to fix aghosting.

* Fix map saving.

* Map command improvements:

Implement loadbp
Made certain commands aware of uninitialized maps.

* Adds IMapManager.GetAllGrids()

* Add lsgrid and lsmap commands.

* MetaData component serialization fixes.

Serialize name and description default as null.
Don't serialize prototype.

* Explicit UID indices in map files.

* Update map format doc again.
2019-04-29 12:50:28 +02:00

331 lines
9.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Shared.Map
{
/// <inheritdoc cref="IMapManager"/>
public partial class MapManager : IMapManagerInternal, IPostInjectInit
{
[Dependency] private readonly IGameTiming _gameTiming;
public IGameTiming GameTiming => _gameTiming;
/// <inheritdoc />
public IMap DefaultMap => GetMap(MapId.Nullspace);
/// <inheritdoc />
public event EventHandler<TileChangedEventArgs> TileChanged;
public event GridEventHandler OnGridCreated;
public event GridEventHandler OnGridRemoved;
/// <summary>
/// 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.
/// </summary>
/// <inheritdoc />
public event EventHandler<GridChangedEventArgs> GridChanged;
/// <inheritdoc />
public event EventHandler<MapEventArgs> MapCreated;
/// <inheritdoc />
public event EventHandler<MapEventArgs> MapDestroyed;
/// <inheritdoc />
public bool SuppressOnTileChanged { get; set; }
private MapId HighestMapID = MapId.Nullspace;
private GridId HighestGridID = GridId.Nullspace;
/// <summary>
/// Holds an indexed collection of map grids.
/// </summary>
private readonly Dictionary<MapId, Map> _maps = new Dictionary<MapId, Map>();
private readonly Dictionary<GridId, MapGrid> _grids = new Dictionary<GridId, MapGrid>();
private readonly List<(GameTick tick, GridId gridId)> _gridDeletionHistory = new List<(GameTick, GridId)>();
private readonly List<(GameTick tick, MapId mapId)> _mapDeletionHistory = new List<(GameTick, MapId)>();
public void PostInject()
{
CreateMap(MapId.Nullspace, GridId.Nullspace);
}
/// <inheritdoc />
public void Initialize()
{
// So uh I removed the contents from this but I'm too lazy to remove the Initialize method.
// Deal with it.
}
public void Startup()
{
// Ditto, removed contents but too lazy to remove method.
}
public void Shutdown()
{
foreach (var map in _maps.Keys.ToArray())
{
if (map != MapId.Nullspace)
{
DeleteMap(map);
}
}
DebugTools.Assert(_grids.Count == 1);
}
/// <summary>
/// Raises the OnTileChanged event.
/// </summary>
/// <param name="tileRef">A reference to the new tile.</param>
/// <param name="oldTile">The old tile that got replaced.</param>
public void RaiseOnTileChanged(TileRef tileRef, Tile oldTile)
{
if (SuppressOnTileChanged)
return;
TileChanged?.Invoke(this, new TileChangedEventArgs(tileRef, oldTile));
}
/// <inheritdoc />
public void DeleteMap(MapId mapID)
{
if (!_maps.TryGetValue(mapID, out var map))
{
throw new InvalidOperationException($"Attempted to delete nonexistant map '{mapID}'");
}
// grids are cached because Delete modifies collection
foreach (var grid in map.GetAllGrids().ToList())
{
DeleteGrid(grid.Index);
}
MapDestroyed?.Invoke(this, new MapEventArgs(_maps[mapID]));
_maps.Remove(mapID);
if (_netManager.IsClient)
return;
_mapDeletionHistory.Add((_gameTiming.CurTick, mapID));
}
/// <inheritdoc />
public IMap CreateMap(MapId? mapID = null, GridId? defaultGridID = null)
{
if (defaultGridID != null && GridExists(defaultGridID.Value))
{
throw new InvalidOperationException($"Grid '{defaultGridID}' already exists.");
}
MapId actualID;
if (mapID != null)
{
actualID = mapID.Value;
}
else
{
actualID = new MapId(HighestMapID.Value + 1);
}
if (MapExists(actualID))
{
throw new InvalidOperationException($"A map with ID {actualID} already exists");
}
if (HighestMapID.Value < actualID.Value)
{
HighestMapID = actualID;
}
var newMap = new Map(this, actualID);
_maps.Add(actualID, newMap);
MapCreated?.Invoke(this, new MapEventArgs(newMap));
newMap.DefaultGrid = CreateGrid(newMap.Index, defaultGridID);
return newMap;
}
/// <inheritdoc />
public IMap GetMap(MapId mapID)
{
return _maps[mapID];
}
/// <inheritdoc />
public bool MapExists(MapId mapID)
{
return _maps.ContainsKey(mapID);
}
/// <inheritdoc />
public bool TryGetMap(MapId mapID, out IMap map)
{
if (_maps.TryGetValue(mapID, out var mapinterface))
{
map = mapinterface;
return true;
}
map = null;
return false;
}
public IEnumerable<IMap> GetAllMaps()
{
return _maps.Values;
}
public IEnumerable<IMapGrid> GetAllGrids()
{
return _grids.Values;
}
public IMapGrid CreateGrid(MapId currentMapID, GridId? gridID = null, ushort chunkSize = 16, float snapSize = 1)
{
var map = _maps[currentMapID];
GridId actualID;
if (gridID != null)
{
actualID = gridID.Value;
}
else
{
actualID = new GridId(HighestGridID.Value + 1);
}
if (GridExists(actualID))
{
throw new InvalidOperationException($"A map with ID {actualID} already exists");
}
if (HighestGridID.Value < actualID.Value)
{
HighestGridID = actualID;
}
var grid = new MapGrid(this, actualID, chunkSize, snapSize, currentMapID);
_grids.Add(actualID, grid);
map.AddGrid(grid);
OnGridCreated?.Invoke(actualID);
return grid;
}
public IMapGrid GetGrid(GridId gridID)
{
return _grids[gridID];
}
public bool TryGetGrid(GridId gridId, out IMapGrid grid)
{
if (_grids.TryGetValue(gridId, out var gridinterface))
{
grid = gridinterface;
return true;
}
grid = null;
return false;
}
public bool GridExists(GridId gridID)
{
return _grids.ContainsKey(gridID);
}
public void DeleteGrid(GridId gridID)
{
var grid = _grids[gridID];
var map = (Map)grid.ParentMap;
grid.Dispose();
map.RemoveGrid(grid);
_grids.Remove(grid.Index);
OnGridRemoved?.Invoke(gridID);
if (_netManager.IsClient)
{
return;
}
_gridDeletionHistory.Add((_gameTiming.CurTick, gridID));
}
}
/// <summary>
/// Arguments for when a map is created or deleted locally ore remotely.
/// </summary>
public class MapEventArgs : EventArgs
{
/// <summary>
/// Map that is being modified.
/// </summary>
public IMap Map { get; }
/// <summary>
/// Creates a new instance of this class.
/// </summary>
public MapEventArgs(IMap map)
{
Map = map;
}
}
/// <summary>
/// Arguments for when a single tile on a grid is changed locally or remotely.
/// </summary>
public class TileChangedEventArgs : EventArgs
{
/// <summary>
/// New tile that replaced the old one.
/// </summary>
public TileRef NewTile { get; }
/// <summary>
/// Old tile that was replaced.
/// </summary>
public Tile OldTile { get; }
/// <summary>
/// Creates a new instance of this class.
/// </summary>
public TileChangedEventArgs(TileRef newTile, Tile oldTile)
{
NewTile = newTile;
OldTile = oldTile;
}
}
/// <summary>
/// Arguments for when a one or more tiles on a grid is changed at once.
/// </summary>
public class GridChangedEventArgs : EventArgs
{
/// <summary>
/// Grid being changed.
/// </summary>
public IMapGrid Grid { get; }
public IReadOnlyCollection<(MapIndices position, Tile tile)> Modified { get; }
/// <summary>
/// Creates a new instance of this class.
/// </summary>
public GridChangedEventArgs(IMapGrid grid, IReadOnlyCollection<(MapIndices position, Tile tile)> modified)
{
Grid = grid;
Modified = modified;
}
}
}