mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
* Refactor map loading & saving * test fixes * ISerializationManager tweaks * Fix component composition * Try fix entity deserialization component composition * comments * CL * error preinit * a * cleanup * error if version is too new * Add AlwaysPushSerializationTest * Add auto-inclusion test * Better categorization * Combine test components * Save -> TrySave Also better handling for saving multiple entities individually * Create new partial class for map loading * Add OrphanSerializationTest * Include MapIds in BeforeSerializationEvent * Addd LifetimeSerializationTest * Add TestMixedLifetimeSerialization * Add CategorizationTest * explicitly serialize list of nullspace entities * Add backwards compatibility test * Version comments also fixes wrong v4 format * add MapMergeTest * Add NetEntity support * Optimize EntityDeserializer Avoid unnecessary component deserialization * fix assert & other bugs * fucking containers strike again * Fix deletion of pre-init entities * fix release note merge conflict * Update Robust.Shared/Map/MapManager.GridCollection.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * VV --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
307 lines
14 KiB
C#
307 lines
14 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using NUnit.Framework;
|
|
using Robust.Shared.EntitySerialization;
|
|
using Robust.Shared.EntitySerialization.Components;
|
|
using Robust.Shared.EntitySerialization.Systems;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Utility;
|
|
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
|
|
|
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
|
|
|
[TestFixture]
|
|
public sealed partial class AutoIncludeSerializationTest : RobustIntegrationTest
|
|
{
|
|
[Test]
|
|
public async Task TestAutoIncludeSerialization()
|
|
{
|
|
var server = StartServer();
|
|
await server.WaitIdleAsync();
|
|
var entMan = server.EntMan;
|
|
var mapSys = server.System<SharedMapSystem>();
|
|
var loader = server.System<MapLoaderSystem>();
|
|
var mapMan = server.ResolveDependency<IMapManager>();
|
|
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
|
var mapPath = new ResPath($"{nameof(AutoIncludeSerializationTest)}_map.yml");
|
|
var gridPath = new ResPath($"{nameof(AutoIncludeSerializationTest)}_grid.yml");
|
|
|
|
tileMan.Register(new TileDef("space"));
|
|
var tDef = new TileDef("a");
|
|
tileMan.Register(tDef);
|
|
|
|
// Create a map that contains an entity that references a nullspace entity.
|
|
MapId mapId = default;
|
|
Entity<TransformComponent, EntitySaveTestComponent> map = default;
|
|
Entity<TransformComponent, EntitySaveTestComponent> grid = default;
|
|
Entity<TransformComponent, EntitySaveTestComponent> onGrid = default;
|
|
Entity<TransformComponent, EntitySaveTestComponent> offGrid = default;
|
|
Entity<TransformComponent, EntitySaveTestComponent> nullSpace = default;
|
|
|
|
void AssertCount(int expected) => Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(expected));
|
|
|
|
await server.WaitPost(() =>
|
|
{
|
|
var mapUid = mapSys.CreateMap(out mapId);
|
|
var gridUid = mapMan.CreateGridEntity(mapId);
|
|
mapSys.SetTile(gridUid, Vector2i.Zero, new Tile(tDef.TileId));
|
|
|
|
var onGridUid = entMan.SpawnEntity(null, new EntityCoordinates(gridUid, 0.5f, 0.5f));
|
|
var offGridUid = entMan.SpawnEntity(null, new MapCoordinates(10f, 10f, mapId));
|
|
var nullSpaceUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
|
|
|
map = Get(mapUid, entMan);
|
|
grid = Get(gridUid, entMan);
|
|
onGrid = Get(onGridUid, entMan);
|
|
offGrid = Get(offGridUid, entMan);
|
|
nullSpace = Get(nullSpaceUid, entMan);
|
|
});
|
|
|
|
await server.WaitRunTicks(5);
|
|
|
|
Assert.That(map.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(grid.Comp1!.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(onGrid.Comp1!.ParentUid, Is.EqualTo(grid.Owner));
|
|
Assert.That(offGrid.Comp1!.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(nullSpace.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
|
|
// Assign unique ids.
|
|
map.Comp2!.Id = nameof(map);
|
|
grid.Comp2!.Id = nameof(grid);
|
|
onGrid.Comp2!.Id = nameof(onGrid);
|
|
offGrid.Comp2!.Id = nameof(offGrid);
|
|
nullSpace.Comp2!.Id = nameof(nullSpace);
|
|
|
|
// First simple map loading without any references to other entities.
|
|
// This will cause the null-space entity to be lost.
|
|
// Save the map, then delete all the entities.
|
|
AssertCount(5);
|
|
Assert.That(loader.TrySaveMap(mapId, mapPath));
|
|
Assert.That(loader.TrySaveGrid(grid, gridPath));
|
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
|
AssertCount(1);
|
|
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
|
AssertCount(0);
|
|
|
|
// Load up the file that only saved the grid and check that the expected entities exist.
|
|
await server.WaitPost(() => mapSys.CreateMap(out mapId));
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
|
|
|
AssertCount(2);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
|
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
|
AssertCount(0);
|
|
|
|
// Load up the map, and check that the expected entities exist.
|
|
Entity<MapComponent>? loadedMap = default;
|
|
HashSet<Entity<MapGridComponent>>? loadedGrids = default!;
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out loadedMap, out loadedGrids)));
|
|
mapId = loadedMap!.Value.Comp.MapId;
|
|
Assert.That(loadedGrids, Has.Count.EqualTo(1));
|
|
|
|
AssertCount(4);
|
|
map = Find(nameof(map), entMan);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
offGrid = Find(nameof(offGrid), entMan);
|
|
|
|
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
|
|
// Re-spawn the nullspace entity
|
|
await server.WaitPost(() =>
|
|
{
|
|
var nullSpaceUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
|
nullSpace = Get(nullSpaceUid, entMan);
|
|
nullSpace.Comp2.Id = nameof(nullSpace);
|
|
});
|
|
|
|
// Repeat the previous saves, but with an entity that references the null-space entity.
|
|
onGrid.Comp2.Entity = nullSpace.Owner;
|
|
|
|
AssertCount(5);
|
|
Assert.That(loader.TrySaveMap(mapId, mapPath));
|
|
Assert.That(loader.TrySaveGrid(grid, gridPath));
|
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
|
AssertCount(1);
|
|
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
|
AssertCount(0);
|
|
|
|
// Load up the file that only saved the grid and check that the expected entities exist.
|
|
await server.WaitPost(() => mapSys.CreateMap(out mapId));
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
|
|
|
AssertCount(3);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
nullSpace = Find(nameof(nullSpace), entMan);
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
Assert.That(onGrid.Comp2.Entity, Is.EqualTo(nullSpace.Owner));
|
|
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
|
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
|
AssertCount(1);
|
|
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
|
AssertCount(0);
|
|
|
|
// Load up the map, and check that the expected entities exist.
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out loadedMap, out loadedGrids)));
|
|
mapId = loadedMap!.Value.Comp.MapId;
|
|
Assert.That(loadedGrids, Has.Count.EqualTo(1));
|
|
|
|
AssertCount(5);
|
|
map = Find(nameof(map), entMan);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
offGrid = Find(nameof(offGrid), entMan);
|
|
nullSpace = Find(nameof(nullSpace), entMan);
|
|
|
|
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(onGrid.Comp2.Entity, Is.EqualTo(nullSpace.Owner));
|
|
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
|
|
// Check that attempting to save a reference to a non-null-space entity does not auto-include it.
|
|
Entity<TransformComponent, EntitySaveTestComponent> otherMap = default;
|
|
await server.WaitPost(() =>
|
|
{
|
|
var otherMapUid = mapSys.CreateMap();
|
|
otherMap = Get(otherMapUid, entMan);
|
|
otherMap.Comp2.Id = nameof(otherMap);
|
|
});
|
|
onGrid.Comp2.Entity = otherMap.Owner;
|
|
|
|
// By default it should log an error, but tests don't have a nice way to validate that an error was logged, so we'll just suppress it.
|
|
var opts = SerializationOptions.Default with {MissingEntityBehaviour = MissingEntityBehaviour.Ignore};
|
|
AssertCount(6);
|
|
Assert.That(loader.TrySaveMap(mapId, mapPath, opts));
|
|
Assert.That(loader.TrySaveGrid(grid, gridPath, opts));
|
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
|
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
|
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
|
|
AssertCount(0);
|
|
|
|
// Check the grid file
|
|
await server.WaitPost(() => mapSys.CreateMap(out mapId));
|
|
var dOpts = DeserializationOptions.Default with {LogInvalidEntities = false};
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _, dOpts)));
|
|
AssertCount(2);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
|
AssertCount(0);
|
|
|
|
// Check the map file
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out loadedMap, out loadedGrids, dOpts)));
|
|
mapId = loadedMap!.Value.Comp.MapId;
|
|
Assert.That(loadedGrids, Has.Count.EqualTo(1));
|
|
AssertCount(4);
|
|
map = Find(nameof(map), entMan);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
offGrid = Find(nameof(offGrid), entMan);
|
|
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
|
|
// repeat the check, but this time with auto inclusion fully enabled.
|
|
Entity<TransformComponent, EntitySaveTestComponent> otherEnt = default;
|
|
await server.WaitPost(() =>
|
|
{
|
|
var otherMapUid = mapSys.CreateMap(out var otherMapId);
|
|
otherMap = Get(otherMapUid, entMan);
|
|
otherMap.Comp2.Id = nameof(otherMap);
|
|
|
|
var otherEntUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, otherMapId));
|
|
otherEnt = Get(otherEntUid, entMan);
|
|
otherEnt.Comp2.Id = nameof(otherEnt);
|
|
|
|
var nullSpaceUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
|
nullSpace = Get(nullSpaceUid, entMan);
|
|
nullSpace.Comp2.Id = nameof(nullSpace);
|
|
});
|
|
|
|
onGrid.Comp2.Entity = otherMap.Owner;
|
|
otherEnt.Comp2!.Entity = nullSpace;
|
|
|
|
AssertCount(7);
|
|
opts = opts with {MissingEntityBehaviour = MissingEntityBehaviour.AutoInclude};
|
|
Assert.That(loader.TrySaveGeneric(map.Owner, mapPath, out var cat, opts));
|
|
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
|
|
Assert.That(loader.TrySaveGeneric(grid.Owner, gridPath, out cat, opts));
|
|
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
|
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
|
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
|
|
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
|
AssertCount(0);
|
|
|
|
// Check the grid file
|
|
await server.WaitPost(() => mapSys.CreateMap(out mapId));
|
|
var mapLoadOpts = MapLoadOptions.Default with
|
|
{
|
|
DeserializationOptions = DeserializationOptions.Default with {LogOrphanedGrids = false}
|
|
};
|
|
LoadResult? result = default;
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(gridPath, out result, mapLoadOpts)));
|
|
Assert.That(result!.Grids, Has.Count.EqualTo(1));
|
|
Assert.That(result.Orphans, Is.Empty); // Grid was orphaned, but was adopted after a new map was created
|
|
Assert.That(result.Maps, Has.Count.EqualTo(2));
|
|
Assert.That(result.NullspaceEntities, Has.Count.EqualTo(1));
|
|
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1)); // auto-generated map isn't marked as "loaded"
|
|
AssertCount(5);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
otherMap = Find(nameof(otherMap), entMan);
|
|
otherEnt = Find(nameof(otherEnt), entMan);
|
|
nullSpace = Find(nameof(nullSpace), entMan);
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
Assert.That(otherEnt.Comp1.ParentUid, Is.EqualTo(otherMap.Owner));
|
|
Assert.That(otherMap.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
|
|
await server.WaitPost(() => entMan.DeleteEntity(grid.Comp1.ParentUid));
|
|
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
|
AssertCount(0);
|
|
|
|
// Check the map file
|
|
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(mapPath, out result)));
|
|
Assert.That(result.Orphans, Is.Empty);
|
|
Assert.That(result.NullspaceEntities, Has.Count.EqualTo(1));
|
|
Assert.That(result.Grids, Has.Count.EqualTo(1));
|
|
Assert.That(result.Maps, Has.Count.EqualTo(2));
|
|
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(2));
|
|
AssertCount(7);
|
|
map = Find(nameof(map), entMan);
|
|
grid = Find(nameof(grid), entMan);
|
|
onGrid = Find(nameof(onGrid), entMan);
|
|
offGrid = Find(nameof(offGrid), entMan);
|
|
otherMap = Find(nameof(otherMap), entMan);
|
|
otherEnt = Find(nameof(otherEnt), entMan);
|
|
nullSpace = Find(nameof(nullSpace), entMan);
|
|
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
|
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
|
Assert.That(otherEnt.Comp1.ParentUid, Is.EqualTo(otherMap.Owner));
|
|
Assert.That(otherMap.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
|
await server.WaitPost(() => entMan.DeleteEntity(map));
|
|
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
|
|
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
|
AssertCount(0);
|
|
}
|
|
}
|