diff --git a/Robust.Client/Placement/PlacementManager.cs b/Robust.Client/Placement/PlacementManager.cs index 5429e6685..7c6eeb707 100644 --- a/Robust.Client/Placement/PlacementManager.cs +++ b/Robust.Client/Placement/PlacementManager.cs @@ -278,7 +278,7 @@ namespace Robust.Client.Placement } } - private void OnEntityAttached(object o, EventArgs eventArgs) + private void OnEntityAttached(EntityAttachedEventArgs eventArgs) { // player attached to a new entity, basically disable the editor Clear(); diff --git a/Robust.Client/Player/LocalPlayer.cs b/Robust.Client/Player/LocalPlayer.cs index 7a325f61c..ab2060249 100644 --- a/Robust.Client/Player/LocalPlayer.cs +++ b/Robust.Client/Player/LocalPlayer.cs @@ -26,12 +26,12 @@ namespace Robust.Client.Player /// /// An entity has been attached to the local player. /// - public event EventHandler EntityAttached; + public event Action EntityAttached; /// /// An entity has been detached from the local player. /// - public event EventHandler EntityDetached; + public event Action EntityDetached; /// /// Game entity that the local player is controlling. If this is null, the player @@ -93,7 +93,7 @@ namespace Robust.Client.Player var transform = ControlledEntity.Transform; transform.OnMove += OnPlayerMoved; - EntityAttached?.Invoke(this, EventArgs.Empty); + EntityAttached?.Invoke(new EntityAttachedEventArgs(entity)); entity.SendMessage(null, new PlayerAttachedMsg()); // notify ECS Systems @@ -105,6 +105,7 @@ namespace Robust.Client.Player /// public void DetachEntity() { + var previous = ControlledEntity; if (ControlledEntity != null && ControlledEntity.Initialized) { ControlledEntity.GetComponent().Current = false; @@ -116,9 +117,13 @@ namespace Robust.Client.Player // notify ECS Systems ControlledEntity.EntityManager.RaiseEvent(this, new PlayerAttachSysMessage(null)); } + ControlledEntity = null; - EntityDetached?.Invoke(this, EventArgs.Empty); + if (previous != null) + { + EntityDetached?.Invoke(new EntityDetachedEventArgs(previous)); + } } private void OnPlayerMoved(object sender, MoveEventArgs args) @@ -169,4 +174,24 @@ namespace Robust.Client.Player NewStatus = newStatus; } } + + public class EntityDetachedEventArgs : EventArgs + { + public EntityDetachedEventArgs(IEntity oldEntity) + { + OldEntity = oldEntity; + } + + public IEntity OldEntity { get; } + } + + public class EntityAttachedEventArgs : EventArgs + { + public EntityAttachedEventArgs(IEntity newEntity) + { + NewEntity = newEntity; + } + + public IEntity NewEntity { get; } + } } diff --git a/Robust.Server/Console/Commands/MapCommands.cs b/Robust.Server/Console/Commands/MapCommands.cs index 9a9f175a5..a6e6caa12 100644 --- a/Robust.Server/Console/Commands/MapCommands.cs +++ b/Robust.Server/Console/Commands/MapCommands.cs @@ -1,4 +1,6 @@ using System.Globalization; +using System.Linq; +using System.Text; using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Maps; using Robust.Server.Interfaces.Player; @@ -14,7 +16,7 @@ namespace Robust.Server.Console.Commands { public string Command => "addmap"; public string Description => "Adds a new empty map to the round. If the mapID already exists, this command does nothing."; - public string Help => "addmap "; + public string Help => "addmap [initialize]"; public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) { @@ -24,10 +26,15 @@ 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); + } shell.SendText(player, $"Map with ID {mapId} created."); return; } @@ -82,11 +89,38 @@ namespace Robust.Server.Console.Commands { public string Command => "loadbp"; public string Description => "Loads a blueprint from disk into the game."; - public string Help => "loadbp "; + public string Help => "loadbp "; public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) { - //TODO: Make me work after placement can create new grids. + if (args.Length < 2) + { + return; + } + + if (!int.TryParse(args[0], out var intMapId)) + { + return; + } + + var mapId = new MapId(intMapId); + + // no loading into null space + if (mapId == MapId.Nullspace) + { + shell.SendText(player, "Cannot load into nullspace."); + return; + } + + var mapManager = IoCManager.Resolve(); + if (!mapManager.TryGetMap(mapId, out var map)) + { + shell.SendText(player, "Target map does not exist."); + return; + } + + var mapLoader = IoCManager.Resolve(); + mapLoader.LoadBlueprint(map, args[1]); } } @@ -233,6 +267,7 @@ namespace Robust.Server.Console.Commands public string Command => "tpgrid"; public string Description => "Teleports a grid to a new location."; public string Help => "tpgrid []"; + public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) { if (args.Length < 3 || args.Length > 4) @@ -255,7 +290,89 @@ namespace Robust.Server.Console.Commands shell.SendText(player, "Grid was teleported."); } + } + } + internal sealed class RunMapInitCommand : IClientCommand + { + public string Command => "mapinit"; + public string Description => default; + public string Help => "mapinit "; + + public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) + { + if (args.Length != 1) + { + shell.SendText(player, "Wrong number of args."); + return; + } + + var mapManager = IoCManager.Resolve(); + var pauseManager = IoCManager.Resolve(); + + var arg = args[0]; + var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture)); + + if (!mapManager.TryGetMap(mapId, out var map)) + { + shell.SendText(player, "Map does not exist!"); + return; + } + + if (pauseManager.IsMapInitialized(map)) + { + shell.SendText(player, "Map is already initialized!"); + return; + } + + pauseManager.DoMapInitialize(map); + } + } + + internal sealed class ListMapsCommand : IClientCommand + { + public string Command => "lsmap"; + public string Description => default; + public string Help => "lsmap"; + + public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) + { + var mapManager = IoCManager.Resolve(); + var pauseManager = IoCManager.Resolve(); + + var msg = new StringBuilder(); + + foreach (var map in mapManager.GetAllMaps().OrderBy(map => map.Index.Value)) + { + msg.AppendFormat("{0}: default grid: {1}, init: {2}, paused: {3} , grids: {4}\n", + map.Index, map.DefaultGrid.Index, pauseManager.IsMapInitialized(map), + pauseManager.IsMapPaused(map), + string.Join(",", map.GetAllGrids().Select(grid => grid.Index))); + } + + shell.SendText(player, msg.ToString()); + } + } + + internal sealed class ListGridsCommand : IClientCommand + { + public string Command => "lsgrid"; + public string Description => default; + public string Help => "lsgrid"; + + public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) + { + var mapManager = IoCManager.Resolve(); + + var msg = new StringBuilder(); + + foreach (var grid in mapManager.GetAllGrids().OrderBy(grid => grid.Index.Value)) + { + msg.AppendFormat("{0}: map: {1}, default: {2}, pos: {3} \n", + grid.Index, grid.ParentMap.Index, grid.IsDefaultGrid, grid.WorldPosition); + } + + shell.SendText(player, msg.ToString()); } } } diff --git a/Robust.Server/GameObjects/ServerEntityManager.cs b/Robust.Server/GameObjects/ServerEntityManager.cs index 5873e3ef6..48ec25934 100644 --- a/Robust.Server/GameObjects/ServerEntityManager.cs +++ b/Robust.Server/GameObjects/ServerEntityManager.cs @@ -27,6 +27,7 @@ namespace Robust.Server.GameObjects { var newEnt = CreateEntity(protoName); InitializeAndStartEntity(newEnt); + newEnt.RunMapInit(); return newEnt; } @@ -39,6 +40,7 @@ namespace Robust.Server.GameObjects var result = CreateEntity(entityType); result.Transform.GridPosition = coordinates; InitializeAndStartEntity(result); + result.RunMapInit(); entity = result; return true; } @@ -60,6 +62,7 @@ namespace Robust.Server.GameObjects var entity = CreateEntity(entityType); entity.Transform.GridPosition = coordinates; InitializeAndStartEntity(entity); + entity.RunMapInit(); return entity; } diff --git a/Robust.Server/Interfaces/GameObjects/IMapInit.cs b/Robust.Server/Interfaces/GameObjects/IMapInit.cs new file mode 100644 index 000000000..3c04b9e9a --- /dev/null +++ b/Robust.Server/Interfaces/GameObjects/IMapInit.cs @@ -0,0 +1,27 @@ +using System.Linq; +using Robust.Server.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; + +namespace Robust.Server.Interfaces.GameObjects +{ + /// + /// Defines a component that has "map initialization" behavior. + /// Basically irreversible behavior that moves the map from "map editor" to playable, + /// like spawning preset objects. + /// + public interface IMapInit + { + void MapInit(); + } + + public static class MapInitExt + { + public static void RunMapInit(this IEntity entity) + { + foreach (var init in entity.GetAllComponents().ToList()) + { + init.MapInit(); + } + } + } +} diff --git a/Robust.Server/Interfaces/Timing/IPauseManager.cs b/Robust.Server/Interfaces/Timing/IPauseManager.cs index 73c399406..c7ab6ea0d 100644 --- a/Robust.Server/Interfaces/Timing/IPauseManager.cs +++ b/Robust.Server/Interfaces/Timing/IPauseManager.cs @@ -11,6 +11,15 @@ namespace Robust.Server.Interfaces.Timing void SetMapPaused(IMap map, bool paused); void SetMapPaused(MapId mapId, bool paused); + void DoMapInitialize(MapId mapId); + void DoMapInitialize(IMap map); + + void DoGridMapInitialize(GridId gridId); + void DoGridMapInitialize(IMapGrid grid); + + void AddUninitializedMap(MapId mapId); + void AddUninitializedMap(IMap map); + [Pure] bool IsMapPaused(IMap map); @@ -22,6 +31,12 @@ namespace Robust.Server.Interfaces.Timing [Pure] bool IsGridPaused(GridId gridId); + + [Pure] + bool IsMapInitialized(MapId mapId); + + [Pure] + bool IsMapInitialized(IMap map); } public static class PauseManagerExt diff --git a/Robust.Server/Maps/MapLoader.cs b/Robust.Server/Maps/MapLoader.cs index f39549f83..2e768ba72 100644 --- a/Robust.Server/Maps/MapLoader.cs +++ b/Robust.Server/Maps/MapLoader.cs @@ -15,6 +15,7 @@ using Robust.Shared.GameObjects; using System.Globalization; using Robust.Shared.Interfaces.GameObjects; using System.Linq; +using Robust.Server.Interfaces.Timing; namespace Robust.Server.Maps { @@ -37,12 +38,14 @@ namespace Robust.Server.Maps [Dependency] private readonly IServerEntityManagerInternal _serverEntityManager; + [Dependency] private readonly IPauseManager _pauseManager; + /// public void SaveBlueprint(GridId gridId, string yamlPath) { var grid = _mapManager.GetGrid(gridId); - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager); + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager); context.RegisterGrid(grid); var root = context.Serialize(); var document = new YamlDocument(root); @@ -90,6 +93,7 @@ namespace Robust.Server.Maps reader = new StreamReader(file); } + IMapGrid grid; using (reader) { Logger.InfoS("map", $"Loading Grid: {resPath}"); @@ -101,16 +105,26 @@ namespace Robust.Server.Maps throw new InvalidDataException("Cannot instance map with multiple grids as blueprint."); } - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, (YamlMappingNode)data.RootNode, map); + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, (YamlMappingNode)data.RootNode, map); context.Deserialize(); - return context.Grids[0]; + grid = context.Grids[0]; + + if (!context.MapIsPostInit && _pauseManager.IsMapInitialized(map)) + { + foreach (var entity in context.Entities) + { + entity.RunMapInit(); + } + } } + + return grid; } /// public void SaveMap(IMap map, string yamlPath) { - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager); + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager); foreach (var grid in map.GetAllGrids()) { context.RegisterGrid(grid); @@ -172,8 +186,16 @@ namespace Robust.Server.Maps throw new InvalidDataException("Cannot instance map with multiple grids as blueprint."); } - var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, (YamlMappingNode)data.RootNode, _mapManager.GetMap(mapId)); + var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, (YamlMappingNode)data.RootNode, _mapManager.GetMap(mapId)); context.Deserialize(); + + if (!context.MapIsPostInit && _pauseManager.IsMapInitialized(mapId)) + { + foreach (var entity in context.Entities) + { + entity.RunMapInit(); + } + } } } @@ -185,12 +207,17 @@ namespace Robust.Server.Maps private readonly IMapManager _mapManager; private readonly ITileDefinitionManager _tileDefinitionManager; private readonly IServerEntityManagerInternal _serverEntityManager; + private readonly IPauseManager _pauseManager; private readonly Dictionary GridIDMap = new Dictionary(); public readonly List Grids = new List(); private readonly Dictionary EntityUidMap = new Dictionary(); - private readonly List Entities = new List(); + private readonly Dictionary UidEntityMap = new Dictionary(); + public readonly List Entities = new List(); + + + private int uidCounter; private readonly YamlMappingNode RootNode; private readonly IMap TargetMap; @@ -202,20 +229,24 @@ namespace Robust.Server.Maps private Dictionary _tileMap; - public MapContext(IMapManager maps, ITileDefinitionManager tileDefs, IServerEntityManagerInternal entities) + public bool MapIsPostInit { get; private set; } + + public MapContext(IMapManager maps, ITileDefinitionManager tileDefs, IServerEntityManagerInternal entities, IPauseManager pauseManager) { _mapManager = maps; _tileDefinitionManager = tileDefs; _serverEntityManager = entities; + _pauseManager = pauseManager; RootNode = new YamlMappingNode(); } - public MapContext(IMapManager maps, ITileDefinitionManager tileDefs, IServerEntityManagerInternal entities, YamlMappingNode node, IMap targetMap) + public MapContext(IMapManager maps, ITileDefinitionManager tileDefs, IServerEntityManagerInternal entities, IPauseManager pauseManager, YamlMappingNode node, IMap targetMap) { _mapManager = maps; _tileDefinitionManager = tileDefs; _serverEntityManager = entities; + _pauseManager = pauseManager; RootNode = node; TargetMap = targetMap; @@ -253,6 +284,15 @@ namespace Robust.Server.Maps { throw new InvalidDataException("Cannot handle this map file version."); } + + if (meta.TryGetNode("postmapinit", out var mapInitNode)) + { + MapIsPostInit = mapInitNode.AsBool(); + } + else + { + MapIsPostInit = true; + } } private void ReadTileMapSection() @@ -297,12 +337,14 @@ namespace Robust.Server.Maps foreach (var entityDef in entities.Cast()) { var type = entityDef.GetNode("type").AsString(); + var uid = Entities.Count; + if (entityDef.TryGetNode("uid", out var uidNode)) + { + uid = uidNode.AsInt(); + } var entity = _serverEntityManager.AllocEntity(type); Entities.Add(entity); - if (entityDef.TryGetNode("name", out var nameNode)) - { - entity.Name = nameNode.AsString(); - } + UidEntityMap.Add(uid, entity.Uid); } } @@ -373,6 +415,18 @@ namespace Robust.Server.Maps // TODO: Make these values configurable. meta.Add("name", "DemoStation"); meta.Add("author", "Space-Wizards"); + + var isPostInit = false; + foreach (var grid in Grids) + { + if (_pauseManager.IsMapInitialized(grid.ParentMap)) + { + isPostInit = true; + break; + } + } + + meta.Add("postmapinit", isPostInit ? "true" : "false"); } private void WriteTileMapSection() @@ -403,7 +457,8 @@ namespace Robust.Server.Maps { if (IsMapSavable(entity)) { - EntityUidMap.Add(entity.Uid, EntityUidMap.Count); + var uid = uidCounter++; + EntityUidMap.Add(entity.Uid, uid); Entities.Add(entity); } } @@ -417,13 +472,11 @@ namespace Robust.Server.Maps foreach (var entity in Entities) { CurrentWritingEntity = entity; - var mapping = new YamlMappingNode(); - mapping.Add("type", entity.Prototype.ID); - if (entity.Name != entity.Prototype.Name) + var mapping = new YamlMappingNode { - // TODO: This shouldn't be hardcoded. - mapping.Add("name", entity.Prototype.Name); - } + {"type", entity.Prototype.ID}, + {"uid", EntityUidMap[entity.Uid].ToString(CultureInfo.InvariantCulture)} + }; var components = new YamlSequenceNode(); // See engine#636 for why the Distinct() call. @@ -477,7 +530,7 @@ namespace Robust.Server.Maps } else { - obj = Entities[val].Uid; + obj = UidEntityMap[val]; return true; } } diff --git a/Robust.Server/Robust.Server.csproj b/Robust.Server/Robust.Server.csproj index 6c66fedfe..b6a5a7cf3 100644 --- a/Robust.Server/Robust.Server.csproj +++ b/Robust.Server/Robust.Server.csproj @@ -125,6 +125,7 @@ + diff --git a/Robust.Server/Timing/PauseManager.cs b/Robust.Server/Timing/PauseManager.cs index d9d991b96..c7325682b 100644 --- a/Robust.Server/Timing/PauseManager.cs +++ b/Robust.Server/Timing/PauseManager.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.Timing; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; @@ -8,14 +10,15 @@ using Robust.Shared.ViewVariables; namespace Robust.Server.Timing { - public class PauseManager : IPauseManager, IPostInjectInit + internal sealed class PauseManager : IPauseManager, IPostInjectInit { [Dependency] private IMapManager _mapManager; + [Dependency] private IEntityManager _entityManager; [ViewVariables] private readonly HashSet _pausedMaps = new HashSet(); + [ViewVariables] private readonly HashSet _unInitializedMaps = new HashSet(); public void SetMapPaused(IMap map, bool paused) => SetMapPaused(map.Index, paused); - public void SetMapPaused(MapId mapId, bool paused) { if (paused) @@ -28,6 +31,47 @@ namespace Robust.Server.Timing } } + public void DoMapInitialize(IMap map) => DoMapInitialize(map.Index); + public void DoMapInitialize(MapId mapId) + { + if (IsMapInitialized(mapId)) + { + throw new ArgumentException("That map is already initialized."); + } + + _unInitializedMaps.Remove(mapId); + + foreach (var entity in _entityManager.GetEntities()) + { + if (entity.Transform.MapID != mapId) + { + continue; + } + + entity.RunMapInit(); + } + } + + public void DoGridMapInitialize(IMapGrid grid) => DoGridMapInitialize(grid.Index); + public void DoGridMapInitialize(GridId gridId) + { + foreach (var entity in _entityManager.GetEntities()) + { + if (entity.Transform.GridID != gridId) + { + continue; + } + + entity.RunMapInit(); + } + } + + public void AddUninitializedMap(IMap map) => AddUninitializedMap(map.Index); + public void AddUninitializedMap(MapId mapId) + { + _unInitializedMaps.Add(mapId); + } + public bool IsMapPaused(IMap map) => IsMapPaused(map.Index); public bool IsMapPaused(MapId mapId) => _pausedMaps.Contains(mapId); public bool IsGridPaused(IMapGrid grid) => _pausedMaps.Contains(grid.ParentMapId); @@ -38,9 +82,19 @@ namespace Robust.Server.Timing return IsGridPaused(grid); } + public bool IsMapInitialized(IMap map) => IsMapInitialized(map.Index); + public bool IsMapInitialized(MapId mapId) + { + return !_unInitializedMaps.Contains(mapId); + } + public void PostInject() { - _mapManager.MapDestroyed += (sender, args) => _pausedMaps.Remove(args.Map.Index); + _mapManager.MapDestroyed += (sender, args) => + { + _pausedMaps.Remove(args.Map.Index); + _unInitializedMaps.Add(args.Map.Index); + }; } } } diff --git a/Robust.Shared/GameObjects/Components/MetaDataComponent.cs b/Robust.Shared/GameObjects/Components/MetaDataComponent.cs index 3ba511585..d33750e8c 100644 --- a/Robust.Shared/GameObjects/Components/MetaDataComponent.cs +++ b/Robust.Shared/GameObjects/Components/MetaDataComponent.cs @@ -164,11 +164,11 @@ namespace Robust.Shared.GameObjects { base.ExposeData(serializer); - serializer.DataField(ref _entityName, "name", string.Empty); - serializer.DataField(ref _entityDescription, "desc", string.Empty); - serializer.DataField(ref _entityPrototype, "proto", null, - s => _prototypes.Index(s), - p => p.ID); + serializer.DataField(ref _entityName, "name", null); + serializer.DataField(ref _entityDescription, "desc", null); + //serializer.DataField(ref _entityPrototype, "proto", null, + // s => _prototypes.Index(s), + // p => p.ID); } } } diff --git a/Robust.Shared/Interfaces/Map/IMapManager.cs b/Robust.Shared/Interfaces/Map/IMapManager.cs index f07a353d3..e085772b6 100644 --- a/Robust.Shared/Interfaces/Map/IMapManager.cs +++ b/Robust.Shared/Interfaces/Map/IMapManager.cs @@ -28,6 +28,8 @@ namespace Robust.Shared.Interfaces.Map IEnumerable GetAllMaps(); + IEnumerable GetAllGrids(); + /// /// 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. diff --git a/Robust.Shared/Map/MapManager.cs b/Robust.Shared/Map/MapManager.cs index 845b0c100..62d1442c6 100644 --- a/Robust.Shared/Map/MapManager.cs +++ b/Robust.Shared/Map/MapManager.cs @@ -186,6 +186,11 @@ namespace Robust.Shared.Map return _maps.Values; } + public IEnumerable GetAllGrids() + { + return _grids.Values; + } + public IMapGrid CreateGrid(MapId currentMapID, GridId? gridID = null, ushort chunkSize = 16, float snapSize = 1) { var map = _maps[currentMapID]; diff --git a/docs/Map Format.md b/docs/Map Format.md index 2c21dfc5c..4c2eaf493 100644 --- a/docs/Map Format.md +++ b/docs/Map Format.md @@ -19,6 +19,9 @@ Fields: * `format`: Version identifier. **The current version is `2`.** Can be used to bail out early for unsupported map files. * `name`: A name. Simple huh. Can be left out. * `author`: Authorship info. Also simple. Can be left out. +* `postmapinit`: Whether this map is "post map init". This means that presets such as procedural generation have applied. + In general, maps touched only via map editing mode will have this false. Maps saved mid game will not. + Default value is true if left out. ### The `tilemap` section @@ -42,7 +45,8 @@ Contains data for all the grids. The section is an ordered sequence. Each sequen Contains data for all entities on the map. Just like grids these are stored in an indexed list, and an entity declaration is pretty much just like a prototype. -Each entity has a `type` field which specifies which prototype it is, and the components list works as overrides in the same way as entity parenting. +Each entity has a `type` field which specifies which prototype it is, and the components list works as overrides in the same way as prototype parenting. +Each entity also has a numerical `uid` field, which is used to give this entity an unique identifier when referenced by other entities. #### Chunk Data @@ -84,7 +88,7 @@ Direct hard `IEntity` references are stored as entity UID, it is simply decoded In-game `EntityUid` instances are either: * Serialized as YAML `null` if the entity referenced to is not included in the map saving. If it's on a different grid, for example. -* An integer representing the index in the `entities` section corresponding to the serialized entity. +* An integer representing the `uid` of the serialized entity. ### Grid IDs