TransformComponent.GridID now returns GridID.Nullspace instead of throwing when the entity in question is not on a grid.

Added MapManager.Restart().
Added MapManager.CreateNewMapEntity() and MapManager.SetMapEntity() for manipulating the map root entity.
You can now spawn entities directly into Nullspace, after setting up the Nullspace map entity.
This commit is contained in:
Acruid
2019-12-29 16:08:46 -08:00
parent aa3e126249
commit 58d0a771e8
9 changed files with 285 additions and 14 deletions

View File

@@ -186,6 +186,11 @@ namespace Robust.Client.GameObjects
Shutdown();
}
public override IEntity CreateEntityUninitialized(string prototypeName)
{
return CreateEntity(prototypeName);
}
public override IEntity CreateEntityUninitialized(string prototypeName, GridCoordinates coordinates)
{
var newEntity = CreateEntity(prototypeName, NewClientEntityUid());
@@ -226,6 +231,15 @@ namespace Robust.Client.GameObjects
return SpawnEntity(entityType, coordinates);
}
/// <inheritdoc />
public override IEntity SpawnEntityAt(string entityType, MapCoordinates coordinates)
{
var grid = _mapManager.FindGridAt(coordinates);
var gridCoords = new GridCoordinates(grid.WorldToLocal(coordinates.Position), grid);
return SpawnEntityAt(entityType, gridCoords);
}
private EntityUid NewClientEntityUid()
{
return new EntityUid(NextClientEntityUid++);

View File

@@ -27,6 +27,11 @@ namespace Robust.Server.GameObjects
private readonly List<(GameTick tick, EntityUid uid)> DeletionHistory = new List<(GameTick, EntityUid)>();
public override IEntity CreateEntityUninitialized(string prototypeName)
{
return CreateEntity(prototypeName);
}
public override IEntity CreateEntityUninitialized(string prototypeName, GridCoordinates coordinates)
{
var newEntity = CreateEntity(prototypeName);
@@ -42,11 +47,8 @@ namespace Robust.Server.GameObjects
public override IEntity CreateEntityUninitialized(string prototypeName, MapCoordinates coordinates)
{
var newEntity = CreateEntity(prototypeName);
if(coordinates.MapId != MapId.Nullspace)
{
newEntity.Transform.AttachParent(_mapManager.GetMapEntity(coordinates.MapId));
newEntity.Transform.WorldPosition = coordinates.Position;
}
newEntity.Transform.AttachParent(_mapManager.GetMapEntity(coordinates.MapId));
newEntity.Transform.WorldPosition = coordinates.Position;
return newEntity;
}
@@ -77,6 +79,14 @@ namespace Robust.Server.GameObjects
return entity;
}
/// <inheritdoc />
public override IEntity SpawnEntityAt(string entityType, MapCoordinates coordinates)
{
var entity = CreateEntityUninitialized(entityType, coordinates);
InitializeAndStartEntity((Entity)entity);
return entity;
}
/// <inheritdoc />
public List<EntityState> GetEntityStates(GameTick fromTick)
{

View File

@@ -37,12 +37,12 @@ namespace Robust.Shared.GameObjects.Components.Map
internal set => _mapIndex = value;
}
/// <inheritdoc />
public void ClearMapId()
{
_mapIndex = MapId.Nullspace;
}
/// <inheritdoc />
public override ComponentState GetComponentState()
{

View File

@@ -88,7 +88,8 @@ namespace Robust.Shared.GameObjects.Components.Transform
if (_parent.IsValid())
return Parent.GridID;
throw new InvalidOperationException("Transform node does not exist inside scene tree!");
// Not on a grid
return GridId.Nullspace;
}
}
@@ -313,6 +314,17 @@ namespace Robust.Shared.GameObjects.Components.Transform
/// <inheritdoc />
public Vector2 LerpDestination => _nextPosition;
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
// Verifies MapID can be resolved.
// If it cannot, then this is an orphan entity, and an exception will be thrown.
// DO NOT REMOVE THIS LINE
var _ = MapID;
}
/// <inheritdoc />
protected override void Startup()
{

View File

@@ -106,6 +106,8 @@ namespace Robust.Shared.GameObjects
#region Entity Management
public abstract IEntity CreateEntityUninitialized(string prototypeName);
public abstract IEntity CreateEntityUninitialized(string prototypeName, GridCoordinates coordinates);
public abstract IEntity CreateEntityUninitialized(string prototypeName, MapCoordinates coordinates);
@@ -115,13 +117,7 @@ namespace Robust.Shared.GameObjects
public abstract IEntity SpawnEntityAt(string entityType, GridCoordinates coordinates);
public IEntity SpawnEntityAt(string entityType, MapCoordinates coordinates)
{
var grid = _mapManager.FindGridAt(coordinates);
var gridCoords = new GridCoordinates(grid.WorldToLocal(coordinates.Position), grid);
return SpawnEntityAt(entityType, gridCoords);
}
public abstract IEntity SpawnEntityAt(string entityType, MapCoordinates coordinates);
/// <summary>
/// Returns an entity by id

View File

@@ -31,6 +31,8 @@ namespace Robust.Shared.Interfaces.GameObjects
#region Entity Management
IEntity CreateEntityUninitialized(string prototypeName);
IEntity CreateEntityUninitialized(string prototypeName, GridCoordinates coordinates);
IEntity CreateEntityUninitialized(string prototypeName, MapCoordinates coordinates);

View File

@@ -45,6 +45,8 @@ namespace Robust.Shared.Interfaces.Map
void Shutdown();
void Startup();
void Restart();
/// <summary>
/// Creates a new map.
/// </summary>
@@ -68,6 +70,24 @@ namespace Robust.Shared.Interfaces.Map
/// <returns>True if the map exists, false otherwise.</returns>
bool MapExists(MapId mapID);
/// <summary>
/// Creates a new entity, then sets it as the map entity.
/// </summary>
/// <returns>Newly created entity.</returns>
IEntity CreateNewMapEntity(MapId mapId);
/// <summary>
/// Sets the MapEntity(root node) for a given map. If an entity is already set, it will be deleted
/// before the new one is set.
/// </summary>
void SetMapEntity(MapId mapId, EntityUid newMapEntityId);
/// <summary>
/// Sets the MapEntity(root node) for a given map. If an entity is already set, it will be deleted
/// before the new one is set.
/// </summary>
void SetMapEntity(MapId mapId, IEntity newMapEntity);
EntityUid GetMapEntityId(MapId mapId);
IEntity GetMapEntity(MapId mapId);

View File

@@ -89,6 +89,39 @@ namespace Robust.Shared.Map
DebugTools.Assert(_grids.Count == 1);
}
/// <inheritdoc />
public void Restart()
{
foreach (var mapId in _maps.ToArray())
{
if (mapId != MapId.Nullspace)
{
DeleteMap(mapId);
}
}
if (!_maps.Contains(MapId.Nullspace))
{
CreateMap(MapId.Nullspace, GridId.Nullspace);
}
else if(_mapEntities.TryGetValue(MapId.Nullspace, out var mapEntId))
{
var mapEnt = _entityManager.GetEntity(mapEntId);
var defaultGridId = _defaultGrids[MapId.Nullspace];
var defaultGridEntityId = GetGrid(defaultGridId).GridEntity;
foreach (var childTransform in mapEnt.Transform.Children.ToArray())
{
if(childTransform.Owner.Uid == defaultGridEntityId)
continue;
childTransform.Owner.Delete();
}
}
DebugTools.Assert(_grids.Count == 1);
DebugTools.Assert(GridExists(GridId.Nullspace));
}
/// <summary>
/// Raises the OnTileChanged event.
/// </summary>
@@ -214,6 +247,62 @@ namespace Robust.Shared.Map
return _maps.Contains(mapID);
}
public IEntity CreateNewMapEntity(MapId mapId)
{
var newEntity = _entityManager.CreateEntityUninitialized(null);
SetMapEntity(mapId, newEntity);
return newEntity;
}
/// <inheritdoc />
public void SetMapEntity(MapId mapId, EntityUid newMapEntityId)
{
var newMapEntity = _entityManager.GetEntity(newMapEntityId);
SetMapEntity(mapId, newMapEntity);
}
/// <inheritdoc />
public void SetMapEntity(MapId mapId, IEntity newMapEntity)
{
if(!_maps.Contains(mapId))
throw new InvalidOperationException($"Map {mapId} does not exist.");
foreach (var kvEntity in _mapEntities)
{
if (kvEntity.Value == newMapEntity.Uid)
{
throw new InvalidOperationException($"Entity {newMapEntity} is already the root node of map {kvEntity.Key}.");
}
}
// remove existing graph
if (_mapEntities.TryGetValue(mapId, out var oldEntId))
{
//Note: This prevents setting a subgraph as the root, since the subgraph will be deleted
var oldMapEnt = _entityManager.GetEntity(oldEntId);
_entityManager.DeleteEntity(oldMapEnt);
}
else
{
_mapEntities.Add(mapId, EntityUid.Invalid);
}
// re-use or add map component
if (!newMapEntity.TryGetComponent(out MapComponent mapComp))
{
mapComp = newMapEntity.AddComponent<MapComponent>();
}
else
{
if (mapComp.WorldMap != mapId)
Logger.WarningS("map", $"Setting map {mapId} root to entity {newMapEntity}, but entity thinks it is root node of map {mapComp.WorldMap}.");
}
// set as new map entity
mapComp.WorldMap = mapId;
_mapEntities[mapId] = newMapEntity.Uid;
}
public EntityUid GetMapEntityId(MapId mapId)
{
return _mapEntities[mapId];
@@ -221,6 +310,9 @@ namespace Robust.Shared.Map
public IEntity GetMapEntity(MapId mapId)
{
if(!_mapEntities.ContainsKey(mapId))
throw new InvalidOperationException($"Map {mapId} does not have a set map entity.");
return _entityManager.GetEntity(_mapEntities[mapId]);
}

View File

@@ -0,0 +1,125 @@
using NUnit.Framework;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Shared.Map
{
[TestFixture, TestOf(typeof(MapManager))]
class MapManager_Tests : RobustUnitTest
{
[SetUp]
public void Setup()
{
var mapMan = IoCManager.Resolve<IMapManager>();
mapMan.Restart();
}
/// <summary>
/// When the map manager is restarted, the maps are deleted.
/// </summary>
[Test]
public void Restart_ExistingMap_IsRemoved()
{
var mapMan = IoCManager.Resolve<IMapManager>();
var mapID = new MapId(11);
mapMan.CreateMap(mapID);
mapMan.Restart();
Assert.That(mapMan.MapExists(mapID), Is.False);
}
/// <summary>
/// When the map manager is restarted, the grids are removed.
/// </summary>
[Test]
public void Restart_ExistingGrid_IsRemoved()
{
var mapMan = IoCManager.Resolve<IMapManager>();
var mapID = new MapId(11);
var gridId = new GridId(7);
mapMan.CreateMap(mapID);
mapMan.CreateGrid(mapID, gridId);
mapMan.Restart();
Assert.That(mapMan.GridExists(gridId), Is.False);
}
/// <summary>
/// When the map manager is restarted, Nullspace is recreated.
/// </summary>
[Test]
public void Restart_NullspaceMap_IsEmptied()
{
var mapMan = IoCManager.Resolve<IMapManager>();
var entMan = IoCManager.Resolve<IServerEntityManager>();
mapMan.CreateNewMapEntity(MapId.Nullspace);
var oldEntity = (Entity)entMan.CreateEntityUninitialized(null, new MapCoordinates(Vector2.Zero, MapId.Nullspace));
oldEntity.InitializeComponents();
mapMan.Restart();
Assert.That(mapMan.MapExists(MapId.Nullspace), Is.True);
Assert.That(mapMan.GridExists(GridId.Nullspace), Is.True);
Assert.That(oldEntity.Deleted, Is.True);
}
/// <summary>
/// When using SetMapEntity, the existing entities on the map are removed, and the new map entity gets a IMapComponent.
/// </summary>
[Test]
public void SetMapEntity_WithExistingEntity_ExistingEntityDeleted()
{
// Arrange
var mapID = new MapId(11);
var mapMan = IoCManager.Resolve<IMapManager>();
var entMan = IoCManager.Resolve<IServerEntityManager>();
mapMan.CreateMap(new MapId(7));
mapMan.CreateMap(mapID);
var oldMapEntity = mapMan.GetMapEntity(mapID);
var newMapEntity = entMan.CreateEntityUninitialized(null, new MapCoordinates(Vector2.Zero, new MapId(7)));
// Act
mapMan.SetMapEntity(mapID, newMapEntity);
// Assert
Assert.That(oldMapEntity.Deleted);
Assert.That(newMapEntity.HasComponent<IMapComponent>());
var mapComp = newMapEntity.GetComponent<IMapComponent>();
Assert.That(mapComp.WorldMap == mapID);
}
/// <summary>
/// After creating a new map entity for nullspace, you can spawn entities into nullspace like any other map.
/// </summary>
[Test]
public void SpawnEntityAt_IntoNullspace_Success()
{
// Arrange
var mapMan = IoCManager.Resolve<IMapManager>();
var entMan = IoCManager.Resolve<IEntityManager>();
mapMan.CreateNewMapEntity(MapId.Nullspace);
// Act
var newEntity = entMan.SpawnEntityAt(null, new MapCoordinates(Vector2.Zero, MapId.Nullspace));
// Assert
Assert.That(newEntity.Transform.MapID, Is.EqualTo(MapId.Nullspace));
}
}
}