mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Merge remote-tracking branch 'origin/master' into 25-11-01-cef-update
This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework.Constraints;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting
|
||||
{
|
||||
public sealed class ApproxEqualityConstraint : Constraint
|
||||
{
|
||||
public object Expected { get; }
|
||||
public double? Tolerance { get; }
|
||||
|
||||
public ApproxEqualityConstraint(object expected, double? tolerance = null)
|
||||
{
|
||||
Expected = expected;
|
||||
Tolerance = tolerance;
|
||||
}
|
||||
|
||||
public override ConstraintResult ApplyTo<TActual>(TActual actual)
|
||||
{
|
||||
if (Expected is not IApproxEquatable<TActual> equatable)
|
||||
{
|
||||
if (Expected is float f1 && actual is float f2)
|
||||
{
|
||||
if (Tolerance != null)
|
||||
{
|
||||
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(f1, f2, Tolerance.Value));
|
||||
}
|
||||
|
||||
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(f1, f2));
|
||||
}
|
||||
|
||||
if (Expected is double d1 && actual is float d2)
|
||||
{
|
||||
if (Tolerance != null)
|
||||
{
|
||||
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(d1, d2, Tolerance.Value));
|
||||
}
|
||||
|
||||
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(d1, d2));
|
||||
}
|
||||
|
||||
if (Expected is Vector2 exp && actual is Vector2 act)
|
||||
{
|
||||
if (Tolerance != null)
|
||||
{
|
||||
return new ConstraintResult(this, actual, exp.EqualsApprox(act, Tolerance.Value));
|
||||
}
|
||||
|
||||
return new ConstraintResult(this, actual, exp.EqualsApprox(act));
|
||||
}
|
||||
|
||||
if (Expected is Matrix3x2 m3x2Expected && actual is Matrix3x2 m3x2Actual)
|
||||
{
|
||||
if (Tolerance != null)
|
||||
{
|
||||
return new ConstraintResult(this, actual, m3x2Expected.EqualsApprox(m3x2Actual, Tolerance.Value));
|
||||
}
|
||||
|
||||
return new ConstraintResult(this, actual, m3x2Expected.EqualsApprox(m3x2Actual));
|
||||
}
|
||||
|
||||
return new ConstraintResult(this, actual, false);
|
||||
}
|
||||
|
||||
if (Tolerance != null)
|
||||
{
|
||||
return new ConstraintResult(this, actual, equatable.EqualsApprox(actual, Tolerance.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ConstraintResult(this, actual, equatable.EqualsApprox(actual));
|
||||
}
|
||||
}
|
||||
|
||||
public override string Description => $"approximately {Expected}";
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,7 @@ using NUnit.Framework;
|
||||
|
||||
// So it can use RobustServerSimulation.
|
||||
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
|
||||
[assembly: InternalsVisibleTo("Robust.Server.IntegrationTests")]
|
||||
[assembly: InternalsVisibleTo("Robust.Shared.IntegrationTests")]
|
||||
|
||||
[assembly: Parallelizable(ParallelScope.Fixtures)]
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Client.GameObjects.Components
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(TransformComponent))]
|
||||
public sealed class TransformComponentTests
|
||||
{
|
||||
private static (ISimulation, EntityUid gridA, EntityUid gridB) SimulationFactory()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.InitializeInstance();
|
||||
|
||||
var mapId = sim.Resolve<IEntityManager>().System<SharedMapSystem>().CreateMap();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
|
||||
// Adds two grids to use in tests.
|
||||
var gridA = mapManager.CreateGridEntity(mapId);
|
||||
var gridB = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
return (sim, gridA, gridB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure that component state locations are RELATIVE.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ComponentStatePositionTest()
|
||||
{
|
||||
var (sim, gridIdA, gridIdB) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var xformSystem = entMan.System<SharedTransformSystem>();
|
||||
var gridSystem = entMan.System<SharedGridTraversalSystem>();
|
||||
gridSystem.Enabled = false;
|
||||
|
||||
// Arrange
|
||||
var initialPos = new EntityCoordinates(gridIdA, new Vector2(0, 0));
|
||||
var parent = entMan.SpawnEntity(null, initialPos);
|
||||
var child = entMan.SpawnEntity(null, initialPos);
|
||||
var parentTrans = entMan.GetComponent<TransformComponent>(parent);
|
||||
var childTrans = entMan.GetComponent<TransformComponent>(child);
|
||||
ComponentHandleState handleState;
|
||||
|
||||
var compState = new TransformComponentState(new Vector2(5, 5), new Angle(0), entMan.GetNetEntity(gridIdB), false, false);
|
||||
handleState = new ComponentHandleState(compState, null);
|
||||
xformSystem.OnHandleState(parent, parentTrans, ref handleState);
|
||||
|
||||
compState = new TransformComponentState(new Vector2(6, 6), new Angle(0), entMan.GetNetEntity(gridIdB), false, false);
|
||||
handleState = new ComponentHandleState(compState, null);
|
||||
xformSystem.OnHandleState(child, childTrans, ref handleState);
|
||||
// World pos should be 6, 6 now.
|
||||
|
||||
// Act
|
||||
var oldWpos = xformSystem.GetWorldPosition(childTrans);
|
||||
compState = new TransformComponentState(new Vector2(1, 1), new Angle(0), entMan.GetNetEntity(parent), false, false);
|
||||
handleState = new ComponentHandleState(compState, null);
|
||||
xformSystem.OnHandleState(child, childTrans, ref handleState);
|
||||
var newWpos = xformSystem.GetWorldPosition(childTrans);
|
||||
|
||||
gridSystem.Enabled = true;
|
||||
|
||||
// Assert
|
||||
Assert.That(newWpos, Is.EqualTo(oldWpos));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that world rotation is built properly
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WorldRotationTest()
|
||||
{
|
||||
var (sim, gridIdA, gridIdB) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var xformSystem = entMan.System<SharedTransformSystem>();
|
||||
var metaSystem = entMan.System<MetaDataSystem>();
|
||||
var gridSystem = entMan.System<SharedGridTraversalSystem>();
|
||||
gridSystem.Enabled = false;
|
||||
|
||||
// Arrange
|
||||
var initalPos = new EntityCoordinates(gridIdA, new Vector2(0, 0));
|
||||
var node1 = entMan.SpawnEntity(null, initalPos);
|
||||
var node2 = entMan.SpawnEntity(null, initalPos);
|
||||
var node3 = entMan.SpawnEntity(null, initalPos);
|
||||
|
||||
metaSystem.SetEntityName(node1, "node1_dummy");
|
||||
metaSystem.SetEntityName(node2, "node2_dummy");
|
||||
metaSystem.SetEntityName(node3, "node3_dummy");
|
||||
|
||||
var node1Trans = entMan.GetComponent<TransformComponent>(node1);
|
||||
var node2Trans = entMan.GetComponent<TransformComponent>(node2);
|
||||
var node3Trans = entMan.GetComponent<TransformComponent>(node3);
|
||||
|
||||
var compState = new TransformComponentState(new Vector2(6, 6), Angle.FromDegrees(135), entMan.GetNetEntity(gridIdB), false, false);
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
xformSystem.OnHandleState(node1, node1Trans, ref handleState);
|
||||
|
||||
compState = new TransformComponentState(new Vector2(1, 1), Angle.FromDegrees(45), entMan.GetNetEntity(node1), false, false);
|
||||
handleState = new ComponentHandleState(compState, null);
|
||||
xformSystem.OnHandleState(node2, node2Trans, ref handleState);
|
||||
|
||||
compState = new TransformComponentState(new Vector2(0, 0), Angle.FromDegrees(45), entMan.GetNetEntity(node2), false, false);
|
||||
handleState = new ComponentHandleState(compState, null);
|
||||
xformSystem.OnHandleState(node3, node3Trans, ref handleState);
|
||||
|
||||
// Act
|
||||
var result = xformSystem.GetWorldRotation(node3Trans);
|
||||
|
||||
gridSystem.Enabled = true;
|
||||
|
||||
// Assert (135 + 45 + 45 = 225)
|
||||
Assert.That(result, new ApproxEqualityConstraint(Angle.FromDegrees(225)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Client.GameStates
|
||||
{
|
||||
[TestFixture, Parallelizable, TestOf(typeof(GameStateProcessor))]
|
||||
sealed class GameStateProcessor_Tests
|
||||
{
|
||||
[Test]
|
||||
public void FillBufferBlocksProcessing()
|
||||
{
|
||||
var timingMock = new Mock<IClientGameTiming>();
|
||||
timingMock.SetupProperty(p => p.CurTick);
|
||||
|
||||
var timing = timingMock.Object;
|
||||
var managerMock = new Mock<IClientGameStateManager>();
|
||||
var logMock = new Mock<ISawmill>();
|
||||
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
|
||||
processor.Interpolation = true;
|
||||
|
||||
processor.AddNewState(GameStateFactory(0, 1));
|
||||
processor.AddNewState(GameStateFactory(1, 2)); // buffer is at 2/3, so processing should be blocked
|
||||
|
||||
// calculate states for first tick
|
||||
timing.LastProcessedTick = new GameTick(0);
|
||||
var result = processor.TryGetServerState(out _, out _);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FillBufferAndCalculateFirstState()
|
||||
{
|
||||
var timingMock = new Mock<IClientGameTiming>();
|
||||
timingMock.SetupProperty(p => p.CurTick);
|
||||
|
||||
var timing = timingMock.Object;
|
||||
var managerMock = new Mock<IClientGameStateManager>();
|
||||
var logMock = new Mock<ISawmill>();
|
||||
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
|
||||
|
||||
processor.AddNewState(GameStateFactory(0, 1));
|
||||
processor.AddNewState(GameStateFactory(1, 2));
|
||||
processor.AddNewState(GameStateFactory(2, 3)); // buffer is now full, otherwise cannot calculate states.
|
||||
|
||||
// calculate states for first tick
|
||||
timing.LastProcessedTick = new GameTick(0);
|
||||
var result = processor.TryGetServerState(out var curState, out var nextState);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(curState, Is.Not.Null);
|
||||
Assert.That(curState!.ToSequence.Value, Is.EqualTo(1));
|
||||
Assert.That(nextState, Is.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a full state is in the queue (fromSequence = 0), it will modify CurTick to the states' toSequence,
|
||||
/// then return the state as curState.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void FullStateResyncsCurTick()
|
||||
{
|
||||
var timingMock = new Mock<IClientGameTiming>();
|
||||
timingMock.SetupProperty(p => p.CurTick);
|
||||
|
||||
var timing = timingMock.Object;
|
||||
var managerMock = new Mock<IClientGameStateManager>();
|
||||
var logMock = new Mock<ISawmill>();
|
||||
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
|
||||
|
||||
processor.AddNewState(GameStateFactory(0, 1));
|
||||
processor.AddNewState(GameStateFactory(1, 2));
|
||||
processor.AddNewState(GameStateFactory(2, 3)); // buffer is now full, otherwise cannot calculate states.
|
||||
|
||||
// calculate states for first tick
|
||||
timing.LastProcessedTick = new GameTick(2);
|
||||
processor.TryGetServerState(out var state, out _);
|
||||
|
||||
Assert.That(state, Is.Not.Null);
|
||||
Assert.That(state!.ToSequence.Value, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatesReceivedPastCurTickAreDropped()
|
||||
{
|
||||
var (timing, processor) = SetupProcessorFactory();
|
||||
|
||||
// a few moments later...
|
||||
timing.LastProcessedTick = new GameTick(4); // current clock is ahead of server
|
||||
processor.AddNewState(GameStateFactory(3, 4)); // received a late state
|
||||
var result = processor.TryGetServerState(out _, out _);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The server fell behind the client, so the client clock is now ahead of the incoming states.
|
||||
/// Without extrapolation, processing blocks.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ServerLagsWithoutExtrapolation()
|
||||
{
|
||||
var (timing, processor) = SetupProcessorFactory();
|
||||
|
||||
// a few moments later...
|
||||
timing.LastProcessedTick = new GameTick(4); // current clock is ahead of server
|
||||
var result = processor.TryGetServerState(out _, out _);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There is a hole in the state buffer, we have a future state but their FromSequence is too high!
|
||||
/// In this case we stop and wait for the server to get us the missing link.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Hole()
|
||||
{
|
||||
var (timing, processor) = SetupProcessorFactory();
|
||||
|
||||
processor.AddNewState(GameStateFactory(4, 5));
|
||||
timing.LastRealTick = new GameTick(3);
|
||||
timing.LastProcessedTick = new GameTick(3);
|
||||
|
||||
var result = processor.TryGetServerState(out _, out _);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that the game state manager goes into extrapolation mode *temporarily*,
|
||||
/// if we are missing the curState, but we have a future state that we can apply to skip it.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ExtrapolateAdvanceWithFutureState()
|
||||
{
|
||||
var (timing, processor) = SetupProcessorFactory();
|
||||
|
||||
processor.Interpolation = true;
|
||||
|
||||
timing.LastProcessedTick = new GameTick(3);
|
||||
|
||||
timing.LastRealTick = new GameTick(3);
|
||||
processor.AddNewState(GameStateFactory(3, 5));
|
||||
|
||||
// We're missing the state for this tick so go into extrap.
|
||||
var result = processor.TryGetServerState(out var curState, out _);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(curState, Is.Null);
|
||||
|
||||
timing.LastProcessedTick = new GameTick(4);
|
||||
|
||||
// But we DO have the state for the tick after so apply away!
|
||||
result = processor.TryGetServerState(out curState, out _);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(curState, Is.Not.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new empty GameState with the given to and from properties.
|
||||
/// </summary>
|
||||
private static GameState GameStateFactory(uint from, uint to)
|
||||
{
|
||||
return new(new GameTick(@from), new GameTick(to), 0, default, default, default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GameTiming and GameStateProcessor, fills the processor with enough states, and calculate the first tick.
|
||||
/// CurTick = 1, states 1 - 3 are in the buffer.
|
||||
/// </summary>
|
||||
private static (IClientGameTiming timing, GameStateProcessor processor) SetupProcessorFactory()
|
||||
{
|
||||
var timingMock = new Mock<IClientGameTiming>();
|
||||
timingMock.SetupProperty(p => p.CurTick);
|
||||
timingMock.SetupProperty(p => p.LastProcessedTick);
|
||||
timingMock.SetupProperty(p => p.LastRealTick);
|
||||
timingMock.SetupProperty(p => p.TickTimingAdjustment);
|
||||
|
||||
var timing = timingMock.Object;
|
||||
var managerMock = new Mock<IClientGameStateManager>();
|
||||
var logMock = new Mock<ISawmill>();
|
||||
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
|
||||
|
||||
processor.AddNewState(GameStateFactory(0, 1));
|
||||
processor.AddNewState(GameStateFactory(1, 2));
|
||||
processor.AddNewState(GameStateFactory(2, 3)); // buffer is now full, otherwise cannot calculate states.
|
||||
|
||||
processor.OnFullStateReceived();
|
||||
timing.LastProcessedTick = timing.LastRealTick = new GameTick(1);
|
||||
|
||||
return (timing, processor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.Graphics
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(IEyeManager))]
|
||||
public sealed class EyeManagerTest : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestViewportRotation()
|
||||
{
|
||||
// At cardinal rotations with a square viewport these should all be the same.
|
||||
var client = StartClient();
|
||||
await client.WaitIdleAsync();
|
||||
|
||||
var eyeManager = client.ResolveDependency<IEyeManager>();
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
// At this stage integration tests aren't pooled so no way I'm making each of these a new test for now.
|
||||
foreach (var angle in new[]
|
||||
{
|
||||
Angle.Zero,
|
||||
new Angle(Math.PI / 4),
|
||||
new Angle(Math.PI / 2),
|
||||
new Angle(Math.PI),
|
||||
new Angle(-Math.PI / 4),
|
||||
new Angle(-Math.PI / 2),
|
||||
new Angle(-Math.PI)
|
||||
})
|
||||
{
|
||||
|
||||
eyeManager.CurrentEye.Rotation = angle;
|
||||
|
||||
var worldAABB = eyeManager.GetWorldViewport();
|
||||
var worldPort = eyeManager.GetWorldViewbounds();
|
||||
Assert.That(worldAABB.EqualsApprox(worldPort.CalcBoundingBox()),
|
||||
$"Invalid EyeRotation bounds found for {angle}: Expected {worldAABB} and received {worldPort.CalcBoundingBox()}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.Graphics
|
||||
{
|
||||
[TestFixture, Parallelizable, TestOf(typeof(Eye))]
|
||||
sealed class EyeTest
|
||||
{
|
||||
[Test]
|
||||
public void Constructor_DefaultZoom_isTwo()
|
||||
{
|
||||
var eye = new Eye();
|
||||
|
||||
Assert.That(eye.Zoom, Is.Approximately(Vector2.One*2f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Constructor_DefaultPosition_isZero()
|
||||
{
|
||||
var eye = new Eye();
|
||||
|
||||
Assert.That(eye.Position, Is.EqualTo(MapCoordinates.Nullspace));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Constructor_DefaultDrawFov_isTrue()
|
||||
{
|
||||
var eye = new Eye();
|
||||
|
||||
Assert.That(eye.DrawFov, Is.True);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.Graphics
|
||||
{
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
[TestOf(typeof(StyleBox))]
|
||||
public sealed class StyleBoxTest
|
||||
{
|
||||
[Test]
|
||||
public void TestGetEnvelopBox()
|
||||
{
|
||||
var styleBox = new StyleBoxFlat();
|
||||
|
||||
Assert.That(
|
||||
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50), 1),
|
||||
Is.EqualTo(new UIBox2(0, 0, 50, 50)));
|
||||
|
||||
styleBox.ContentMarginLeftOverride = 3;
|
||||
styleBox.ContentMarginTopOverride = 5;
|
||||
styleBox.ContentMarginRightOverride = 7;
|
||||
styleBox.ContentMarginBottomOverride = 11;
|
||||
|
||||
Assert.That(
|
||||
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50), 1),
|
||||
Is.EqualTo(new UIBox2(0, 0, 60, 66)));
|
||||
|
||||
Assert.That(
|
||||
styleBox.GetEnvelopBox(new Vector2(10, 10), new Vector2(50, 50), 1),
|
||||
Is.EqualTo(new UIBox2(10, 10, 70, 76)));
|
||||
|
||||
Assert.That(
|
||||
styleBox.GetEnvelopBox(new Vector2(10, 10), new Vector2(50, 50), 2.0f),
|
||||
Is.EqualTo(new UIBox2(10, 10, 80, 92)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Graphics;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.UnitTesting.Client.Graphics
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class TextureLoadParametersTest
|
||||
{
|
||||
[Test]
|
||||
public void TestLoadEmptyYaml()
|
||||
{
|
||||
// Test whether it defaults for empty YAML.
|
||||
var yaml = new YamlMappingNode();
|
||||
var loaded = TextureLoadParameters.FromYaml(yaml);
|
||||
var defaultParams = TextureLoadParameters.Default;
|
||||
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(defaultParams.SampleParameters.Filter));
|
||||
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(defaultParams.SampleParameters.WrapMode));
|
||||
Assert.That(loaded.Srgb, Is.EqualTo(defaultParams.Srgb));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLoadEmptySamplingYaml()
|
||||
{
|
||||
// Test whether it defaults for empty YAML.
|
||||
var yaml = _getMapping("sample: {}\n");
|
||||
var loaded = TextureLoadParameters.FromYaml(yaml);
|
||||
var defaultParams = TextureLoadParameters.Default;
|
||||
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(defaultParams.SampleParameters.Filter));
|
||||
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(defaultParams.SampleParameters.WrapMode));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLoadYamlOne()
|
||||
{
|
||||
var yaml = _getMapping(TestDataOne);
|
||||
var loaded = TextureLoadParameters.FromYaml(yaml);
|
||||
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(true));
|
||||
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(TextureWrapMode.Repeat));
|
||||
Assert.That(loaded.Srgb, Is.EqualTo(false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLoadYamlTwo()
|
||||
{
|
||||
var yaml = _getMapping(TestDataTwo);
|
||||
var loaded = TextureLoadParameters.FromYaml(yaml);
|
||||
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(false));
|
||||
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(TextureWrapMode.MirroredRepeat));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLoadYamlThree()
|
||||
{
|
||||
var yaml = _getMapping(TestDataThree);
|
||||
var loaded = TextureLoadParameters.FromYaml(yaml);
|
||||
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(TextureWrapMode.None));
|
||||
}
|
||||
|
||||
private YamlMappingNode _getMapping(string data)
|
||||
{
|
||||
var yamlStream = new YamlStream();
|
||||
yamlStream.Load(new StringReader(data));
|
||||
return (YamlMappingNode) yamlStream.Documents[0].RootNode;
|
||||
}
|
||||
|
||||
private const string TestDataOne = @"
|
||||
sample:
|
||||
filter: true
|
||||
wrap: repeat
|
||||
srgb: false
|
||||
";
|
||||
|
||||
private const string TestDataTwo = @"
|
||||
sample:
|
||||
filter: false
|
||||
wrap: mirrored_repeat
|
||||
";
|
||||
|
||||
private const string TestDataThree = @"
|
||||
sample:
|
||||
wrap: none
|
||||
";
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Input;
|
||||
|
||||
namespace Robust.UnitTesting.Client.Input
|
||||
{
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public sealed class KeyboardTest
|
||||
{
|
||||
public static IEnumerable<object[]> TestCases =>
|
||||
((Keyboard.Key[]) Enum.GetValues(typeof(Keyboard.Key)))
|
||||
.Where(p => p.ToString().Contains("Mouse"))
|
||||
.Select(p => new object[] {p, true});
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
[TestCase(Keyboard.Key.A, false)]
|
||||
public void TestKeyboardIsMouseKey(Keyboard.Key key, bool isMouse)
|
||||
{
|
||||
Assert.That(key.IsMouseKey(), Is.EqualTo(isMouse));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.ResourceManagement;
|
||||
|
||||
namespace Robust.UnitTesting.Client.ResourceManagement
|
||||
{
|
||||
[TestOf(typeof(RSIResource))]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
[TestFixture]
|
||||
public sealed class RsiResourceTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple test in which the delays are already identical so nothing should change much.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFoldDelaysIdentical()
|
||||
{
|
||||
var delays = new[]
|
||||
{
|
||||
new float[] {5, 5, 5, 5},
|
||||
new float[] {5, 5, 5, 5},
|
||||
new float[] {5, 5, 5, 5},
|
||||
new float[] {5, 5, 5, 5},
|
||||
};
|
||||
|
||||
var (newDelays, indices) = RSIResource.FoldDelays(delays);
|
||||
|
||||
Assert.That(newDelays, Is.EqualTo(new[] {5f, 5f, 5f, 5f}));
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var o = i * 4;
|
||||
Assert.That(indices[i], Is.EqualTo(new[] {o, o + 1, o + 2, o + 3}));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test in which the delays are different but equal amount of frames.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFoldDelaysDifferent()
|
||||
{
|
||||
var delays = new[]
|
||||
{
|
||||
new float[] {5, 5, 5, 5},
|
||||
new float[] {5, 7, 3, 5},
|
||||
new float[] {5, 5, 5, 5},
|
||||
new float[] {5, 5, 5, 5},
|
||||
};
|
||||
|
||||
var (newDelays, indices) = RSIResource.FoldDelays(delays);
|
||||
|
||||
Assert.That(newDelays, Is.EqualTo(new[] {5f, 5f, 2f, 3f, 5f}));
|
||||
|
||||
Assert.That(indices[0], Is.EqualTo(new[] {0, 1, 2, 2, 3}));
|
||||
Assert.That(indices[1], Is.EqualTo(new[] {4, 5, 5, 6, 7}));
|
||||
Assert.That(indices[2], Is.EqualTo(new[] {8, 9, 10, 10, 11}));
|
||||
Assert.That(indices[3], Is.EqualTo(new[] {12, 13, 14, 14, 15}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test in which the delays are different but equal amount of frames.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFoldFramesDifferent()
|
||||
{
|
||||
var delays = new[]
|
||||
{
|
||||
new float[] {5, 5, 5, 5},
|
||||
new float[] {5, 5, 5, 5},
|
||||
new float[] {5, 5, 10},
|
||||
new float[] {5, 5, 5, 5},
|
||||
};
|
||||
|
||||
var (newDelays, indices) = RSIResource.FoldDelays(delays);
|
||||
|
||||
Assert.That(newDelays, Is.EqualTo(new[] {5f, 5f, 5f, 5f}));
|
||||
|
||||
Assert.That(indices[0], Is.EqualTo(new[] {0, 1, 2, 3}));
|
||||
Assert.That(indices[1], Is.EqualTo(new[] {4, 5, 6, 7}));
|
||||
Assert.That(indices[2], Is.EqualTo(new[] {8, 9, 10, 10}));
|
||||
Assert.That(indices[3], Is.EqualTo(new[] {11, 12, 13, 14}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The very complicated test where a ton of stuff goes down.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFoldWild()
|
||||
{
|
||||
var delays = new[]
|
||||
{
|
||||
new float[] {1, 9},
|
||||
new float[] {3, 3, 3, 1},
|
||||
new float[] {7, 1, 2},
|
||||
new float[] {5, 2, 2, 1},
|
||||
};
|
||||
|
||||
var (newDelays, indices) = RSIResource.FoldDelays(delays);
|
||||
|
||||
Assert.That(newDelays, Is.EqualTo(new[] {1f, 2f, 2f, 1f, 1f, 1f, 1f, 1f}));
|
||||
|
||||
Assert.That(indices[0], Is.EqualTo(new[] {0, 1, 1, 1, 1, 1, 1, 1}));
|
||||
Assert.That(indices[1], Is.EqualTo(new[] {2, 2, 3, 3, 4, 4, 4, 5}));
|
||||
Assert.That(indices[2], Is.EqualTo(new[] {6, 6, 6, 6, 6, 7, 8, 8}));
|
||||
Assert.That(indices[3], Is.EqualTo(new[] {9, 9, 9, 10, 10, 11, 11, 12}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.Sprite;
|
||||
|
||||
public sealed class SpriteBoundsTest : RobustIntegrationTest
|
||||
{
|
||||
private static readonly string Prototypes = @"
|
||||
- type: entity
|
||||
id: spriteBoundsTest1
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: debugRotation.rsi
|
||||
layers:
|
||||
- state: direction1
|
||||
|
||||
- type: entity
|
||||
id: spriteBoundsTest2
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: debugRotation.rsi
|
||||
layers:
|
||||
- state: direction1
|
||||
- state: direction1
|
||||
rotation: 0.1
|
||||
|
||||
- type: entity
|
||||
id: spriteBoundsTest3
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: debugRotation.rsi
|
||||
layers:
|
||||
- state: direction1
|
||||
- state: direction1
|
||||
rotation: 0.1
|
||||
visible: false
|
||||
";
|
||||
|
||||
[Test]
|
||||
public async Task TestSpriteBounds()
|
||||
{
|
||||
var client = StartClient(new ClientIntegrationOptions {ExtraPrototypes = Prototypes});
|
||||
await client.WaitIdleAsync();
|
||||
var baseClient = client.Resolve<IBaseClient>();
|
||||
|
||||
await client.WaitPost(() => baseClient.StartSinglePlayer());
|
||||
await client.WaitIdleAsync();
|
||||
|
||||
var entMan = client.EntMan;
|
||||
var sys = client.System<SpriteSystem>();
|
||||
|
||||
EntityUid uid1 = default;
|
||||
EntityUid uid2 = default;
|
||||
EntityUid uid3 = default;
|
||||
|
||||
await client.WaitPost(() =>
|
||||
{
|
||||
uid1 = entMan.Spawn("spriteBoundsTest1");
|
||||
uid2 = entMan.Spawn("spriteBoundsTest2");
|
||||
uid3 = entMan.Spawn("spriteBoundsTest3");
|
||||
});
|
||||
|
||||
var ent1 = new Entity<SpriteComponent>(uid1, entMan.GetComponent<SpriteComponent>(uid1));
|
||||
var ent2 = new Entity<SpriteComponent>(uid2, entMan.GetComponent<SpriteComponent>(uid2));
|
||||
var ent3 = new Entity<SpriteComponent>(uid3, entMan.GetComponent<SpriteComponent>(uid3));
|
||||
|
||||
// None of the entities have empty bounding boxes
|
||||
var box1 = sys.GetLocalBounds(ent1);
|
||||
var box2 = sys.GetLocalBounds(ent2);
|
||||
var box3 = sys.GetLocalBounds(ent3);
|
||||
Assert.That(!box1.EqualsApprox(Box2.Empty));
|
||||
Assert.That(!box2.EqualsApprox(Box2.Empty));
|
||||
Assert.That(!box3.EqualsApprox(Box2.Empty));
|
||||
|
||||
// ent2 should have a larger bb than ent1 as it has a visible rotated layer
|
||||
// ents 1 & 3 should have the same bounds, as the rotated layer is invisible in ent3
|
||||
Assert.That(box1.EqualsApprox(box3));
|
||||
Assert.That(!box1.EqualsApprox(box2));
|
||||
Assert.That(box2.EqualsApprox(ent2.Comp.Layers[1].Bounds));
|
||||
Assert.That(box2.Size.X, Is.GreaterThan(box1.Size.X));
|
||||
Assert.That(box2.Size.Y, Is.GreaterThan(box1.Size.Y));
|
||||
|
||||
// Toggling layer visibility updates the bounds
|
||||
sys.LayerSetVisible(ent2!, 1, false);
|
||||
sys.LayerSetVisible(ent3!, 1, true);
|
||||
|
||||
var newBox2 = sys.GetLocalBounds(ent2);
|
||||
var newBox3 = sys.GetLocalBounds(ent3);
|
||||
Assert.That(!newBox2.EqualsApprox(Box2.Empty));
|
||||
Assert.That(!newBox3.EqualsApprox(Box2.Empty));
|
||||
|
||||
Assert.That(box1.EqualsApprox(newBox2));
|
||||
Assert.That(!box1.EqualsApprox(newBox3));
|
||||
Assert.That(newBox3.EqualsApprox(ent3.Comp.Layers[1].Bounds));
|
||||
Assert.That(newBox3.Size.X, Is.GreaterThan(box1.Size.X));
|
||||
Assert.That(newBox3.Size.Y, Is.GreaterThan(box1.Size.Y));
|
||||
|
||||
// Changing the rotation, offset, scale, all trigger a bounds updatge
|
||||
sys.LayerSetRotation(ent3!, 1, Angle.Zero);
|
||||
var box = sys.GetLocalBounds(ent3);
|
||||
Assert.That(box1.EqualsApprox(box));
|
||||
|
||||
// scale
|
||||
sys.LayerSetScale(ent3!, 1, Vector2.One * 2);
|
||||
box = sys.GetLocalBounds(ent3);
|
||||
Assert.That(box1.EqualsApprox(box.Scale(0.5f)));
|
||||
sys.LayerSetScale(ent3!, 1, Vector2.One);
|
||||
box = sys.GetLocalBounds(ent3);
|
||||
Assert.That(box1.EqualsApprox(box));
|
||||
|
||||
// offset
|
||||
Assert.That(box.Center, Is.Approximately(Vector2.Zero));
|
||||
sys.LayerSetOffset(ent3!, 1, Vector2.One);
|
||||
box = sys.GetLocalBounds(ent3);
|
||||
Assert.That(box.Size.X, Is.GreaterThan(box1.Size.X));
|
||||
Assert.That(box.Size.Y, Is.GreaterThan(box1.Size.Y));
|
||||
Assert.That(box.Center.X, Is.GreaterThan(0));
|
||||
Assert.That(box.Center.Y, Is.GreaterThan(0));
|
||||
|
||||
await client.WaitPost(() =>
|
||||
{
|
||||
entMan.DeleteEntity(uid1);
|
||||
entMan.DeleteEntity(uid2);
|
||||
entMan.DeleteEntity(uid3);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Control))]
|
||||
public sealed class ControlTest : RobustUnitTest
|
||||
{
|
||||
private static readonly AttachedProperty _refTypeAttachedProperty
|
||||
= AttachedProperty.Create("_refType", typeof(ControlTest), typeof(string), "foo", v => (string?) v != "bar");
|
||||
|
||||
private static readonly AttachedProperty _valueTypeAttachedProperty
|
||||
= AttachedProperty.Create("_valueType", typeof(ControlTest), typeof(float));
|
||||
|
||||
private static readonly AttachedProperty _nullableAttachedProperty
|
||||
= AttachedProperty.Create("_nullable", typeof(ControlTest), typeof(float?));
|
||||
|
||||
private static readonly AttachedProperty<int> _genericProperty =
|
||||
AttachedProperty<int>.Create("generic", typeof(ControlTest), 5, i => i % 2 == 1);
|
||||
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that you can't parent a control to its (grand)child.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNoRecursion()
|
||||
{
|
||||
var control1 = new Control();
|
||||
var control2 = new Control();
|
||||
var control3 = new Control();
|
||||
|
||||
control1.AddChild(control2);
|
||||
// Test direct parent/child.
|
||||
Assert.That(() => control2.AddChild(control1), Throws.ArgumentException);
|
||||
|
||||
control2.AddChild(control3);
|
||||
// Test grand child.
|
||||
Assert.That(() => control3.AddChild(control1), Throws.ArgumentException);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVisibleInTree()
|
||||
{
|
||||
var control1 = new Control();
|
||||
|
||||
// Not visible because not parented to root control.
|
||||
Assert.That(control1.Visible, Is.True);
|
||||
Assert.That(control1.VisibleInTree, Is.False);
|
||||
|
||||
control1.UserInterfaceManager.RootControl.AddChild(control1);
|
||||
Assert.That(control1.Visible, Is.True);
|
||||
Assert.That(control1.VisibleInTree, Is.True);
|
||||
|
||||
control1.Visible = false;
|
||||
Assert.That(control1.Visible, Is.False);
|
||||
Assert.That(control1.VisibleInTree, Is.False);
|
||||
control1.Visible = true;
|
||||
|
||||
var control2 = new Control();
|
||||
Assert.That(control2.VisibleInTree, Is.False);
|
||||
|
||||
control1.AddChild(control2);
|
||||
Assert.That(control2.VisibleInTree, Is.True);
|
||||
|
||||
control1.Visible = false;
|
||||
Assert.That(control2.VisibleInTree, Is.False);
|
||||
|
||||
control2.Visible = false;
|
||||
Assert.That(control2.VisibleInTree, Is.False);
|
||||
|
||||
control1.Visible = true;
|
||||
Assert.That(control2.VisibleInTree, Is.False);
|
||||
|
||||
control1.Orphan();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAttachedPropertiesBasic()
|
||||
{
|
||||
var control = new Control();
|
||||
|
||||
control.SetValue(_refTypeAttachedProperty, "honk");
|
||||
|
||||
Assert.That(control.GetValue(_refTypeAttachedProperty), Is.EqualTo("honk"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAttachedPropertiesValidate()
|
||||
{
|
||||
var control = new Control();
|
||||
|
||||
Assert.Throws<ArgumentException>(() => control.SetValue(_refTypeAttachedProperty, "bar"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAttachedPropertiesInvalidType()
|
||||
{
|
||||
var control = new Control();
|
||||
|
||||
Assert.Throws<ArgumentException>(() => control.SetValue(_refTypeAttachedProperty, new object()));
|
||||
Assert.Throws<ArgumentException>(() => control.SetValue(_valueTypeAttachedProperty, new object()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAttachedPropertiesInvalidNull()
|
||||
{
|
||||
var control = new Control();
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => control.SetValue(_valueTypeAttachedProperty, null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAttachedPropertiesValidNull()
|
||||
{
|
||||
var control = new Control();
|
||||
|
||||
control.SetValue(_nullableAttachedProperty, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAttachedPropertiesGeneric()
|
||||
{
|
||||
var control = new Control();
|
||||
|
||||
Assert.That(control.GetValue(_genericProperty), Is.EqualTo(5));
|
||||
|
||||
control.SetValue(_genericProperty, 11);
|
||||
|
||||
Assert.That(control.GetValue(_genericProperty), Is.EqualTo(11));
|
||||
|
||||
Assert.That(() => control.SetValue(_genericProperty, 10), Throws.ArgumentException);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAnimations()
|
||||
{
|
||||
var control = new TestControl();
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(3),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackControlProperty
|
||||
{
|
||||
Property = nameof(TestControl.Foo),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(1f, 1f),
|
||||
new AnimationTrackProperty.KeyFrame(3f, 2f)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
control.PlayAnimation(animation, "foo");
|
||||
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
|
||||
|
||||
Assert.That(control.Foo, new ApproxEqualityConstraint(0f)); // Should still be 0.
|
||||
|
||||
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5001f));
|
||||
|
||||
Assert.That(control.Foo, new ApproxEqualityConstraint(1f, 0.01)); // Should now be 1.
|
||||
|
||||
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
|
||||
|
||||
Assert.That(control.Foo, new ApproxEqualityConstraint(1.5f, 0.01)); // Should now be 1.5.
|
||||
|
||||
control.DoFrameUpdateRecursive(new FrameEventArgs(1.0f));
|
||||
|
||||
Assert.That(control.Foo, new ApproxEqualityConstraint(2.5f, 0.01)); // Should now be 2.5.
|
||||
|
||||
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
|
||||
|
||||
Assert.That(control.Foo, new ApproxEqualityConstraint(3f, 0.01)); // Should now be 3.
|
||||
|
||||
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
|
||||
|
||||
Assert.That(control.Foo, new ApproxEqualityConstraint(3f, 0.01)); // Should STILL be 3.
|
||||
}
|
||||
|
||||
private sealed class TestControl : Control
|
||||
{
|
||||
[Animatable] public float Foo { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class BaseButtonTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
private string UIRightClick = "UIRightClick";
|
||||
private string OpenContextMenu = "UseSecondary";
|
||||
|
||||
private sealed class TestButton : BaseButton { }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToggleButtonToggle()
|
||||
{
|
||||
var button = new TestButton
|
||||
{
|
||||
ToggleMode = true
|
||||
};
|
||||
var toggled = false;
|
||||
|
||||
button.OnToggled += _ =>
|
||||
{
|
||||
toggled = true;
|
||||
};
|
||||
|
||||
var click = new GUIBoundKeyEventArgs(EngineKeyFunctions.UIClick, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
|
||||
button.KeyBindDown(click);
|
||||
button.KeyBindUp(click);
|
||||
Assert.That(button.Pressed, Is.EqualTo(true));
|
||||
Assert.That(toggled, Is.EqualTo(true));
|
||||
|
||||
toggled = false;
|
||||
button.KeyBindDown(click);
|
||||
button.KeyBindUp(click);
|
||||
Assert.That(button.Pressed, Is.EqualTo(false));
|
||||
Assert.That(toggled, Is.EqualTo(true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToggleButtonAllKeybinds()
|
||||
{
|
||||
var button = new TestButton
|
||||
{
|
||||
EnableAllKeybinds = true,
|
||||
ToggleMode = true
|
||||
};
|
||||
|
||||
var uiRightClickPressed = false;
|
||||
var openContextMenuPressed = false;
|
||||
var toggled = false;
|
||||
button.OnPressed += args =>
|
||||
{
|
||||
if (args.Event.Function == UIRightClick)
|
||||
uiRightClickPressed = true;
|
||||
else if (args.Event.Function == OpenContextMenu)
|
||||
openContextMenuPressed = true;
|
||||
};
|
||||
button.OnToggled += _ =>
|
||||
{
|
||||
toggled = true;
|
||||
};
|
||||
|
||||
var uiRightClick = new GUIBoundKeyEventArgs(UIRightClick, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
|
||||
var openContextMenu = new GUIBoundKeyEventArgs(OpenContextMenu, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
|
||||
button.KeyBindDown(uiRightClick);
|
||||
button.KeyBindDown(openContextMenu);
|
||||
|
||||
Assert.That(button.Pressed, Is.EqualTo(false));
|
||||
Assert.That(toggled, Is.EqualTo(false));
|
||||
button.KeyBindUp(uiRightClick);
|
||||
Assert.That(button.Pressed, Is.EqualTo(false));
|
||||
Assert.That(toggled, Is.EqualTo(false));
|
||||
button.KeyBindUp(openContextMenu);
|
||||
|
||||
Assert.That(uiRightClickPressed, Is.EqualTo(true));
|
||||
Assert.That(openContextMenuPressed, Is.EqualTo(true));
|
||||
Assert.That(button.Pressed, Is.EqualTo(false));
|
||||
Assert.That(toggled, Is.EqualTo(false));
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(BoxContainer))]
|
||||
public sealed class BoxContainerTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[Test]
|
||||
public void TestLayoutBasic()
|
||||
{
|
||||
var root = new LayoutContainer();
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = new Vector2(50, 60)
|
||||
};
|
||||
var control1 = new Control {MinSize = new Vector2(20, 20)};
|
||||
var control2 = new Control {MinSize = new Vector2(30, 30)};
|
||||
|
||||
root.AddChild(boxContainer);
|
||||
|
||||
boxContainer.AddChild(control1);
|
||||
boxContainer.AddChild(control2);
|
||||
|
||||
root.Arrange(new UIBox2(0, 0, 50, 60));
|
||||
|
||||
Assert.That(control1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(control1.Size, Is.EqualTo(new Vector2(50, 20)));
|
||||
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 20)));
|
||||
Assert.That(control2.Size, Is.EqualTo(new Vector2(50, 30)));
|
||||
Assert.That(boxContainer.DesiredSize, Is.EqualTo(new Vector2(50, 60)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLayoutExpand()
|
||||
{
|
||||
var root = new LayoutContainer();
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = new Vector2(50, 60)
|
||||
};
|
||||
var control1 = new Control
|
||||
{
|
||||
VerticalExpand = true
|
||||
};
|
||||
var control2 = new Control {MinSize = new Vector2(30, 30)};
|
||||
|
||||
boxContainer.AddChild(control1);
|
||||
boxContainer.AddChild(control2);
|
||||
|
||||
root.AddChild(boxContainer);
|
||||
|
||||
root.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(control1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(control1.Size, Is.EqualTo(new Vector2(50, 30)));
|
||||
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 30)));
|
||||
Assert.That(control2.Size, Is.EqualTo(new Vector2(50, 30)));
|
||||
Assert.That(boxContainer.DesiredSize, Is.EqualTo(new Vector2(50, 60)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCalcMinSize()
|
||||
{
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
var control1 = new Control
|
||||
{
|
||||
MinSize = new Vector2(50, 30)
|
||||
};
|
||||
var control2 = new Control {MinSize = new Vector2(30, 50)};
|
||||
|
||||
boxContainer.AddChild(control1);
|
||||
boxContainer.AddChild(control2);
|
||||
|
||||
boxContainer.Measure(new Vector2(100, 100));
|
||||
|
||||
Assert.That(boxContainer.DesiredSize, Is.EqualTo(new Vector2(50, 80)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTwoExpand()
|
||||
{
|
||||
var root = new LayoutContainer();
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = new Vector2(30, 80)
|
||||
};
|
||||
var control1 = new Control
|
||||
{
|
||||
VerticalExpand = true,
|
||||
};
|
||||
var control2 = new Control
|
||||
{
|
||||
VerticalExpand = true,
|
||||
};
|
||||
var control3 = new Control {MinSize = new Vector2(0, 50)};
|
||||
|
||||
root.AddChild(boxContainer);
|
||||
|
||||
boxContainer.AddChild(control1);
|
||||
boxContainer.AddChild(control3);
|
||||
boxContainer.AddChild(control2);
|
||||
|
||||
root.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(control1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(control1.Size, Is.EqualTo(new Vector2(30, 15)));
|
||||
Assert.That(control3.Position, Is.EqualTo(new Vector2(0, 15)));
|
||||
Assert.That(control3.Size, Is.EqualTo(new Vector2(30, 50)));
|
||||
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 65)));
|
||||
Assert.That(control2.Size, Is.EqualTo(new Vector2(30, 15)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTwoExpandRatio()
|
||||
{
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SetSize = new Vector2(100, 10),
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
MinWidth = 10,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 20,
|
||||
},
|
||||
new Control
|
||||
{
|
||||
MinWidth = 10,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 80
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(20));
|
||||
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(80));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTwoExpandOneSmall()
|
||||
{
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SetSize = new Vector2(100, 10),
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
MinWidth = 30,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 20,
|
||||
},
|
||||
new Control
|
||||
{
|
||||
MinWidth = 30,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 80
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(30));
|
||||
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(70));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(CenterContainer))]
|
||||
public sealed class CenterContainerTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
var container = new CenterContainer();
|
||||
var child = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
container.AddChild(child);
|
||||
|
||||
container.Arrange(UIBox2.FromDimensions(0, 0, 100, 100));
|
||||
|
||||
Assert.That(container.DesiredSize, Is.EqualTo(new Vector2(50, 50)));
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(25, 25)));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(GridContainer))]
|
||||
public sealed class GridContainerTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestBasic(bool limitByCount)
|
||||
{
|
||||
var grid = limitByCount ? new GridContainer {Columns = 2} : new GridContainer {MaxGridWidth = 125};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(104, 158)));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(54, 0)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
|
||||
Assert.That(child5.Position, Is.EqualTo(new Vector2(0, 108)));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestBasicRows(bool limitByCount)
|
||||
{
|
||||
var grid = limitByCount
|
||||
? new GridContainer {Rows = 2}
|
||||
: new GridContainer {MaxGridHeight = 125};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(158, 104)));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(0, 54)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
|
||||
Assert.That(child5.Position, Is.EqualTo(new Vector2(108, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnevenLimitSize()
|
||||
{
|
||||
// when uneven sizes are used and limiting by size, they should all be treated as equal size cells based on the
|
||||
// max minwidth / minheight among them.
|
||||
// Note that when limiting by count, the behavior is different - rows and columns are individually
|
||||
// expanded based on the max size of their elements
|
||||
var grid = new GridContainer {MaxGridWidth = 125};
|
||||
var child1 = new Control {MinSize = new Vector2(12, 24)};
|
||||
var child2 = new Control {MinSize = new Vector2(30, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(40, 20)};
|
||||
var child4 = new Control {MinSize = new Vector2(20, 12)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 10)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(104, 158)));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(54, 0)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
|
||||
Assert.That(child5.Position, Is.EqualTo(new Vector2(0, 108)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnevenLimitSizeRows()
|
||||
{
|
||||
var grid = new GridContainer {MaxGridHeight = 125};
|
||||
var child1 = new Control {MinSize = new Vector2(12, 2)};
|
||||
var child2 = new Control {MinSize = new Vector2(5, 23)};
|
||||
var child3 = new Control {MinSize = new Vector2(42, 4)};
|
||||
var child4 = new Control {MinSize = new Vector2(2, 50)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 34)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(158, 104)));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(0, 54)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
|
||||
Assert.That(child5.Position, Is.EqualTo(new Vector2(108, 0)));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestBasicBackwards(bool limitByCount)
|
||||
{
|
||||
var grid = limitByCount
|
||||
? new GridContainer {Columns = 2, ExpandBackwards = true}
|
||||
: new GridContainer {MaxGridWidth = 125, ExpandBackwards = true};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(104, 158)));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(new Vector2(0, 108)));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(54, 108)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
|
||||
Assert.That(child5.Position, Is.EqualTo(Vector2.Zero));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestBasicRowsBackwards(bool limitByCount)
|
||||
{
|
||||
var grid = limitByCount
|
||||
? new GridContainer {Rows = 2, ExpandBackwards = true}
|
||||
: new GridContainer {MaxGridHeight = 125, ExpandBackwards = true};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(158, 104)));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(new Vector2(108, 0)));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(108, 54)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
|
||||
Assert.That(child5.Position, Is.EqualTo(Vector2.Zero));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestExpand(bool limitByCount)
|
||||
{
|
||||
// in the presence of a MaxWidth with expanding elements, the
|
||||
// pre-expanded size should be used to determine the size of each "cell", and then expansion
|
||||
// happens within the defined control size
|
||||
var grid = limitByCount
|
||||
? new GridContainer {Columns = 2, SetSize = new Vector2(200, 200)}
|
||||
: new GridContainer {MaxGridWidth = 125, SetSize = new Vector2(200, 200)};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50), HorizontalExpand = true};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50), VerticalExpand = true};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(child1.Size, Is.EqualTo(new Vector2(146, 50)));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(150, 0)));
|
||||
Assert.That(child2.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
|
||||
Assert.That(child3.Size, Is.EqualTo(new Vector2(146, 92)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(150, 54)));
|
||||
Assert.That(child4.Size, Is.EqualTo(new Vector2(50, 92)));
|
||||
Assert.That(child5.Position, Is.EqualTo(new Vector2(0, 150)));
|
||||
Assert.That(child5.Size, Is.EqualTo(new Vector2(146, 50)));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestExpandRows(bool limitByCount)
|
||||
{
|
||||
var grid = limitByCount
|
||||
? new GridContainer {Rows = 2, SetSize = new Vector2(200, 200)}
|
||||
: new GridContainer {MaxGridHeight = 125, SetSize = new Vector2(200, 200)};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50), VerticalExpand = true};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50), HorizontalExpand = true};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Arrange(new UIBox2(0, 0, 250, 250));
|
||||
|
||||
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(child1.Size, Is.EqualTo(new Vector2(50, 146)));
|
||||
Assert.That(child2.Position, Is.EqualTo(new Vector2(0, 150)));
|
||||
Assert.That(child2.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
|
||||
Assert.That(child3.Size, Is.EqualTo(new Vector2(92, 146)));
|
||||
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 150)));
|
||||
Assert.That(child4.Size, Is.EqualTo(new Vector2(92, 50)));
|
||||
Assert.That(child5.Position, Is.EqualTo(new Vector2(150, 0)));
|
||||
Assert.That(child5.Size, Is.EqualTo(new Vector2(50, 146)));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestRowCount(bool limitByCount)
|
||||
{
|
||||
var grid = limitByCount
|
||||
? new GridContainer {Columns = 2}
|
||||
: new GridContainer {MaxGridWidth = 125};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Measure(new Vector2(250, 250));
|
||||
|
||||
Assert.That(grid.Rows, Is.EqualTo(3));
|
||||
|
||||
grid.RemoveChild(child5);
|
||||
|
||||
Assert.That(grid.Rows, Is.EqualTo(2));
|
||||
|
||||
grid.DisposeAllChildren();
|
||||
|
||||
Assert.That(grid.Rows, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestColumnCountRows(bool limitByCount)
|
||||
{
|
||||
var grid = limitByCount
|
||||
? new GridContainer {Rows = 2}
|
||||
: new GridContainer {MaxGridHeight = 125};
|
||||
var child1 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child2 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child3 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child4 = new Control {MinSize = new Vector2(50, 50)};
|
||||
var child5 = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
grid.AddChild(child1);
|
||||
grid.AddChild(child2);
|
||||
grid.AddChild(child3);
|
||||
grid.AddChild(child4);
|
||||
grid.AddChild(child5);
|
||||
|
||||
grid.Measure(new Vector2(250, 250));
|
||||
|
||||
Assert.That(grid.Columns, Is.EqualTo(3));
|
||||
|
||||
grid.RemoveChild(child5);
|
||||
|
||||
Assert.That(grid.Columns, Is.EqualTo(2));
|
||||
|
||||
grid.DisposeAllChildren();
|
||||
|
||||
Assert.That(grid.Columns, Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(LayoutContainer))]
|
||||
public sealed class LayoutContainerTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[Test]
|
||||
public void TestMarginLayoutBasic()
|
||||
{
|
||||
var control = new LayoutContainer {Size = new Vector2(100, 100)};
|
||||
var child = new Control();
|
||||
|
||||
LayoutContainer.SetMarginRight(child, 5);
|
||||
LayoutContainer.SetMarginBottom(child, 5);
|
||||
control.AddChild(child);
|
||||
|
||||
control.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(5, 5)));
|
||||
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
|
||||
|
||||
LayoutContainer.SetMarginTop(child, 3);
|
||||
LayoutContainer.SetMarginLeft(child, 3);
|
||||
|
||||
control.InvalidateArrange();
|
||||
control.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(2, 2)));
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(3, 3)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAnchorLayoutBasic()
|
||||
{
|
||||
var control = new LayoutContainer {Size = new Vector2(100, 100)};
|
||||
var child = new Control();
|
||||
LayoutContainer.SetAnchorRight(child, 1);
|
||||
LayoutContainer.SetAnchorBottom(child, 1);
|
||||
control.AddChild(child);
|
||||
|
||||
control.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
|
||||
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
|
||||
|
||||
LayoutContainer.SetAnchorLeft(child, 0.5f);
|
||||
control.InvalidateArrange();
|
||||
control.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(50, 0)));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 100)));
|
||||
LayoutContainer.SetAnchorTop(child, 0.5f);
|
||||
control.InvalidateArrange();
|
||||
control.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(50, 50)));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarginLayoutMinimumSize()
|
||||
{
|
||||
var control = new LayoutContainer {Size = new Vector2(100, 100)};
|
||||
var child = new Control
|
||||
{
|
||||
MinSize = new Vector2(50, 50),
|
||||
};
|
||||
|
||||
LayoutContainer.SetMarginRight(child, 20);
|
||||
LayoutContainer.SetMarginBottom(child, 20);
|
||||
|
||||
control.AddChild(child);
|
||||
control.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarginAnchorLayout()
|
||||
{
|
||||
var control = new LayoutContainer {Size = new Vector2(100, 100)};
|
||||
var child = new Control();
|
||||
|
||||
LayoutContainer.SetMarginRight(child, -10);
|
||||
LayoutContainer.SetMarginBottom(child, -10);
|
||||
LayoutContainer.SetMarginTop(child, 10);
|
||||
LayoutContainer.SetMarginLeft(child, 10);
|
||||
LayoutContainer.SetAnchorRight(child, 1);
|
||||
LayoutContainer.SetAnchorBottom(child, 1);
|
||||
|
||||
control.AddChild(child);
|
||||
control.InvalidateArrange();
|
||||
control.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(10, 10)));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(80, 80)));
|
||||
}
|
||||
|
||||
// Test that a control grows its size instead of position by default. (GrowDirection.End)
|
||||
[Test]
|
||||
public void TestGrowEnd()
|
||||
{
|
||||
var parent = new LayoutContainer();
|
||||
var child = new Control {SetSize = new Vector2(100, 100)};
|
||||
|
||||
LayoutContainer.SetAnchorRight(child, 1);
|
||||
|
||||
parent.AddChild(child);
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
// Child should be at 0,0.
|
||||
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
|
||||
|
||||
// Right margin should make the child not have enough space and grow left.
|
||||
LayoutContainer.SetMarginRight(child, -100);
|
||||
parent.InvalidateArrange();
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
|
||||
}
|
||||
|
||||
// Test GrowDirection.Begin
|
||||
[Test]
|
||||
public void TestGrowBegin()
|
||||
{
|
||||
var parent = new LayoutContainer();
|
||||
var child = new Control {SetSize = new Vector2(100, 100)};
|
||||
|
||||
LayoutContainer.SetGrowHorizontal(child, LayoutContainer.GrowDirection.Begin);
|
||||
LayoutContainer.SetAnchorRight(child, 1);
|
||||
|
||||
parent.AddChild(child);
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
// Child should be at 0,0.
|
||||
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
|
||||
|
||||
// Right margin should make the child not have enough space and grow left.
|
||||
LayoutContainer.SetMarginRight(child, -100);
|
||||
parent.InvalidateArrange();
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(-100, 0)));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
|
||||
}
|
||||
|
||||
// Test GrowDirection.Both
|
||||
[Test]
|
||||
public void TestGrowBoth()
|
||||
{
|
||||
var parent = new LayoutContainer {MinSize = new Vector2(100, 100)};
|
||||
var child = new Control {SetSize = new Vector2(100, 100)};
|
||||
|
||||
LayoutContainer.SetGrowHorizontal(child, LayoutContainer.GrowDirection.Both);
|
||||
LayoutContainer.SetAnchorRight(child, 1);
|
||||
|
||||
parent.AddChild(child);
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
// Child should be at 0,0.
|
||||
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
|
||||
|
||||
// Right margin should make the child not have enough space and grow left.
|
||||
LayoutContainer.SetMarginRight(child, -100);
|
||||
parent.InvalidateArrange();
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(-50, 0)));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
|
||||
}
|
||||
|
||||
// Test that changing a grow direction updates the position correctly.
|
||||
[Test]
|
||||
public void TestGrowDirectionChange()
|
||||
{
|
||||
var parent = new LayoutContainer {MinSize = new Vector2(100, 100)};
|
||||
var child = new Control();
|
||||
parent.AddChild(child);
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
// Child should be at -100,0.
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(0, 0)));
|
||||
|
||||
child.MinSize = new Vector2(100, 100);
|
||||
parent.InvalidateMeasure();
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(0, 0)));
|
||||
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
|
||||
|
||||
LayoutContainer.SetGrowHorizontal(child, LayoutContainer.GrowDirection.Begin);
|
||||
parent.InvalidateArrange();
|
||||
parent.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(child.Position, Is.EqualTo(new Vector2(-100, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(LineEdit))]
|
||||
public sealed class LineEditTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[Test]
|
||||
public void TestRuneBackspace()
|
||||
{
|
||||
var lineEdit = new TestLineEdit();
|
||||
|
||||
lineEdit.Text = "Foo👏";
|
||||
lineEdit.CursorPosition = lineEdit.Text.Length;
|
||||
|
||||
var eventArgs = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.TextBackspace,
|
||||
BoundKeyState.Down,
|
||||
default, false, default, default);
|
||||
lineEdit.KeyBindDown(eventArgs);
|
||||
|
||||
Assert.That(lineEdit.Text, Is.EqualTo("Foo"));
|
||||
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRuneDelete()
|
||||
{
|
||||
var lineEdit = new TestLineEdit();
|
||||
|
||||
lineEdit.Text = "Foo👏";
|
||||
lineEdit.CursorPosition = 3;
|
||||
|
||||
var eventArgs = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.TextDelete,
|
||||
BoundKeyState.Down,
|
||||
default, false, default, default);
|
||||
lineEdit.KeyBindDown(eventArgs);
|
||||
|
||||
Assert.That(lineEdit.Text, Is.EqualTo("Foo"));
|
||||
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMoveLeft()
|
||||
{
|
||||
var lineEdit = new TestLineEdit();
|
||||
|
||||
lineEdit.Text = "Foo👏";
|
||||
lineEdit.CursorPosition = lineEdit.Text.Length;
|
||||
|
||||
var eventArgs = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.TextCursorLeft,
|
||||
BoundKeyState.Down,
|
||||
default, false, default, default);
|
||||
lineEdit.KeyBindDown(eventArgs);
|
||||
|
||||
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
|
||||
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMoveRight()
|
||||
{
|
||||
var lineEdit = new TestLineEdit();
|
||||
|
||||
lineEdit.Text = "Foo👏";
|
||||
lineEdit.CursorPosition = 3;
|
||||
|
||||
var eventArgs = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.TextCursorRight,
|
||||
BoundKeyState.Down,
|
||||
default, false, default, default);
|
||||
lineEdit.KeyBindDown(eventArgs);
|
||||
|
||||
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
|
||||
Assert.That(lineEdit.CursorPosition, Is.EqualTo(5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMoveSelectLeft()
|
||||
{
|
||||
var lineEdit = new TestLineEdit();
|
||||
|
||||
lineEdit.Text = "Foo👏";
|
||||
lineEdit.CursorPosition = lineEdit.Text.Length;
|
||||
|
||||
var eventArgs = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.TextCursorSelectLeft,
|
||||
BoundKeyState.Down,
|
||||
default, false, default, default);
|
||||
lineEdit.KeyBindDown(eventArgs);
|
||||
|
||||
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
|
||||
Assert.That(lineEdit.SelectionStart, Is.EqualTo(5));
|
||||
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMoveSelectRight()
|
||||
{
|
||||
var lineEdit = new TestLineEdit();
|
||||
|
||||
lineEdit.Text = "Foo👏";
|
||||
lineEdit.CursorPosition = 3;
|
||||
|
||||
var eventArgs = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.TextCursorSelectRight,
|
||||
BoundKeyState.Down,
|
||||
default, false, default, default);
|
||||
lineEdit.KeyBindDown(eventArgs);
|
||||
|
||||
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
|
||||
Assert.That(lineEdit.SelectionStart, Is.EqualTo(3));
|
||||
Assert.That(lineEdit.CursorPosition, Is.EqualTo(5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
// RIGHT
|
||||
[TestCase("Foo Bar Baz", false, 0, ExpectedResult = 3)]
|
||||
[TestCase("Foo Bar Baz", false, 8, ExpectedResult = 11)]
|
||||
[TestCase("Foo[Bar[Baz", false, 0, ExpectedResult = 3)]
|
||||
[TestCase("Foo[Bar[Baz", false, 3, ExpectedResult = 4)]
|
||||
[TestCase("Foo^Bar^Baz", false, 0, ExpectedResult = 3)]
|
||||
[TestCase("Foo^Bar^Baz", false, 3, ExpectedResult = 5)]
|
||||
[TestCase("Foo^^^Bar^Baz", false, 3, ExpectedResult = 9)]
|
||||
[TestCase("^^^ ^^^", false, 0, ExpectedResult = 6)]
|
||||
[TestCase("^^^ ^^^", false, 7, ExpectedResult = 13)]
|
||||
// LEFT
|
||||
[TestCase("Foo Bar Baz", true, 4, ExpectedResult = 0)]
|
||||
[TestCase("Foo Bar Baz", true, 11, ExpectedResult = 8)]
|
||||
[TestCase("Foo[Bar[Baz", true, 3, ExpectedResult = 0)]
|
||||
[TestCase("Foo[Bar[Baz", true, 4, ExpectedResult = 3)]
|
||||
[TestCase("Foo^Bar^Baz", true, 3, ExpectedResult = 0)]
|
||||
[TestCase("Foo^Bar^Baz", true, 5, ExpectedResult = 3)]
|
||||
[TestCase("Foo^^^Bar^Baz", true, 9, ExpectedResult = 3)]
|
||||
[TestCase("^^^ ^^^", true, 7, ExpectedResult = 0)]
|
||||
[TestCase("^^^ ^^^", true, 13, ExpectedResult = 7)]
|
||||
public int TestMoveWord(string text, bool left, int initCursorPos)
|
||||
{
|
||||
// ^ is replaced by 👏 because Rider refuses to run the tests otherwise.
|
||||
text = text.Replace("^", "👏");
|
||||
|
||||
var lineEdit = new TestLineEdit();
|
||||
|
||||
lineEdit.Text = text;
|
||||
lineEdit.CursorPosition = initCursorPos;
|
||||
|
||||
var eventArgs = new GUIBoundKeyEventArgs(
|
||||
left ? EngineKeyFunctions.TextCursorWordLeft : EngineKeyFunctions.TextCursorWordRight,
|
||||
BoundKeyState.Down,
|
||||
default, false, default, default);
|
||||
lineEdit.KeyBindDown(eventArgs);
|
||||
|
||||
return lineEdit.CursorPosition;
|
||||
}
|
||||
|
||||
|
||||
private sealed class TestLineEdit : LineEdit
|
||||
{
|
||||
public override bool HasKeyboardFocus()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(MultiselectOptionButton<>))]
|
||||
public sealed class MultiselectOptionButtonTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[Test]
|
||||
public void TestAddRemoveItem()
|
||||
{
|
||||
var uiManager = IoCManager.Resolve<IUserInterfaceManagerInternal>();
|
||||
uiManager.InitializeTesting();
|
||||
var optionButton = new MultiselectOptionButton<string>();
|
||||
optionButton.AddItem("Label1", "Key1");
|
||||
optionButton.AddItem("Label2", "Key2");
|
||||
optionButton.AddItem("Label3", "Key3");
|
||||
|
||||
Assert.That(optionButton.GetIdx("Key1"), Is.EqualTo(0));
|
||||
Assert.That(optionButton.GetIdx("Key2"), Is.EqualTo(1));
|
||||
Assert.That(optionButton.GetIdx("Key3"), Is.EqualTo(2));
|
||||
Assert.That(optionButton.GetItemKey(0), Is.EqualTo("Key1"));
|
||||
Assert.That(optionButton.GetItemKey(1), Is.EqualTo("Key2"));
|
||||
Assert.That(optionButton.GetItemKey(2), Is.EqualTo("Key3"));
|
||||
|
||||
optionButton.RemoveItem(1);
|
||||
|
||||
Assert.That(optionButton.GetIdx("Key1"), Is.EqualTo(0));
|
||||
Assert.That(optionButton.GetIdx("Key3"), Is.EqualTo(1));
|
||||
Assert.That(optionButton.GetItemKey(0), Is.EqualTo("Key1"));
|
||||
Assert.That(optionButton.GetItemKey(1), Is.EqualTo("Key3"));
|
||||
|
||||
optionButton.RemoveItem(0);
|
||||
|
||||
Assert.That(optionButton.GetIdx("Key3"), Is.EqualTo(0));
|
||||
Assert.That(optionButton.GetItemKey(0), Is.EqualTo("Key3"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(PopupContainer))]
|
||||
public sealed class PopupContainerTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
var container = new PopupContainer {MinSize = new Vector2(100, 100)};
|
||||
|
||||
container.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
var popup = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
container.AddChild(popup);
|
||||
|
||||
PopupContainer.SetPopupOrigin(popup, new Vector2(25, 25));
|
||||
|
||||
container.InvalidateArrange();
|
||||
container.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(popup.Position, Is.EqualTo(new Vector2(25, 25)));
|
||||
Assert.That(popup.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
|
||||
// Test that pos gets pushed back to the top left if the size + offset is too large to fit.
|
||||
PopupContainer.SetPopupOrigin(popup, new Vector2(75, 75));
|
||||
|
||||
container.InvalidateArrange();
|
||||
container.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(popup.Position, Is.EqualTo(new Vector2(50, 50)));
|
||||
Assert.That(popup.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
|
||||
// Test that pos = 0 if the popup is too large to fit.
|
||||
popup.MinSize = new Vector2(150, 150);
|
||||
|
||||
container.InvalidateArrange();
|
||||
container.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(popup.Position, Is.EqualTo(new Vector2(0, 0)));
|
||||
Assert.That(popup.Size, Is.EqualTo(new Vector2(150, 150)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAltPos()
|
||||
{
|
||||
var container = new PopupContainer {MinSize = new Vector2(100, 100)};
|
||||
|
||||
container.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
var popup = new Control {MinSize = new Vector2(50, 50)};
|
||||
|
||||
container.AddChild(popup);
|
||||
|
||||
PopupContainer.SetPopupOrigin(popup, new Vector2(75, 75));
|
||||
PopupContainer.SetAltOrigin(popup, new Vector2(65, 25));
|
||||
|
||||
container.InvalidateArrange();
|
||||
container.Arrange(new UIBox2(0, 0, 100, 100));
|
||||
|
||||
Assert.That(popup.Position, Is.EqualTo(new Vector2(15, 25)));
|
||||
Assert.That(popup.Size, Is.EqualTo(new Vector2(50, 50)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(RadioOptions<int>))]
|
||||
public sealed class RadioOptionsTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[Test]
|
||||
public void TestDefaultInvoke()
|
||||
{
|
||||
//Arrange
|
||||
RadioOptions<int> _optionButton = new RadioOptions<int>(RadioOptionsLayout.Horizontal);
|
||||
|
||||
int itemId = _optionButton.AddItem("High", 1);
|
||||
|
||||
int countSelected = 0;
|
||||
_optionButton.OnItemSelected += args =>
|
||||
{
|
||||
countSelected++;
|
||||
};
|
||||
|
||||
//Act
|
||||
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId, _optionButton));
|
||||
|
||||
//Assert
|
||||
Assert.That(countSelected, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOverrideInvoke()
|
||||
{
|
||||
//Arrange
|
||||
RadioOptions<int> _optionButton = new RadioOptions<int>(RadioOptionsLayout.Horizontal);
|
||||
|
||||
int countSelected = 0;
|
||||
|
||||
int itemId = _optionButton.AddItem("High", 1, args => { countSelected--; });
|
||||
int itemId2 = _optionButton.AddItem("High", 2);
|
||||
|
||||
_optionButton.OnItemSelected += args =>
|
||||
{
|
||||
countSelected++;
|
||||
};
|
||||
|
||||
//Act
|
||||
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId, _optionButton));
|
||||
|
||||
//Assert
|
||||
Assert.That(countSelected, Is.EqualTo(-1));
|
||||
|
||||
//Act
|
||||
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId2, _optionButton));
|
||||
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId2, _optionButton));
|
||||
|
||||
//Assert
|
||||
Assert.That(countSelected, Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(TextEditShared))]
|
||||
[Parallelizable]
|
||||
internal sealed class TextEditSharedTest
|
||||
{
|
||||
// @formatter:off
|
||||
[Test]
|
||||
[TestCase("foo bar baz", 0, ExpectedResult = 0)]
|
||||
[TestCase("foo bar baz", 1, ExpectedResult = 0)]
|
||||
[TestCase("foo bar baz", 4, ExpectedResult = 0)]
|
||||
[TestCase("foo bar baz", 5, ExpectedResult = 4)]
|
||||
[TestCase("foo bar baz", 8, ExpectedResult = 4)]
|
||||
[TestCase("foo bar baz", 9, ExpectedResult = 8)]
|
||||
[TestCase("foo +bar baz", 5, ExpectedResult = 4)]
|
||||
[TestCase("foo +bar baz", 4, ExpectedResult = 0)]
|
||||
[TestCase("foo +bar baz", 6, ExpectedResult = 5)]
|
||||
[TestCase("foo +bar baz", 7, ExpectedResult = 5)]
|
||||
[TestCase("Foo Bar Baz", 4, ExpectedResult = 0)]
|
||||
[TestCase("Foo Bar Baz", 11, ExpectedResult = 8)]
|
||||
[TestCase("Foo[Bar[Baz", 3, ExpectedResult = 0)]
|
||||
[TestCase("Foo[Bar[Baz", 4, ExpectedResult = 3)]
|
||||
[TestCase("Foo^Bar^Baz", 3, ExpectedResult = 0)]
|
||||
[TestCase("Foo^Bar^Baz", 5, ExpectedResult = 3)]
|
||||
[TestCase("Foo^^^Bar^Baz", 9, ExpectedResult = 3)]
|
||||
[TestCase("^^^ ^^^", 7, ExpectedResult = 0)]
|
||||
[TestCase("^^^ ^^^", 13, ExpectedResult = 7)]
|
||||
// @formatter:on
|
||||
public int TestPrevWordPosition(string str, int cursor)
|
||||
{
|
||||
// For my sanity.
|
||||
str = str.Replace("^", "👏");
|
||||
|
||||
return TextEditShared.PrevWordPosition(str, cursor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
// @formatter:off
|
||||
[TestCase("foo bar baz", 11, ExpectedResult = 11)]
|
||||
[TestCase("foo bar baz", 0, ExpectedResult = 3 )]
|
||||
[TestCase("foo bar baz", 1, ExpectedResult = 3 )]
|
||||
[TestCase("foo bar baz", 3, ExpectedResult = 7 )]
|
||||
[TestCase("foo bar baz", 4, ExpectedResult = 7 )]
|
||||
[TestCase("foo bar baz", 5, ExpectedResult = 7 )]
|
||||
[TestCase("Foo Bar Baz", 0, ExpectedResult = 3 )]
|
||||
[TestCase("Foo Bar Baz", 8, ExpectedResult = 11)]
|
||||
[TestCase("foo +bar baz", 0, ExpectedResult = 3 )]
|
||||
[TestCase("foo +bar baz", 4, ExpectedResult = 5 )]
|
||||
[TestCase("Foo[Bar[Baz", 0, ExpectedResult = 3 )]
|
||||
[TestCase("Foo[Bar[Baz", 3, ExpectedResult = 4 )]
|
||||
[TestCase("Foo^Bar^Baz", 0, ExpectedResult = 3 )]
|
||||
[TestCase("Foo^Bar^Baz", 3, ExpectedResult = 5 )]
|
||||
[TestCase("Foo^^^Bar^Baz", 3, ExpectedResult = 9 )]
|
||||
[TestCase("^^^ ^^^", 0, ExpectedResult = 6 )]
|
||||
[TestCase("^^^ ^^^", 7, ExpectedResult = 13)]
|
||||
// @formatter:on
|
||||
public int TestEndWordPosition(string str, int cursor)
|
||||
{
|
||||
// For my sanity.
|
||||
str = str.Replace("^", "👏");
|
||||
|
||||
return TextEditShared.EndWordPosition(str, cursor);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(TextEdit))]
|
||||
public sealed class TextEditTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/space-wizards/RobustToolbox/issues/4953
|
||||
// It was possible to move the cursor up/down if there was multi-line placeholder text.
|
||||
[Test]
|
||||
public void TestInvalidMoveInPlaceholder()
|
||||
{
|
||||
var textEdit = new TextEdit { Placeholder = new Rope.Leaf("Foo\nBar") };
|
||||
textEdit.Arrange(new UIBox2(0, 0, 200, 200));
|
||||
|
||||
var click = new GUIBoundKeyEventArgs(EngineKeyFunctions.TextCursorDown, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
|
||||
textEdit.KeyBindDown(click);
|
||||
textEdit.KeyBindUp(click);
|
||||
|
||||
Assert.That(textEdit.CursorPosition.Index, Is.Zero);
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/space-wizards/RobustToolbox/issues/4957
|
||||
// Moving left (with the arrow keys) in an empty TextEdit would cause an exception.
|
||||
[Test]
|
||||
public void TestEmptyMoveLeft()
|
||||
{
|
||||
var textEdit = new TextEdit();
|
||||
textEdit.Arrange(new UIBox2(0, 0, 200, 200));
|
||||
|
||||
var click = new GUIBoundKeyEventArgs(EngineKeyFunctions.TextCursorLeft, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
|
||||
textEdit.KeyBindDown(click);
|
||||
textEdit.KeyBindUp(click);
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class StylesheetTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectors()
|
||||
{
|
||||
var selectorElementLabel = new SelectorElement(typeof(Label), null, null, null);
|
||||
|
||||
var label = new Label();
|
||||
var panel = new PanelContainer {StyleIdentifier = "bar"};
|
||||
Assert.That(selectorElementLabel.Matches(label), Is.True);
|
||||
Assert.That(selectorElementLabel.Matches(panel), Is.False);
|
||||
|
||||
selectorElementLabel = new SelectorElement(typeof(Label), new[] {"foo"}, null, null);
|
||||
Assert.That(selectorElementLabel.Matches(label), Is.False);
|
||||
Assert.That(selectorElementLabel.Matches(panel), Is.False);
|
||||
|
||||
Assert.That(label.HasStyleClass("foo"), Is.False);
|
||||
label.AddStyleClass("foo");
|
||||
Assert.That(selectorElementLabel.Matches(label), Is.True);
|
||||
Assert.That(label.HasStyleClass("foo"));
|
||||
// Make sure it doesn't throw.
|
||||
label.AddStyleClass("foo");
|
||||
label.RemoveStyleClass("foo");
|
||||
Assert.That(selectorElementLabel.Matches(label), Is.False);
|
||||
Assert.That(label.HasStyleClass("foo"), Is.False);
|
||||
// Make sure it doesn't throw.
|
||||
label.RemoveStyleClass("foo");
|
||||
|
||||
selectorElementLabel = new SelectorElement(null, null, "bar", null);
|
||||
Assert.That(selectorElementLabel.Matches(label), Is.False);
|
||||
Assert.That(selectorElementLabel.Matches(panel), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStyleProperties()
|
||||
{
|
||||
var sheet = new Stylesheet(new[]
|
||||
{
|
||||
new StyleRule(new SelectorElement(typeof(Label), null, "baz", null), new[]
|
||||
{
|
||||
new StyleProperty("foo", "honk"),
|
||||
}),
|
||||
new StyleRule(new SelectorElement(typeof(Label), null, null, null), new[]
|
||||
{
|
||||
new StyleProperty("foo", "heh"),
|
||||
}),
|
||||
new StyleRule(new SelectorElement(typeof(Label), null, null, null), new[]
|
||||
{
|
||||
new StyleProperty("foo", "bar"),
|
||||
}),
|
||||
});
|
||||
|
||||
var uiMgr = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
uiMgr.Stylesheet = sheet;
|
||||
|
||||
var control = new Label();
|
||||
|
||||
uiMgr.StateRoot.AddChild(control);
|
||||
control.ForceRunStyleUpdate();
|
||||
|
||||
control.TryGetStyleProperty("foo", out string? value);
|
||||
Assert.That(value, Is.EqualTo("bar"));
|
||||
|
||||
control.StyleIdentifier = "baz";
|
||||
control.ForceRunStyleUpdate();
|
||||
|
||||
control.TryGetStyleProperty("foo", out value);
|
||||
Assert.That(value, Is.EqualTo("honk"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStylesheetOverride()
|
||||
{
|
||||
var sheetA = new Stylesheet(new[]
|
||||
{
|
||||
new StyleRule(SelectorElement.Class("A"), new[] {new StyleProperty("foo", "bar")}),
|
||||
});
|
||||
|
||||
var sheetB = new Stylesheet(new[]
|
||||
{
|
||||
new StyleRule(SelectorElement.Class("A"), new[] {new StyleProperty("foo", "honk!")})
|
||||
});
|
||||
|
||||
// Set style sheet to null, property shouldn't exist.
|
||||
|
||||
var uiMgr = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
uiMgr.Stylesheet = null;
|
||||
|
||||
var baseControl = new Control();
|
||||
baseControl.AddStyleClass("A");
|
||||
var childA = new Control();
|
||||
childA.AddStyleClass("A");
|
||||
var childB = new Control();
|
||||
childB.AddStyleClass("A");
|
||||
|
||||
uiMgr.StateRoot.AddChild(baseControl);
|
||||
|
||||
baseControl.AddChild(childA);
|
||||
childA.AddChild(childB);
|
||||
|
||||
baseControl.ForceRunStyleUpdate();
|
||||
|
||||
Assert.That(baseControl.TryGetStyleProperty("foo", out object? _), Is.False);
|
||||
|
||||
uiMgr.RootControl.Stylesheet = sheetA;
|
||||
childA.Stylesheet = sheetB;
|
||||
|
||||
// Assign sheets.
|
||||
baseControl.ForceRunStyleUpdate();
|
||||
|
||||
baseControl.TryGetStyleProperty("foo", out object? value);
|
||||
Assert.That(value, Is.EqualTo("bar"));
|
||||
|
||||
childA.TryGetStyleProperty("foo", out value);
|
||||
Assert.That(value, Is.EqualTo("honk!"));
|
||||
|
||||
childB.TryGetStyleProperty("foo", out value);
|
||||
Assert.That(value, Is.EqualTo("honk!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class UserInterfaceManagerTest : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Client;
|
||||
|
||||
private IUserInterfaceManagerInternal _userInterfaceManager = default!;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_userInterfaceManager = IoCManager.Resolve<IUserInterfaceManagerInternal>();
|
||||
_userInterfaceManager.InitializeTesting();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[SuppressMessage("ReSharper", "AccessToModifiedClosure")]
|
||||
public void TestMouseDown()
|
||||
{
|
||||
// We create 4 controls.
|
||||
// Control 2 is set to stop mouse events,
|
||||
// Control 3 pass,
|
||||
// Control 4 ignore.
|
||||
// We check that 4 and 1 do not receive events, that 3 receives before 2, and that positions are correct.
|
||||
var control1 = new LayoutContainer
|
||||
{
|
||||
MinSize = new Vector2(50, 50)
|
||||
};
|
||||
var control2 = new LayoutContainer
|
||||
{
|
||||
MinSize = new Vector2(50, 50),
|
||||
MouseFilter = Control.MouseFilterMode.Stop
|
||||
};
|
||||
var control3 = new LayoutContainer
|
||||
{
|
||||
MinSize = new Vector2(50, 50),
|
||||
MouseFilter = Control.MouseFilterMode.Pass
|
||||
};
|
||||
var control4 = new LayoutContainer
|
||||
{
|
||||
MinSize = new Vector2(50, 50),
|
||||
MouseFilter = Control.MouseFilterMode.Ignore
|
||||
};
|
||||
|
||||
_userInterfaceManager.RootControl.AddChild(control1);
|
||||
control1.AddChild(control2);
|
||||
// Offsets to test relative positioning on the events.
|
||||
LayoutContainer.SetPosition(control2, new Vector2(5, 5));
|
||||
control2.AddChild(control3);
|
||||
LayoutContainer.SetPosition(control3, new Vector2(5, 5));
|
||||
control3.AddChild(control4);
|
||||
LayoutContainer.SetPosition(control4, new Vector2(5, 5));
|
||||
|
||||
control1.Arrange(new UIBox2(0, 0, 50, 50));
|
||||
|
||||
var mouseEvent = new BoundKeyEventArgs(EngineKeyFunctions.Use, BoundKeyState.Down,
|
||||
new ScreenCoordinates(30, 30, WindowId.Main), true);
|
||||
|
||||
var control2Fired = false;
|
||||
var control3Fired = false;
|
||||
|
||||
control1.OnKeyBindDown += _ => Assert.Fail("Control 1 should not get a mouse event.");
|
||||
|
||||
void Control2MouseDown(GUIBoundKeyEventArgs ev)
|
||||
{
|
||||
Assert.That(control2Fired, NUnit.Framework.Is.False);
|
||||
Assert.That(control3Fired, NUnit.Framework.Is.True);
|
||||
|
||||
Assert.That(ev.RelativePosition, NUnit.Framework.Is.EqualTo(new Vector2(25, 25)));
|
||||
|
||||
control2Fired = true;
|
||||
}
|
||||
|
||||
control2.OnKeyBindDown += Control2MouseDown;
|
||||
|
||||
control3.OnKeyBindDown += ev =>
|
||||
{
|
||||
Assert.That(control2Fired, NUnit.Framework.Is.False);
|
||||
Assert.That(control3Fired, NUnit.Framework.Is.False);
|
||||
|
||||
Assert.That(ev.RelativePosition, NUnit.Framework.Is.EqualTo(new Vector2(20, 20)));
|
||||
|
||||
control3Fired = true;
|
||||
};
|
||||
|
||||
control4.OnKeyBindDown += _ => Assert.Fail("Control 4 should not get a mouse event.");
|
||||
|
||||
_userInterfaceManager.KeyBindDown(mouseEvent);
|
||||
_userInterfaceManager.KeyBindUp(mouseEvent);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(control2Fired, NUnit.Framework.Is.True);
|
||||
Assert.That(control3Fired, NUnit.Framework.Is.True);
|
||||
});
|
||||
|
||||
// Step two: instead of relying on stop for control2 to prevent the event reaching control1,
|
||||
// handle the event in control2.
|
||||
|
||||
control2Fired = false;
|
||||
control3Fired = false;
|
||||
|
||||
control2.OnKeyBindDown -= Control2MouseDown;
|
||||
control2.OnKeyBindDown += ev =>
|
||||
{
|
||||
Assert.That(control2Fired, NUnit.Framework.Is.False);
|
||||
Assert.That(control3Fired, NUnit.Framework.Is.True);
|
||||
|
||||
Assert.That(ev.RelativePosition, NUnit.Framework.Is.EqualTo(new Vector2(25, 25)));
|
||||
|
||||
control2Fired = true;
|
||||
ev.Handle();
|
||||
};
|
||||
control2.MouseFilter = Control.MouseFilterMode.Pass;
|
||||
|
||||
_userInterfaceManager.KeyBindDown(mouseEvent);
|
||||
_userInterfaceManager.KeyBindUp(mouseEvent);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(control2Fired, NUnit.Framework.Is.True);
|
||||
Assert.That(control3Fired, NUnit.Framework.Is.True);
|
||||
});
|
||||
|
||||
control1.Orphan();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGrabKeyboardFocus()
|
||||
{
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
var control1 = new Control {CanKeyboardFocus = true};
|
||||
|
||||
control1.GrabKeyboardFocus();
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control1));
|
||||
Assert.That(control1.HasKeyboardFocus(), NUnit.Framework.Is.EqualTo(true));
|
||||
|
||||
control1.ReleaseKeyboardFocus();
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGrabKeyboardFocusSteal()
|
||||
{
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
var control1 = new Control {CanKeyboardFocus = true};
|
||||
var control2 = new Control {CanKeyboardFocus = true};
|
||||
|
||||
control1.GrabKeyboardFocus();
|
||||
control2.GrabKeyboardFocus();
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control2));
|
||||
control2.ReleaseKeyboardFocus();
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGrabKeyboardFocusOtherRelease()
|
||||
{
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
var control1 = new Control {CanKeyboardFocus = true};
|
||||
var control2 = new Control {CanKeyboardFocus = true};
|
||||
|
||||
control1.GrabKeyboardFocus();
|
||||
control2.ReleaseKeyboardFocus();
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control1));
|
||||
_userInterfaceManager.ReleaseKeyboardFocus();
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGrabKeyboardFocusNull()
|
||||
{
|
||||
Assert.That(() => _userInterfaceManager.GrabKeyboardFocus(null!), Throws.ArgumentNullException);
|
||||
Assert.That(() => _userInterfaceManager.ReleaseKeyboardFocus(null!), Throws.ArgumentNullException);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGrabKeyboardFocusBlocked()
|
||||
{
|
||||
var control = new Control();
|
||||
Assert.That(() => _userInterfaceManager.GrabKeyboardFocus(control), Throws.ArgumentException);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGrabKeyboardFocusOnClick()
|
||||
{
|
||||
var control = new Control
|
||||
{
|
||||
CanKeyboardFocus = true,
|
||||
KeyboardFocusOnClick = true,
|
||||
MinSize = new Vector2(50, 50),
|
||||
MouseFilter = Control.MouseFilterMode.Stop
|
||||
};
|
||||
|
||||
_userInterfaceManager.RootControl.AddChild(control);
|
||||
|
||||
_userInterfaceManager.RootControl.Arrange(new UIBox2(0, 0, 50, 50));
|
||||
|
||||
_userInterfaceManager.HandleCanFocusDown(new ScreenCoordinates(30, 30, WindowId.Main), out _);
|
||||
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control));
|
||||
_userInterfaceManager.ReleaseKeyboardFocus();
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
|
||||
control.Orphan();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert that indeed nothing happens when the control has focus modes off.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNotGrabKeyboardFocusOnClick()
|
||||
{
|
||||
var control = new Control
|
||||
{
|
||||
MinSize = new Vector2(50, 50),
|
||||
MouseFilter = Control.MouseFilterMode.Stop
|
||||
};
|
||||
|
||||
_userInterfaceManager.RootControl.AddChild(control);
|
||||
|
||||
var pos = new ScreenCoordinates(30, 30, WindowId.Main);
|
||||
|
||||
var mouseEvent = new GUIBoundKeyEventArgs(EngineKeyFunctions.Use, BoundKeyState.Down,
|
||||
pos, true, pos.Position / 1 - control.GlobalPosition, pos.Position - control.GlobalPixelPosition);
|
||||
|
||||
_userInterfaceManager.KeyBindDown(mouseEvent);
|
||||
_userInterfaceManager.KeyBindUp(mouseEvent);
|
||||
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
|
||||
control.Orphan();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting
|
||||
{
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Packaging.AssetProcessing.Passes;
|
||||
|
||||
namespace Robust.UnitTesting.Packaging;
|
||||
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
[TestFixture]
|
||||
[TestOf(typeof(AssetPassMergeTextDirectories))]
|
||||
internal sealed class AssetPassMergeTextDirectoriesTest
|
||||
{
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
var pass = new AssetPassMergeTextDirectories("/Prototypes", "yml", f => $"# BEGIN: {f}", f => $"# END: {f}");
|
||||
var collectorPass = AssetPassTest.SetupTestPass(pass);
|
||||
|
||||
pass.InjectFileFromMemory("/Prototypes/LICENSE.txt", "Do whatever\n"u8);
|
||||
pass.InjectFileFromMemory("/Prototypes/b.yml", "# file: B\n"u8);
|
||||
pass.InjectFileFromMemory("/Prototypes/a.yml", "# file: A\n"u8);
|
||||
pass.InjectFileFromMemory("/Prototypes/z/d.yml", "# file: D\n"u8);
|
||||
pass.InjectFileFromMemory("/Prototypes/z/c.yml", "# file: C\n"u8);
|
||||
pass.InjectFinished();
|
||||
await collectorPass.FinishedTask;
|
||||
|
||||
collectorPass.AssertTextFiles(
|
||||
("/Prototypes/__merged.yml", """
|
||||
# BEGIN: /Prototypes/a.yml
|
||||
# file: A
|
||||
# END: /Prototypes/a.yml
|
||||
# BEGIN: /Prototypes/b.yml
|
||||
# file: B
|
||||
# END: /Prototypes/b.yml
|
||||
|
||||
""".Replace("\r\n", "\n")),
|
||||
("/Prototypes/z/__merged.yml", """
|
||||
# BEGIN: /Prototypes/z/c.yml
|
||||
# file: C
|
||||
# END: /Prototypes/z/c.yml
|
||||
# BEGIN: /Prototypes/z/d.yml
|
||||
# file: D
|
||||
# END: /Prototypes/z/d.yml
|
||||
|
||||
""".Replace("\r\n", "\n")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestNormalizeEol()
|
||||
{
|
||||
var pass = new AssetPassMergeTextDirectories("/", "yml");
|
||||
var collectorPass = AssetPassTest.SetupTestPass(pass);
|
||||
|
||||
pass.InjectFileFromMemory("/b.yml", "# file: B\r\n"u8);
|
||||
pass.InjectFileFromMemory("/a.yml", "# file: A\n"u8);
|
||||
pass.InjectFinished();
|
||||
await collectorPass.FinishedTask;
|
||||
|
||||
collectorPass.AssertTextFiles(
|
||||
("/__merged.yml", "# file: A\n# file: B\n"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestFixBom()
|
||||
{
|
||||
var pass = new AssetPassMergeTextDirectories("/", "yml");
|
||||
var collectorPass = AssetPassTest.SetupTestPass(pass);
|
||||
|
||||
pass.InjectFileFromMemory("/b.yml", "\uFEFF# file: B\n"u8);
|
||||
pass.InjectFileFromMemory("/a.yml", "\uFEFF# file: A\n"u8);
|
||||
pass.InjectFinished();
|
||||
await collectorPass.FinishedTask;
|
||||
|
||||
collectorPass.AssertTextFiles(
|
||||
("/__merged.yml", "# file: A\n# file: B\n"));
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Packaging.AssetProcessing;
|
||||
|
||||
namespace Robust.UnitTesting.Packaging;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for testing <see cref="AssetPass"/>.
|
||||
/// </summary>
|
||||
public static class AssetPassTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Make an asset pass write into a <see cref="AssetPassTestCollector"/> and resolve the graph.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The resolved graph logs to the NUnit test context.
|
||||
/// </remarks>
|
||||
public static AssetPassTestCollector SetupTestPass(AssetPass testedPass)
|
||||
{
|
||||
var logger = new PackageLoggerNUnit(TestContext.Out);
|
||||
var collectorPass = new AssetPassTestCollector();
|
||||
|
||||
collectorPass.AddDependency(testedPass);
|
||||
|
||||
AssetGraph.CalculateGraph([testedPass, collectorPass], logger);
|
||||
|
||||
return collectorPass;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Packaging.AssetProcessing;
|
||||
|
||||
namespace Robust.UnitTesting.Packaging;
|
||||
|
||||
/// <summary>
|
||||
/// A simple asset pass that stores all files it receives, for introspection by tests.
|
||||
/// </summary>
|
||||
public sealed class AssetPassTestCollector : AssetPass
|
||||
{
|
||||
public readonly List<AssetFile> Files = [];
|
||||
|
||||
protected override AssetFileAcceptResult AcceptFile(AssetFile file)
|
||||
{
|
||||
lock (Files)
|
||||
{
|
||||
Files.Add(file);
|
||||
}
|
||||
|
||||
return AssetFileAcceptResult.Consumed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert that the only files collected are an exact set of test files.
|
||||
/// </summary>
|
||||
public void AssertTextFiles(params (string path, string data)[] files)
|
||||
{
|
||||
lock (Files)
|
||||
{
|
||||
Assert.That(Files, Has.Count.EqualTo(files.Length));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
var matchingFile = Files.SingleOrDefault(f => f.Path == file.path);
|
||||
if (matchingFile == null)
|
||||
{
|
||||
Assert.Fail($"Unable to find file {file.path}");
|
||||
continue;
|
||||
}
|
||||
|
||||
using var fileData = matchingFile.Open();
|
||||
using var reader = new StreamReader(fileData);
|
||||
var fileText = reader.ReadToEnd();
|
||||
|
||||
Assert.That(fileText, Is.EqualTo(file.data));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.IO;
|
||||
using Robust.Packaging;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Robust.UnitTesting.Packaging;
|
||||
|
||||
/// <summary>
|
||||
/// Package logger for writing to NUnit's test context.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
public sealed class PackageLoggerNUnit(TextWriter writer) : IPackageLogger
|
||||
{
|
||||
public void Log(LogLevel level, string msg)
|
||||
{
|
||||
writer.WriteLine($"[{level}] {msg}");
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Robust.UnitTesting
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
[CVarDefs]
|
||||
public sealed class RTCVars : CVars
|
||||
{
|
||||
/*
|
||||
* Logging
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// The log level which will cause a test failure.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<LogLevel> FailureLogLevel =
|
||||
CVarDef.Create("robust.tests.failure_log_level", Robust.Shared.Log.LogLevel.Error);
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,13 @@
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>../bin/UnitTesting</OutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
<PackageReference Include="Serilog" />
|
||||
<PackageReference Include="JetBrains.Annotations" />
|
||||
<PackageReference Include="Microsoft.CodeCoverage" />
|
||||
<PackageReference Include="Microsoft.DotNet.RemoteExecutor" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="NUnit" />
|
||||
@@ -19,16 +17,15 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\NetSerializer\NetSerializer\NetSerializer.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths.Testing\Robust.Shared.Maths.Testing.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths.Tests\Robust.Shared.Maths.Tests.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Testing\Robust.Shared.Testing.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Shared\ContentPack\ZipTest.zip">
|
||||
<LogicalName>Robust.UnitTesting.Shared.ContentPack.ZipTest.zip</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.CompNetworkGenerator.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -10,7 +10,6 @@ using Lidgren.Network;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client;
|
||||
using Robust.Server;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.UnitTesting.Pool;
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Testing;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.UnitTesting.Pool;
|
||||
using ServerProgram = Robust.Server.Program;
|
||||
|
||||
namespace Robust.UnitTesting
|
||||
@@ -83,6 +83,9 @@ namespace Robust.UnitTesting
|
||||
/// </summary>
|
||||
protected virtual ServerIntegrationInstance StartServer(ServerIntegrationOptions? options = null)
|
||||
{
|
||||
options ??= new ServerIntegrationOptions();
|
||||
options.TestAssembly = GetType().Assembly;
|
||||
|
||||
ServerIntegrationInstance instance;
|
||||
|
||||
if (ShouldPool(options))
|
||||
@@ -132,6 +135,9 @@ namespace Robust.UnitTesting
|
||||
/// </summary>
|
||||
protected virtual ClientIntegrationInstance StartClient(ClientIntegrationOptions? options = null)
|
||||
{
|
||||
options ??= new ClientIntegrationOptions();
|
||||
options.TestAssembly = GetType().Assembly;
|
||||
|
||||
ClientIntegrationInstance instance;
|
||||
|
||||
if (ShouldPool(options))
|
||||
@@ -717,8 +723,8 @@ namespace Robust.UnitTesting
|
||||
//ServerProgram.SetupLogging();
|
||||
ServerProgram.InitReflectionManager(deps);
|
||||
|
||||
if (Options?.LoadTestAssembly != false)
|
||||
deps.Resolve<IReflectionManager>().LoadAssemblies(typeof(RobustIntegrationTest).Assembly);
|
||||
if (Options?.LoadTestAssembly != false && Options?.TestAssembly != null)
|
||||
deps.Resolve<IReflectionManager>().LoadAssemblies(Options.TestAssembly);
|
||||
|
||||
var server = DependencyCollection.Resolve<BaseServer>();
|
||||
|
||||
@@ -747,6 +753,7 @@ namespace Robust.UnitTesting
|
||||
var cfg = deps.Resolve<IConfigurationManagerInternal>();
|
||||
|
||||
cfg.LoadCVarsFromAssembly(typeof(RobustIntegrationTest).Assembly);
|
||||
cfg.LoadCVarsFromAssembly(typeof(RTCVars).Assembly);
|
||||
|
||||
if (Options != null)
|
||||
{
|
||||
@@ -977,8 +984,8 @@ namespace Robust.UnitTesting
|
||||
|
||||
GameController.RegisterReflection(deps);
|
||||
|
||||
if (Options?.LoadTestAssembly != false)
|
||||
deps.Resolve<IReflectionManager>().LoadAssemblies(typeof(RobustIntegrationTest).Assembly);
|
||||
if (Options?.LoadTestAssembly != false && Options?.TestAssembly != null)
|
||||
deps.Resolve<IReflectionManager>().LoadAssemblies(Options.TestAssembly);
|
||||
|
||||
var client = DependencyCollection.Resolve<GameController>();
|
||||
|
||||
@@ -1007,6 +1014,7 @@ namespace Robust.UnitTesting
|
||||
var cfg = deps.Resolve<IConfigurationManagerInternal>();
|
||||
|
||||
cfg.LoadCVarsFromAssembly(typeof(RobustIntegrationTest).Assembly);
|
||||
cfg.LoadCVarsFromAssembly(typeof(RTCVars).Assembly);
|
||||
|
||||
if (Options != null)
|
||||
{
|
||||
@@ -1214,6 +1222,7 @@ namespace Robust.UnitTesting
|
||||
public Assembly[]? ContentAssemblies { get; set; }
|
||||
|
||||
public bool LoadTestAssembly { get; set; } = true;
|
||||
public Assembly? TestAssembly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// String containing extra prototypes to load. Contents of the string are treated like a yaml file in the
|
||||
|
||||
13
Robust.UnitTesting/RobustIntegrationTestDummy.cs
Normal file
13
Robust.UnitTesting/RobustIntegrationTestDummy.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Robust.UnitTesting;
|
||||
|
||||
[TestFixture]
|
||||
internal sealed class RobustIntegrationTestDummy : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public void Foo()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Robust.Client;
|
||||
using Robust.Server;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Testing;
|
||||
|
||||
namespace Robust.UnitTesting
|
||||
{
|
||||
|
||||
@@ -14,18 +14,16 @@ using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Controllers;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Testing;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
using AppearanceSystem = Robust.Client.GameObjects.AppearanceSystem;
|
||||
@@ -112,7 +110,7 @@ namespace Robust.UnitTesting
|
||||
configurationManager.LoadCVarsFromAssembly(assembly);
|
||||
}
|
||||
|
||||
configurationManager.LoadCVarsFromAssembly(typeof(RobustUnitTest).Assembly);
|
||||
configurationManager.LoadCVarsFromAssembly(typeof(RTCVars).Assembly);
|
||||
|
||||
var systems = deps.Resolve<IEntitySystemManager>();
|
||||
// Required systems
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameObjects;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class ComponentMapInitTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Asserts whether a component added after an entity has fully initialized has MapInit called.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ComponentMapInit()
|
||||
{
|
||||
var simFactory = RobustServerSimulation.NewSimulation();
|
||||
simFactory.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<MapInitTestComponent>();
|
||||
}).RegisterEntitySystems(fac =>
|
||||
{
|
||||
fac.LoadExtraSystemType<MapInitTestSystem>();
|
||||
});
|
||||
|
||||
var sim = simFactory.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var ent = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(entManager.GetComponent<MetaDataComponent>(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
var comp = entManager.AddComponent<MapInitTestComponent>(ent);
|
||||
|
||||
Assert.That(comp.Count, Is.EqualTo(1));
|
||||
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class MapInitTestSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<MapInitTestComponent, MapInitEvent>(OnMapInitTestMapInit);
|
||||
}
|
||||
|
||||
private void OnMapInitTestMapInit(EntityUid uid, MapInitTestComponent component, MapInitEvent args)
|
||||
{
|
||||
component.Count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed partial class MapInitTestComponent : Component
|
||||
{
|
||||
public int Count = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameObjects.Components
|
||||
{
|
||||
[TestFixture, Parallelizable]
|
||||
public sealed partial class ContainerTest
|
||||
{
|
||||
private static EntityCoordinates _coords;
|
||||
|
||||
private static ISimulation SimulationFactory()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.InitializeInstance();
|
||||
var map = sim.CreateMap();
|
||||
_coords = new EntityCoordinates(map.Item1, default);
|
||||
|
||||
return sim;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCreation()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var entity = sim.SpawnEntity(null,_coords);
|
||||
|
||||
var container = containerSys.MakeContainer<Container>(entity, "dummy");
|
||||
|
||||
Assert.That(container.ID, Is.EqualTo("dummy"));
|
||||
Assert.That(container.Owner, Is.EqualTo(entity));
|
||||
|
||||
var manager = entManager.GetComponent<ContainerManagerComponent>(entity);
|
||||
|
||||
Assert.That(container.Manager, Is.EqualTo(manager));
|
||||
Assert.That(() => containerSys.MakeContainer<Container>(entity, "dummy"), Throws.ArgumentException);
|
||||
|
||||
Assert.That(containerSys.HasContainer(entity, "dummy2", manager), Is.False);
|
||||
var container2 = containerSys.MakeContainer<Container>(entity, "dummy2");
|
||||
|
||||
Assert.That(container2.Manager, Is.EqualTo(manager));
|
||||
Assert.That(container2.Owner, Is.EqualTo(entity));
|
||||
Assert.That(container2.ID, Is.EqualTo("dummy2"));
|
||||
|
||||
Assert.That(containerSys.HasContainer(entity, "dummy", manager), Is.True);
|
||||
Assert.That(containerSys.HasContainer(entity, "dummy2",manager), Is.True);
|
||||
Assert.That(containerSys.HasContainer(entity, "dummy3", manager), Is.False);
|
||||
|
||||
Assert.That(containerSys.GetContainer(entity, "dummy", manager), Is.EqualTo(container));
|
||||
Assert.That(containerSys.GetContainer(entity, "dummy2", manager), Is.EqualTo(container2));
|
||||
Assert.That(() => containerSys.GetContainer(entity, "dummy3", manager), Throws.TypeOf<KeyNotFoundException>());
|
||||
|
||||
entManager.DeleteEntity(entity);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertion()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var owner = sim.SpawnEntity(null,_coords);
|
||||
var inserted = sim.SpawnEntity(null,_coords);
|
||||
var transform = entManager.GetComponent<TransformComponent>(inserted);
|
||||
|
||||
var container = containerSys.MakeContainer<Container>(owner, "dummy");
|
||||
Assert.That(containerSys.Insert(inserted, container), Is.True);
|
||||
Assert.That(transform.ParentUid, Is.EqualTo(owner));
|
||||
|
||||
var container2 = containerSys.MakeContainer<Container>(inserted, "dummy");
|
||||
Assert.That(containerSys.Insert(owner, container2), Is.False);
|
||||
|
||||
var success = containerSys.Remove(inserted, container);
|
||||
Assert.That(success, Is.True);
|
||||
|
||||
success = containerSys.Remove(inserted, container);
|
||||
Assert.That(success, Is.False);
|
||||
|
||||
containerSys.Insert(inserted, container);
|
||||
entManager.DeleteEntity(owner);
|
||||
// Make sure inserted was detached.
|
||||
Assert.That(transform.Deleted, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNestedRemoval()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var owner = sim.SpawnEntity(null,_coords);
|
||||
var inserted = sim.SpawnEntity(null,_coords);
|
||||
var transform = entManager.GetComponent<TransformComponent>(inserted);
|
||||
var entity = sim.SpawnEntity(null,_coords);
|
||||
|
||||
var container = containerSys.MakeContainer<Container>(owner, "dummy");
|
||||
Assert.That(containerSys.Insert(inserted, container), Is.True);
|
||||
Assert.That(transform.ParentUid, Is.EqualTo(owner));
|
||||
|
||||
var container2 = containerSys.MakeContainer<Container>(inserted, "dummy");
|
||||
Assert.That(containerSys.Insert(entity, container2), Is.True);
|
||||
Assert.That(entManager.GetComponent<TransformComponent>(entity).ParentUid, Is.EqualTo(inserted));
|
||||
|
||||
Assert.That(containerSys.Remove(entity, container2), Is.True);
|
||||
Assert.That(container.Contains(entity), Is.True);
|
||||
Assert.That(entManager.GetComponent<TransformComponent>(entity).ParentUid, Is.EqualTo(owner));
|
||||
|
||||
entManager.DeleteEntity(owner);
|
||||
Assert.That(transform.Deleted, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNestedRemovalWithDenial()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var coordinates =_coords;
|
||||
var entityOne = sim.SpawnEntity(null, coordinates);
|
||||
var entityTwo = sim.SpawnEntity(null, coordinates);
|
||||
var entityThree = sim.SpawnEntity(null, coordinates);
|
||||
var entityItem = sim.SpawnEntity(null, coordinates);
|
||||
|
||||
var container = containerSys.MakeContainer<Container>(entityOne, "dummy");
|
||||
var container2 = containerSys.MakeContainer<ContainerOnlyContainer>(entityTwo, "dummy");
|
||||
var container3 = containerSys.MakeContainer<Container>(entityThree, "dummy");
|
||||
|
||||
Assert.That(containerSys.Insert(entityTwo, container), Is.True);
|
||||
Assert.That(entMan.GetComponent<TransformComponent>(entityTwo).ParentUid, Is.EqualTo(entityOne));
|
||||
|
||||
Assert.That(containerSys.Insert(entityThree, container2), Is.True);
|
||||
Assert.That(entMan.GetComponent<TransformComponent>(entityThree).ParentUid, Is.EqualTo(entityTwo));
|
||||
|
||||
Assert.That(containerSys.Insert(entityItem, container3), Is.True);
|
||||
Assert.That(entMan.GetComponent<TransformComponent>(entityItem).ParentUid, Is.EqualTo(entityThree));
|
||||
|
||||
Assert.That(containerSys.Remove(entityItem, container3), Is.True);
|
||||
Assert.That(container.Contains(entityItem), Is.True);
|
||||
Assert.That(entMan.GetComponent<TransformComponent>(entityItem).ParentUid, Is.EqualTo(entityOne));
|
||||
|
||||
entMan.DeleteEntity(entityOne);
|
||||
Assert.That(entMan.Deleted(entityOne), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaseContainer_SelfInsert_False()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var entity = sim.SpawnEntity(null,_coords);
|
||||
var container = containerSys.MakeContainer<Container>(entity, "dummy");
|
||||
|
||||
Assert.That(containerSys.Insert(entity, container), Is.False);
|
||||
Assert.That(containerSys.CanInsert(entity, container), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaseContainer_InsertMap_False()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var mapEnt = new EntityUid(1);
|
||||
var entity = sim.SpawnEntity(null,_coords);
|
||||
var container = containerSys.MakeContainer<Container>(entity, "dummy");
|
||||
|
||||
Assert.That(containerSys.Insert(mapEnt, container), Is.False);
|
||||
Assert.That(containerSys.CanInsert(mapEnt, container), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaseContainer_InsertGrid_False()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
|
||||
var grid = sim.Resolve<IMapManager>().CreateGridEntity(new MapId(1)).Owner;
|
||||
var entity = sim.SpawnEntity(null,_coords);
|
||||
var container = containerSys.MakeContainer<Container>(entity, "dummy");
|
||||
|
||||
Assert.That(containerSys.Insert(grid, container), Is.False);
|
||||
Assert.That(containerSys.CanInsert(grid, container), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaseContainer_Insert_True()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var containerEntity = sim.SpawnEntity(null,_coords);
|
||||
var container = containerSys.MakeContainer<Container>(containerEntity, "dummy");
|
||||
var insertEntity = sim.SpawnEntity(null,_coords);
|
||||
|
||||
var result = containerSys.Insert(insertEntity, container);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
|
||||
|
||||
Assert.That(entManager.GetComponent<TransformComponent>(containerEntity).ChildCount, Is.EqualTo(1));
|
||||
Assert.That(entManager.GetComponent<TransformComponent>(containerEntity)._children.First(), Is.EqualTo(insertEntity));
|
||||
|
||||
result = containerSys.TryGetContainingContainer(insertEntity, out var resultContainerMan);
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(resultContainerMan?.Manager, Is.EqualTo(container.Manager));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaseContainer_RemoveNotAdded_False()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var containerEntity = sim.SpawnEntity(null,_coords);
|
||||
var container = containerSys.MakeContainer<Container>(containerEntity, "dummy");
|
||||
var insertEntity = sim.SpawnEntity(null,_coords);
|
||||
|
||||
var result = containerSys.Remove(insertEntity, container);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaseContainer_Transfer_True()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
|
||||
var entity1 = sim.SpawnEntity(null,_coords);
|
||||
var container1 = containerSys.MakeContainer<Container>(entity1, "dummy");
|
||||
var entity2 = sim.SpawnEntity(null,_coords);
|
||||
var container2 = containerSys.MakeContainer<Container>(entity2, "dummy");
|
||||
var transferEntity = sim.SpawnEntity(null,_coords);
|
||||
containerSys.Insert(transferEntity, container1);
|
||||
|
||||
var result = containerSys.Insert(transferEntity, container2);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(container1.ContainedEntities.Count, Is.EqualTo(0));
|
||||
Assert.That(container2.ContainedEntities.Count, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Container_Serialize()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var containerSys = entManager.System<ContainerSystem>();
|
||||
var entity = sim.SpawnEntity(null,_coords);
|
||||
var container = containerSys.MakeContainer<Container>(entity, "dummy");
|
||||
var childEnt = sim.SpawnEntity(null,_coords);
|
||||
|
||||
container.OccludesLight = true;
|
||||
container.ShowContents = true;
|
||||
containerSys.Insert(childEnt, container);
|
||||
|
||||
var containerMan = entManager.GetComponent<ContainerManagerComponent>(entity);
|
||||
var getState = new ComponentGetState();
|
||||
entManager.EventBus.RaiseComponentEvent(entity, containerMan, ref getState);
|
||||
var state = (ContainerManagerComponent.ContainerManagerComponentState)getState.State!;
|
||||
|
||||
Assert.That(state.Containers, Has.Count.EqualTo(1));
|
||||
var cont = state.Containers.Values.First();
|
||||
Assert.That(state.Containers.Keys.First(), Is.EqualTo("dummy"));
|
||||
Assert.That(cont.OccludesLight, Is.True);
|
||||
Assert.That(cont.ShowContents, Is.True);
|
||||
Assert.That(cont.ContainedEntities.Count, Is.EqualTo(1));
|
||||
Assert.That(cont.ContainedEntities[0], Is.EqualTo(entManager.GetNetEntity(childEnt)));
|
||||
}
|
||||
|
||||
[SerializedType(nameof(ContainerOnlyContainer))]
|
||||
private sealed partial class ContainerOnlyContainer : BaseContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The generic container class uses a list of entities
|
||||
/// </summary>
|
||||
private readonly List<EntityUid> _containerList = new();
|
||||
|
||||
public override int Count => _containerList.Count;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IReadOnlyList<EntityUid> ContainedEntities => _containerList;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
|
||||
{
|
||||
_containerList.Add(toInsert);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override void InternalRemove(EntityUid toRemove, IEntityManager entMan)
|
||||
{
|
||||
_containerList.Remove(toRemove);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Contains(EntityUid contained)
|
||||
{
|
||||
if (!_containerList.Contains(contained))
|
||||
return false;
|
||||
|
||||
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
|
||||
return true;
|
||||
|
||||
var flags = IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(contained).Flags;
|
||||
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override void InternalShutdown(IEntityManager entMan, SharedContainerSystem system, bool isClient)
|
||||
{
|
||||
foreach (var entity in _containerList.ToArray())
|
||||
{
|
||||
if (!isClient)
|
||||
entMan.DeleteEntity(entity);
|
||||
else if (entMan.EntityExists(entity))
|
||||
system.Remove(entity, this, reparent: false, force: true);
|
||||
}
|
||||
}
|
||||
|
||||
protected internal override bool CanInsert(EntityUid toinsert, bool assumeEmpty, IEntityManager entMan)
|
||||
{
|
||||
return entMan.HasComponent<ContainerManagerComponent>(toinsert);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameObjects.Components;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class TransformIntegration_Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Asserts that calling SetWorldPosition while in a container correctly removes the entity from its container.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WorldPositionContainerSet()
|
||||
{
|
||||
var factory = RobustServerSimulation.NewSimulation();
|
||||
|
||||
var sim = factory.InitializeInstance();
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var containerSystem = entManager.System<SharedContainerSystem>();
|
||||
var xformSystem = entManager.System<SharedTransformSystem>();
|
||||
var map1 = sim.CreateMap().Uid;
|
||||
|
||||
var ent1 = entManager.SpawnEntity(null, new EntityCoordinates(map1, Vector2.Zero));
|
||||
var ent2 = entManager.SpawnEntity(null, new EntityCoordinates(map1, Vector2.Zero));
|
||||
var ent3 = entManager.SpawnEntity(null, new EntityCoordinates(map1, Vector2.Zero));
|
||||
|
||||
var container = containerSystem.EnsureContainer<ContainerSlot>(ent1, "a");
|
||||
|
||||
// Assert that setting worldpos updates parent correctly.
|
||||
containerSystem.Insert(ent2, container, force: true);
|
||||
|
||||
Assert.That(containerSystem.IsEntityInContainer(ent2));
|
||||
|
||||
xformSystem.SetWorldPosition(ent2, Vector2.One);
|
||||
|
||||
Assert.That(!containerSystem.IsEntityInContainer(ent2));
|
||||
Assert.That(xformSystem.GetWorldPosition(ent2), Is.EqualTo(Vector2.One));
|
||||
|
||||
// Assert that you can set recursively contained (but not directly contained) entities correctly.
|
||||
containerSystem.Insert(ent2, container);
|
||||
xformSystem.SetParent(ent3, ent2);
|
||||
|
||||
Assert.That(xformSystem.GetParentUid(ent3), Is.EqualTo(ent2));
|
||||
|
||||
xformSystem.SetWorldPosition(ent3, Vector2.One);
|
||||
|
||||
Assert.That(xformSystem.GetParentUid(ent3), Is.EqualTo(map1));
|
||||
Assert.That(xformSystem.GetWorldPosition(ent3).Equals(Vector2.One));
|
||||
|
||||
// Cleanup
|
||||
entManager.DeleteEntity(map1);
|
||||
}
|
||||
}
|
||||
@@ -1,473 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameObjects.Components
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(TransformComponent))]
|
||||
sealed class Transform_Test : RobustUnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Server;
|
||||
|
||||
private IEntityManager EntityManager = default!;
|
||||
private IMapManager MapManager = default!;
|
||||
private SharedTransformSystem XformSystem => EntityManager.System<SharedTransformSystem>();
|
||||
|
||||
const string Prototypes = @"
|
||||
- type: entity
|
||||
name: dummy
|
||||
id: mapDummy
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Map
|
||||
index: 123
|
||||
# Due to the map getting initialised last this seemed easiest to fix the test while removing the mocks.
|
||||
- type: Broadphase
|
||||
";
|
||||
|
||||
private MapId MapA;
|
||||
private Entity<MapGridComponent> GridA;
|
||||
private MapId MapB;
|
||||
private Entity<MapGridComponent> GridB;
|
||||
|
||||
private static readonly EntityCoordinates InitialPos = new(EntityUid.FirstUid, new Vector2(0, 0));
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
IoCManager.Resolve<IComponentFactory>().GenerateNetIds();
|
||||
|
||||
EntityManager = IoCManager.Resolve<IEntityManager>();
|
||||
MapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
var manager = IoCManager.Resolve<IPrototypeManager>();
|
||||
manager.RegisterKind(typeof(EntityPrototype), typeof(EntityCategoryPrototype));
|
||||
manager.LoadFromStream(new StringReader(Prototypes));
|
||||
manager.ResolveResults();
|
||||
|
||||
var mapSys = EntityManager.System<SharedMapSystem>();
|
||||
// build the net dream
|
||||
mapSys.CreateMap(out MapA);
|
||||
mapSys.CreateMap(out MapB);
|
||||
|
||||
GridA = MapManager.CreateGridEntity(MapA);
|
||||
GridB = MapManager.CreateGridEntity(MapB);
|
||||
|
||||
//NOTE: The grids have not moved, so we can assert worldpos == localpos for the test
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void ClearSimulation()
|
||||
{
|
||||
// One of the tests changes this so we use this to ensure it doesn't get passed to other tests.
|
||||
IoCManager.Resolve<IGameTiming>().InSimulation = false;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentMapSwitchTest()
|
||||
{
|
||||
// two entities
|
||||
var parent = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var child = EntityManager.SpawnEntity(null, InitialPos);
|
||||
|
||||
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
|
||||
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
|
||||
|
||||
// that are not on the same map
|
||||
XformSystem.SetCoordinates(parent, parentTrans, new EntityCoordinates(GridA, new Vector2(5, 5)));
|
||||
XformSystem.SetCoordinates(child, childTrans, new EntityCoordinates(GridB, new Vector2(4, 4)));
|
||||
|
||||
// if they are parented, the child keeps its world position, but moves to the parents map
|
||||
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(childTrans.MapID, NUnit.Framework.Is.EqualTo(parentTrans.MapID));
|
||||
Assert.That(childTrans.GridUid, NUnit.Framework.Is.EqualTo(parentTrans.GridUid));
|
||||
Assert.That(childTrans.Coordinates, NUnit.Framework.Is.EqualTo(new EntityCoordinates(parent, new Vector2(-1, -1))));
|
||||
Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(new Vector2(4, 4)));
|
||||
});
|
||||
|
||||
// move the parent, and the child should move with it
|
||||
XformSystem.SetLocalPosition(child, new Vector2(6, 6), childTrans);
|
||||
XformSystem.SetWorldPosition(parent, new Vector2(-8, -8));
|
||||
|
||||
Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(new Vector2(-2, -2)));
|
||||
|
||||
// if we detach parent, the child should be left where it was, still relative to parents grid
|
||||
var oldLpos = new Vector2(-2, -2);
|
||||
var oldWpos = XformSystem.GetWorldPosition(childTrans);
|
||||
|
||||
XformSystem.AttachToGridOrMap(child, childTrans);
|
||||
|
||||
// the gridId won't match, because we just detached from the grid entity
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(childTrans.Coordinates.Position, NUnit.Framework.Is.EqualTo(oldLpos));
|
||||
Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(oldWpos));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that a child entity does not move when attaching to a parent.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ParentAttachMoveTest()
|
||||
{
|
||||
// Arrange
|
||||
var parent = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var child = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
|
||||
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
|
||||
XformSystem.SetWorldPosition(parent, new Vector2(5, 5));
|
||||
XformSystem.SetWorldPosition(child, new Vector2(6, 6));
|
||||
|
||||
// Act
|
||||
var oldWpos = XformSystem.GetWorldPosition(childTrans);
|
||||
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
|
||||
var newWpos = XformSystem.GetWorldPosition(childTrans);
|
||||
|
||||
// Assert
|
||||
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that a child entity does not move when attaching to a parent.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ParentDoubleAttachMoveTest()
|
||||
{
|
||||
// Arrange
|
||||
var parent = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var childOne = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var childTwo = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
|
||||
var childOneTrans = EntityManager.GetComponent<TransformComponent>(childOne);
|
||||
var childTwoTrans = EntityManager.GetComponent<TransformComponent>(childTwo);
|
||||
XformSystem.SetWorldPosition(parent, new Vector2(1, 1));
|
||||
XformSystem.SetWorldPosition(childOne, new Vector2(2, 2));
|
||||
XformSystem.SetWorldPosition(childTwo, new Vector2(3, 3));
|
||||
|
||||
// Act
|
||||
var oldWpos = XformSystem.GetWorldPosition(childOneTrans);
|
||||
XformSystem.SetParent(childOne, childOneTrans, parent, parentXform: parentTrans);
|
||||
var newWpos = XformSystem.GetWorldPosition(childOneTrans);
|
||||
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
|
||||
|
||||
oldWpos = XformSystem.GetWorldPosition(childTwoTrans);
|
||||
XformSystem.SetParent(childOne, childOneTrans, parent, parentXform: parentTrans);
|
||||
newWpos = XformSystem.GetWorldPosition(childTwoTrans);
|
||||
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
|
||||
|
||||
oldWpos = XformSystem.GetWorldPosition(childTwoTrans);
|
||||
XformSystem.SetParent(childTwo, childTwoTrans, childOne, parentXform: childOneTrans);
|
||||
newWpos = XformSystem.GetWorldPosition(childTwoTrans);
|
||||
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the entity orbits properly when the parent rotates.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ParentRotateTest()
|
||||
{
|
||||
// Arrange
|
||||
var parent = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var child = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
|
||||
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
|
||||
XformSystem.SetWorldPosition(parent, new Vector2(0, 0));
|
||||
XformSystem.SetWorldPosition(child, new Vector2(2, 0));
|
||||
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
|
||||
|
||||
//Act
|
||||
parentTrans.LocalRotation = new Angle(MathHelper.Pi / 2);
|
||||
|
||||
//Assert
|
||||
var result = XformSystem.GetWorldPosition(childTrans);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(MathHelper.CloseToPercent(result.X, 0));
|
||||
Assert.That(MathHelper.CloseToPercent(result.Y, 2));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the entity orbits properly when the parent rotates and is not at the origin.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ParentTransRotateTest()
|
||||
{
|
||||
// Arrange
|
||||
var parent = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var child = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
|
||||
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
|
||||
XformSystem.SetWorldPosition(parent, new Vector2(1, 1));
|
||||
XformSystem.SetWorldPosition(child, new Vector2(2, 1));
|
||||
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
|
||||
|
||||
//Act
|
||||
parentTrans.LocalRotation = new Angle(MathHelper.Pi / 2);
|
||||
|
||||
//Assert
|
||||
var result = XformSystem.GetWorldPosition(childTrans);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(MathHelper.CloseToPercent(result.X, 1));
|
||||
Assert.That(MathHelper.CloseToPercent(result.Y, 2));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to see if parenting multiple entities with WorldPosition places the leaf properly.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void PositionCompositionTest()
|
||||
{
|
||||
// Arrange
|
||||
var node1 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node2 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node3 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node4 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
|
||||
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
|
||||
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
|
||||
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
|
||||
var node4Trans = EntityManager.GetComponent<TransformComponent>(node4);
|
||||
|
||||
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
|
||||
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
|
||||
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
|
||||
XformSystem.SetWorldPosition(node4, new Vector2(0, 2));
|
||||
|
||||
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
|
||||
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
|
||||
XformSystem.SetParent(node4, node4Trans, node3, parentXform: node3Trans);
|
||||
|
||||
//Act
|
||||
node1Trans.LocalRotation = new Angle(MathHelper.Pi / 2);
|
||||
|
||||
//Assert
|
||||
var result = XformSystem.GetWorldPosition(node4Trans);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result.X, new ApproxEqualityConstraint(-2f));
|
||||
Assert.That(result.Y, new ApproxEqualityConstraint(0f));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to see if setting the world position of a child causes position rounding errors.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ParentLocalPositionRoundingErrorTest()
|
||||
{
|
||||
// Arrange
|
||||
var node1 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node2 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node3 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
|
||||
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
|
||||
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
|
||||
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
|
||||
|
||||
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
|
||||
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
|
||||
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
|
||||
|
||||
XformSystem.SetParent(node1, node1Trans, node2, parentXform: node2Trans);
|
||||
XformSystem.SetParent(node2, node2Trans, node3, parentXform: node3Trans);
|
||||
|
||||
// Act
|
||||
var oldWpos = XformSystem.GetWorldPosition(node3Trans);
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
var dx = i % 2 == 0 ? 5 : -5;
|
||||
XformSystem.SetLocalPosition(node1, node1Trans.LocalPosition + new Vector2(dx, dx), node1Trans);
|
||||
XformSystem.SetLocalPosition(node2, node2Trans.LocalPosition + new Vector2(dx, dx), node2Trans);
|
||||
XformSystem.SetLocalPosition(node3, node3Trans.LocalPosition + new Vector2(dx, dx), node3Trans);
|
||||
}
|
||||
|
||||
var newWpos = XformSystem.GetWorldPosition(node3Trans);
|
||||
|
||||
// Assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(MathHelper.CloseToPercent(oldWpos.X, newWpos.Y), $"{oldWpos.X} should be {newWpos.Y}");
|
||||
Assert.That(MathHelper.CloseToPercent(oldWpos.Y, newWpos.Y), newWpos.ToString);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to see if rotating a parent causes major child position rounding errors.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ParentRotationRoundingErrorTest()
|
||||
{
|
||||
IoCManager.Resolve<IGameTiming>().InSimulation = true;
|
||||
|
||||
// Arrange
|
||||
var node1 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node2 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node3 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
|
||||
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
|
||||
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
|
||||
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
|
||||
|
||||
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
|
||||
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
|
||||
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
|
||||
|
||||
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
|
||||
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
|
||||
|
||||
// Act
|
||||
var oldWpos = XformSystem.GetWorldPosition(node3Trans);
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
node1Trans.LocalRotation += new Angle(MathHelper.Pi);
|
||||
node2Trans.LocalRotation += new Angle(MathHelper.Pi);
|
||||
node3Trans.LocalRotation += new Angle(MathHelper.Pi);
|
||||
}
|
||||
|
||||
var newWpos = XformSystem.GetWorldPosition(node3Trans);
|
||||
|
||||
//NOTE: Yes, this does cause a non-zero error
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(MathHelper.CloseToPercent(oldWpos.X, newWpos.Y, 0.0001f));
|
||||
Assert.That(MathHelper.CloseToPercent(oldWpos.Y, newWpos.Y, 0.0001f));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the world and inverse world transforms are built properly.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TreeComposeWorldMatricesTest()
|
||||
{
|
||||
// Arrange
|
||||
var control = Matrix3x2.Identity;
|
||||
|
||||
var node1 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node2 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node3 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node4 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
|
||||
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
|
||||
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
|
||||
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
|
||||
var node4Trans = EntityManager.GetComponent<TransformComponent>(node4);
|
||||
|
||||
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
|
||||
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
|
||||
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
|
||||
XformSystem.SetWorldPosition(node4, new Vector2(0, 2));
|
||||
|
||||
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
|
||||
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
|
||||
XformSystem.SetParent(node4, node4Trans, node3, parentXform: node3Trans);
|
||||
|
||||
//Act
|
||||
node1Trans.LocalRotation = new Angle(MathHelper.Pi / 6.37);
|
||||
XformSystem.SetWorldPosition(node1, new Vector2(1, 1));
|
||||
|
||||
var worldMat = XformSystem.GetWorldMatrix(node4Trans);
|
||||
var invWorldMat = XformSystem.GetInvWorldMatrix(node4Trans);
|
||||
|
||||
var leftVerifyMatrix = Matrix3x2.Multiply(worldMat, invWorldMat);
|
||||
var rightVerifyMatrix = Matrix3x2.Multiply(invWorldMat, worldMat);
|
||||
|
||||
//Assert
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// these should be the same (A × A-1 = A-1 × A = I)
|
||||
Assert.That(leftVerifyMatrix, new ApproxEqualityConstraint(rightVerifyMatrix));
|
||||
|
||||
// verify matrix == identity matrix (or very close to because float precision)
|
||||
Assert.That(leftVerifyMatrix, new ApproxEqualityConstraint(control));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that world rotation is built properly
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WorldRotationTest()
|
||||
{
|
||||
// Arrange
|
||||
var node1 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node2 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node3 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
|
||||
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
|
||||
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
|
||||
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
|
||||
|
||||
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
|
||||
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
|
||||
|
||||
node1Trans.LocalRotation = Angle.FromDegrees(0);
|
||||
node2Trans.LocalRotation = Angle.FromDegrees(45);
|
||||
node3Trans.LocalRotation = Angle.FromDegrees(45);
|
||||
|
||||
// Act
|
||||
node1Trans.LocalRotation = Angle.FromDegrees(135);
|
||||
|
||||
// Assert (135 + 45 + 45 = 225)
|
||||
var result = XformSystem.GetWorldRotation(node3Trans);
|
||||
Assert.That(result, new ApproxEqualityConstraint(Angle.FromDegrees(225)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that, in a chain A -> B -> C, if A is moved C's world position correctly updates.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void MatrixUpdateTest()
|
||||
{
|
||||
var node1 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node2 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
var node3 = EntityManager.SpawnEntity(null, InitialPos);
|
||||
|
||||
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
|
||||
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
|
||||
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
|
||||
|
||||
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
|
||||
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
|
||||
|
||||
XformSystem.SetLocalPosition(node3, new Vector2(5, 5), node3Trans);
|
||||
XformSystem.SetLocalPosition(node2, new Vector2(5, 5), node2Trans);
|
||||
XformSystem.SetLocalPosition(node1, new Vector2(5, 5), node1Trans);
|
||||
|
||||
Assert.That(XformSystem.GetWorldPosition(node3Trans), new ApproxEqualityConstraint(new Vector2(15, 15)));
|
||||
}
|
||||
|
||||
/*
|
||||
* There used to be a TestMapInitOrder test here. The problem is that the actual game will probably explode if
|
||||
* you start initialising children before parents and the test only worked because of specific setup being done
|
||||
* to prevent this in its use case.
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameObjects
|
||||
{
|
||||
public sealed class ServerEntityNetworkManagerTest
|
||||
{
|
||||
[Test]
|
||||
public void TestMessageSort()
|
||||
{
|
||||
var tickA = new GameTick(5);
|
||||
var tickB = new GameTick(3);
|
||||
var channel = new Mock<INetChannel>().Object;
|
||||
var msgA = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 10};
|
||||
var msgB = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 13};
|
||||
var msgC = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 12};
|
||||
var msgD = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 14};
|
||||
var msgE = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickB, Sequence = 7};
|
||||
var msgF = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickB, Sequence = 4};
|
||||
|
||||
var pq = new PriorityQueue<MsgEntity>(new ServerEntityManager.MessageSequenceComparer())
|
||||
{
|
||||
msgA,
|
||||
msgB,
|
||||
msgC,
|
||||
msgD,
|
||||
msgE,
|
||||
msgF
|
||||
};
|
||||
|
||||
|
||||
Assert.That(pq.Take(), Is.EqualTo(msgF));
|
||||
Assert.That(pq.Take(), Is.EqualTo(msgE));
|
||||
Assert.That(pq.Take(), Is.EqualTo(msgA));
|
||||
Assert.That(pq.Take(), Is.EqualTo(msgC));
|
||||
Assert.That(pq.Take(), Is.EqualTo(msgB));
|
||||
Assert.That(pq.Take(), Is.EqualTo(msgD));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameObjects
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class ThrowingEntityDeletion_Test
|
||||
{
|
||||
private ISimulation _sim = default!;
|
||||
|
||||
const string PROTOTYPES = @"
|
||||
- type: entity
|
||||
id: throwInAdd
|
||||
components:
|
||||
- type: DebugExceptionOnAdd
|
||||
- type: entity
|
||||
id: throwsInInitialize
|
||||
components:
|
||||
- type: DebugExceptionInitialize
|
||||
- type: entity
|
||||
id: throwsInStartup
|
||||
components:
|
||||
- type: DebugExceptionStartup
|
||||
";
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_sim = RobustServerSimulation.NewSimulation()
|
||||
.RegisterComponents(f =>
|
||||
{
|
||||
f.RegisterClass<DebugExceptionOnAddComponent>();
|
||||
f.RegisterClass<DebugExceptionInitializeComponent>();
|
||||
f.RegisterClass<DebugExceptionStartupComponent>();
|
||||
})
|
||||
.RegisterEntitySystems(f => f.LoadExtraSystemType<DebugExceptionSystem>())
|
||||
.RegisterPrototypes(protoMan => protoMan.LoadString(PROTOTYPES))
|
||||
.InitializeInstance();
|
||||
}
|
||||
|
||||
[TestCase("throwInAdd")]
|
||||
[TestCase("throwsInInitialize")]
|
||||
[TestCase("throwsInStartup")]
|
||||
public void Test(string prototypeName)
|
||||
{
|
||||
var entMan = _sim.Resolve<IEntityManager>();
|
||||
_sim.Resolve<IEntityManager>().System<SharedMapSystem>().CreateMap(out var map);
|
||||
|
||||
Assert.That(() => entMan.SpawnEntity(prototypeName, new MapCoordinates(0, 0, map)),
|
||||
Throws.TypeOf<EntityCreationException>());
|
||||
|
||||
Assert.That(entMan.GetEntities().Where(p => entMan.GetComponent<MetaDataComponent>(p).EntityPrototype?.ID == prototypeName), Is.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class DefaultEntityTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple test that just spawns a default entity without any components or modifications and checks that the
|
||||
/// client receives the entity.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestSpawnDefaultEntity()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var sEntMan = server.ResolveDependency<IEntityManager>();
|
||||
var cEntMan = client.ResolveDependency<IEntityManager>();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
var playerMan = server.ResolveDependency<IPlayerManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
|
||||
client.SetConnectTarget(server);
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, false));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
var session = playerMan.Sessions.First();
|
||||
await server.WaitPost(() => playerMan.JoinGame(session));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Spawn a default unmodified entity.
|
||||
NetEntity ent = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
ent = sEntMan.GetNetEntity(sEntMan.Spawn());
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check that server & client both think the entity exists.
|
||||
Assert.That(sEntMan.EntityExists(sEntMan.GetEntity(ent)));
|
||||
Assert.That(cEntMan.EntityExists(cEntMan.GetEntity(ent)));
|
||||
|
||||
// Enable PVS and repeat the test.
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
// Set up map and spawn player entity
|
||||
NetEntity player = default;
|
||||
EntityCoordinates coords = default!;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var map = server.System<SharedMapSystem>().CreateMap();
|
||||
coords = new(map, default);
|
||||
var playerUid = sEntMan.SpawnEntity(null, coords);
|
||||
player = sEntMan.GetNetEntity(playerUid);
|
||||
server.PlayerMan.SetAttachedEntity(session, playerUid);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
Assert.That(sEntMan.EntityExists(sEntMan.GetEntity(player)));
|
||||
Assert.That(cEntMan.EntityExists(cEntMan.GetEntity(player)));
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
ent = sEntMan.GetNetEntity(sEntMan.SpawnAtPosition(null, coords));
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
Assert.That(sEntMan.EntityExists(sEntMan.GetEntity(ent)));
|
||||
Assert.That(cEntMan.EntityExists(cEntMan.GetEntity(ent)));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,450 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class DetachedParentTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that the client can handle an entity getting attached to an entity that is outside of their PVS range, or
|
||||
/// that they have never seen. Previously this could result in entities with improperly assigned GridUids due to
|
||||
/// an existing/initialized entity being attached to an un-initialized entity on an already initialized grid.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestDetachedParent()
|
||||
{
|
||||
var server = StartServer(new() {Pool = false});
|
||||
var client = StartClient(new() {Pool = false});
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var xformSys = server.System<SharedTransformSystem>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sEntMan = server.ResolveDependency<IEntityManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Ensure client & server ticks are synced.
|
||||
// Client runs 1 tick ahead
|
||||
{
|
||||
var sTick = (int)server.Timing.CurTick.Value;
|
||||
var cTick = (int)client.Timing.CurTick.Value;
|
||||
var delta = cTick - sTick;
|
||||
|
||||
if (delta > 1)
|
||||
await server.WaitRunTicks(delta - 1);
|
||||
else if (delta < 1)
|
||||
await client.WaitRunTicks(1 - delta);
|
||||
|
||||
sTick = (int)server.Timing.CurTick.Value;
|
||||
cTick = (int)client.Timing.CurTick.Value;
|
||||
delta = cTick - sTick;
|
||||
Assert.That(delta, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
// Set up map and spawn player
|
||||
MapId mapId = default;
|
||||
EntityUid map = default;
|
||||
EntityUid grid = default;
|
||||
EntityUid parent = default;
|
||||
EntityUid player = default;
|
||||
EntityUid child = default;
|
||||
EntityCoordinates gridCoords = default;
|
||||
EntityCoordinates mapCoords = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
// Cycle through some EntityUids to avoid server-side and client-side uids accidentally matching up.
|
||||
// I made a mistake earlier in this test where I used a server-side uid on the client
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
server.EntMan.DeleteEntity(server.EntMan.SpawnEntity(null, MapCoordinates.Nullspace));
|
||||
}
|
||||
|
||||
map = mapSys.CreateMap(out mapId);
|
||||
|
||||
var gridEnt = mapMan.CreateGridEntity(mapId);
|
||||
mapSys.SetTile(gridEnt.Owner, gridEnt.Comp, Vector2i.Zero, new Tile(1));
|
||||
gridCoords = new EntityCoordinates(gridEnt, .5f, .5f);
|
||||
mapCoords = new EntityCoordinates(map, 200, 200);
|
||||
grid = gridEnt.Owner;
|
||||
|
||||
parent = sEntMan.SpawnEntity(null, gridCoords);
|
||||
player = sEntMan.SpawnEntity(null, gridCoords);
|
||||
child = sEntMan.SpawnEntity(null, mapCoords);
|
||||
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, player);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check that transforms are as expected.
|
||||
var childX = server.Transform(child);
|
||||
var parentX = server.Transform(parent);
|
||||
var playerX = server.Transform(player);
|
||||
var gridX = server.Transform(grid);
|
||||
|
||||
Assert.That(childX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(parentX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(playerX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(gridX.MapID, Is.EqualTo(mapId));
|
||||
|
||||
Assert.That(childX.ParentUid, Is.EqualTo(map));
|
||||
Assert.That(parentX.ParentUid, Is.EqualTo(grid));
|
||||
Assert.That(playerX.ParentUid, Is.EqualTo(grid));
|
||||
Assert.That(gridX.ParentUid, Is.EqualTo(map));
|
||||
|
||||
Assert.That(childX.GridUid, Is.Null);
|
||||
Assert.That(parentX.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(playerX.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(gridX.GridUid, Is.EqualTo(grid));
|
||||
|
||||
// Check that the player received the entities, and that their transforms are as expected.
|
||||
// Note that the child entity should be outside of PVS range.
|
||||
|
||||
var cMap = client.EntMan.GetEntity(server.EntMan.GetNetEntity(map));
|
||||
var cGrid = client.EntMan.GetEntity(server.EntMan.GetNetEntity(grid));
|
||||
var cPlayer = client.EntMan.GetEntity(server.EntMan.GetNetEntity(player));
|
||||
var cParent = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent));
|
||||
var cChild = client.EntMan.GetEntity(server.EntMan.GetNetEntity(child));
|
||||
|
||||
Assert.That(cMap, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cGrid, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cPlayer, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cParent, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cChild, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
var cParentX = client.Transform(cParent);
|
||||
var cPlayerX = client.Transform(cPlayer);
|
||||
var cGridX = client.Transform(cGrid);
|
||||
|
||||
Assert.That(cParentX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(cGridX.MapID, Is.EqualTo(mapId));
|
||||
|
||||
Assert.That(cParentX.ParentUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cGridX.ParentUid, Is.EqualTo(cMap));
|
||||
|
||||
Assert.That(cParentX.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cGridX.GridUid, Is.EqualTo(cGrid));
|
||||
|
||||
// Move the player into pvs range of the child, which will move them outside of the grid & parent's PVS range.
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(player, mapCoords));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// the client now knows about the child.
|
||||
cChild = client.EntMan.GetEntity(server.EntMan.GetNetEntity(child));
|
||||
Assert.That(cChild, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
var cChildX = client.Transform(cChild);
|
||||
Assert.That(childX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(cChildX.ParentUid, Is.EqualTo(cMap));
|
||||
Assert.That(cChildX.GridUid, Is.Null);
|
||||
|
||||
// Player transform has updated
|
||||
Assert.That(cPlayerX.GridUid, Is.Null);
|
||||
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cMap));
|
||||
|
||||
// But the other entities have left PVS range
|
||||
Assert.That(cParentX.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cParentX.MapID, Is.EqualTo(MapId.Nullspace));
|
||||
Assert.That(cParentX.GridUid, Is.Null);
|
||||
Assert.That((client.MetaData(cParent).Flags & MetaDataFlags.Detached) != 0);
|
||||
|
||||
// Attach the child & player entities to the parent
|
||||
// This is the main step that the test is actually checking
|
||||
|
||||
var parentCoords = new EntityCoordinates(parent, Vector2.Zero);
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(player, parentCoords));
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(child, parentCoords));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check that server-side transforms are as expected
|
||||
Assert.That(childX.ParentUid, Is.EqualTo(parent));
|
||||
Assert.That(parentX.ParentUid, Is.EqualTo(grid));
|
||||
Assert.That(playerX.ParentUid, Is.EqualTo(parent));
|
||||
Assert.That(gridX.ParentUid, Is.EqualTo(map));
|
||||
|
||||
Assert.That(childX.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(parentX.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(playerX.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(gridX.GridUid, Is.EqualTo(grid));
|
||||
|
||||
// Next check the client-side transforms
|
||||
Assert.That((client.MetaData(cParent).Flags & MetaDataFlags.Detached) == 0);
|
||||
|
||||
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent));
|
||||
Assert.That(cParentX.ParentUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent));
|
||||
Assert.That(cGridX.ParentUid, Is.EqualTo(cMap));
|
||||
|
||||
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cParentX.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cGridX.GridUid, Is.EqualTo(cGrid));
|
||||
|
||||
// Repeat the previous test, but this time attaching to an entity that gets spawned outside of PVS range, that
|
||||
// the client never new about previously.
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(player, mapCoords));
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(child, mapCoords));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Child transform has updated.
|
||||
Assert.That(childX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(cChildX.ParentUid, Is.EqualTo(cMap));
|
||||
Assert.That(cChildX.GridUid, Is.Null);
|
||||
|
||||
// Player transform has updated
|
||||
Assert.That(cPlayerX.GridUid, Is.Null);
|
||||
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cMap));
|
||||
|
||||
// The other entities have left PVS range
|
||||
Assert.That(cParentX.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cParentX.MapID, Is.EqualTo(MapId.Nullspace));
|
||||
Assert.That(cParentX.GridUid, Is.Null);
|
||||
Assert.That((client.MetaData(cParent).Flags & MetaDataFlags.Detached) != 0);
|
||||
|
||||
// Create a new parent entity
|
||||
EntityUid parent2 = default;
|
||||
await server.WaitPost(() => parent2 = sEntMan.SpawnEntity(null, gridCoords));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
var parent2X = server.Transform(parent2);
|
||||
Assert.That(parent2X.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(parent2X.ParentUid, Is.EqualTo(grid));
|
||||
Assert.That(parent2X.GridUid, Is.EqualTo(grid));
|
||||
|
||||
// Client does not know that parent2 exists yet.
|
||||
var cParent2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent2));
|
||||
Assert.That(cParent2, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
// Attach player & child to the new parent.
|
||||
var parent2Coords = new EntityCoordinates(parent2, Vector2.Zero);
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(player, parent2Coords));
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(child, parent2Coords));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check all the transforms
|
||||
cParent2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent2));
|
||||
Assert.That(cParent2, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
var cParent2X = client.Transform(cParent2);
|
||||
|
||||
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent2));
|
||||
Assert.That(cParent2X.ParentUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent2));
|
||||
Assert.That(cGridX.ParentUid, Is.EqualTo(cMap));
|
||||
|
||||
Assert.That(cParent2X.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid));
|
||||
Assert.That(cGridX.GridUid, Is.EqualTo(cGrid));
|
||||
|
||||
// Repeat again, but with a new map.
|
||||
// Set up map and spawn player
|
||||
MapId mapId2 = default;
|
||||
EntityUid map2 = default;
|
||||
EntityUid grid2 = default;
|
||||
EntityUid parent3 = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
map2 = mapSys.CreateMap(out mapId2);
|
||||
var gridEnt = mapMan.CreateGridEntity(mapId2);
|
||||
mapSys.SetTile(gridEnt.Owner, gridEnt.Comp, Vector2i.Zero, new Tile(1));
|
||||
var grid2Coords = new EntityCoordinates(gridEnt, .5f, .5f);
|
||||
grid2 = gridEnt.Owner;
|
||||
parent3 = sEntMan.SpawnEntity(null, grid2Coords);
|
||||
});
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check server-side transforms
|
||||
var grid2X = server.Transform(grid2);
|
||||
var parent3X = server.Transform(parent3);
|
||||
|
||||
Assert.That(parent3X.MapID, Is.EqualTo(mapId2));
|
||||
Assert.That(grid2X.MapID, Is.EqualTo(mapId2));
|
||||
|
||||
Assert.That(parent3X.ParentUid, Is.EqualTo(grid2));
|
||||
Assert.That(grid2X.ParentUid, Is.EqualTo(map2));
|
||||
|
||||
Assert.That(parent3X.GridUid, Is.EqualTo(grid2));
|
||||
Assert.That(grid2X.GridUid, Is.EqualTo(grid2));
|
||||
|
||||
// Client does not know that parent3 exists, but (at least for now) clients always know about all maps and grids.
|
||||
var cParent3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent3));
|
||||
var cGrid2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(grid2));
|
||||
var cMap2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(map2));
|
||||
Assert.That(cMap2, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cGrid2, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cParent3, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
// Attach the entities to the parent on the new map.
|
||||
var parent3Coords = new EntityCoordinates(parent3, Vector2.Zero);
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(player, parent3Coords));
|
||||
await server.WaitPost(() => xformSys.SetCoordinates(child, parent3Coords));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check all the transforms
|
||||
cParent3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent3));
|
||||
Assert.That(cParent3, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
|
||||
var cParent3X = client.Transform(cParent3);
|
||||
var cGrid2X = client.Transform(cGrid2);
|
||||
|
||||
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent3));
|
||||
Assert.That(cParent3X.ParentUid, Is.EqualTo(cGrid2));
|
||||
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent3));
|
||||
Assert.That(cGrid2X.ParentUid, Is.EqualTo(cMap2));
|
||||
|
||||
Assert.That(cParent3X.GridUid, Is.EqualTo(cGrid2));
|
||||
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid2));
|
||||
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid2));
|
||||
Assert.That(cGrid2X.GridUid, Is.EqualTo(cGrid2));
|
||||
|
||||
Assert.That(cParent3X.MapID, Is.EqualTo(mapId2));
|
||||
Assert.That(cChildX.MapID, Is.EqualTo(mapId2));
|
||||
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId2));
|
||||
Assert.That(cGrid2X.MapID, Is.EqualTo(mapId2));
|
||||
|
||||
Assert.That(cParent3X.MapUid, Is.EqualTo(cMap2));
|
||||
Assert.That(cChildX.MapUid, Is.EqualTo(cMap2));
|
||||
Assert.That(cPlayerX.MapUid, Is.EqualTo(cMap2));
|
||||
Assert.That(cGrid2X.MapUid, Is.EqualTo(cMap2));
|
||||
|
||||
|
||||
// Create a new map & grid and move entities in the same tick
|
||||
MapId mapId3 = default;
|
||||
EntityUid map3 = default;
|
||||
EntityUid grid3 = default;
|
||||
EntityUid parent4 = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
map3 = mapSys.CreateMap(out mapId3);
|
||||
var gridEnt = mapMan.CreateGridEntity(mapId3);
|
||||
mapSys.SetTile(gridEnt.Owner, gridEnt.Comp, Vector2i.Zero, new Tile(1));
|
||||
var grid3Coords = new EntityCoordinates(gridEnt, .5f, .5f);
|
||||
grid3 = gridEnt.Owner;
|
||||
parent4 = sEntMan.SpawnEntity(null, grid3Coords);
|
||||
|
||||
var parent4Coords = new EntityCoordinates(parent4, Vector2.Zero);
|
||||
|
||||
// Move existing entity to new parent
|
||||
xformSys.SetCoordinates(player, parent4Coords);
|
||||
|
||||
// Move existing parent & child combination to new grid
|
||||
xformSys.SetCoordinates(parent3, grid3Coords);
|
||||
});
|
||||
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check all the transforms
|
||||
var cParent4 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent4));
|
||||
var cMap3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(map3));
|
||||
var cGrid3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(grid3));
|
||||
|
||||
Assert.That(cParent4, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cMap3, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(cGrid3, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
|
||||
var cParent4X = client.Transform(cParent4);
|
||||
var cGrid3X = client.Transform(cGrid3);
|
||||
|
||||
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent3));
|
||||
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent4));
|
||||
Assert.That(cParent3X.ParentUid, Is.EqualTo(cGrid3));
|
||||
Assert.That(cParent4X.ParentUid, Is.EqualTo(cGrid3));
|
||||
Assert.That(cGrid3X.ParentUid, Is.EqualTo(cMap3));
|
||||
|
||||
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid3));
|
||||
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid3));
|
||||
Assert.That(cParent3X.GridUid, Is.EqualTo(cGrid3));
|
||||
Assert.That(cParent4X.GridUid, Is.EqualTo(cGrid3));
|
||||
Assert.That(cGrid3X.GridUid, Is.EqualTo(cGrid3));
|
||||
|
||||
Assert.That(cChildX.MapID, Is.EqualTo(mapId3));
|
||||
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId3));
|
||||
Assert.That(cParent3X.MapID, Is.EqualTo(mapId3));
|
||||
Assert.That(cParent4X.MapID, Is.EqualTo(mapId3));
|
||||
Assert.That(cGrid3X.MapID, Is.EqualTo(mapId3));
|
||||
|
||||
Assert.That(cChildX.MapUid, Is.EqualTo(cMap3));
|
||||
Assert.That(cPlayerX.MapUid, Is.EqualTo(cMap3));
|
||||
Assert.That(cParent3X.MapUid, Is.EqualTo(cMap3));
|
||||
Assert.That(cParent4X.MapUid, Is.EqualTo(cMap3));
|
||||
Assert.That(cGrid3X.MapUid, Is.EqualTo(cMap3));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class MissingParentTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that PVS & clients can handle entities being sent before their parents are.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestMissingParent()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sEntMan = server.ResolveDependency<IEntityManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
|
||||
var cEntMan = client.ResolveDependency<IEntityManager>();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
|
||||
var cConfMan = client.ResolveDependency<IConfigurationManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Limit client to receiving at most 1 entity per tick.
|
||||
cConfMan.SetCVar(CVars.NetPVSEntityBudget, 1);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Ensure client & server ticks are synced.
|
||||
// Client runs 1 tick ahead
|
||||
{
|
||||
var sTick = (int)server.Timing.CurTick.Value;
|
||||
var cTick = (int)client.Timing.CurTick.Value;
|
||||
var delta = cTick - sTick;
|
||||
|
||||
if (delta > 1)
|
||||
await server.WaitRunTicks(delta - 1);
|
||||
else if (delta < 1)
|
||||
await client.WaitRunTicks(1 - delta);
|
||||
|
||||
sTick = (int)server.Timing.CurTick.Value;
|
||||
cTick = (int)client.Timing.CurTick.Value;
|
||||
delta = cTick - sTick;
|
||||
Assert.That(delta, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
// Set up map and spawn player
|
||||
NetEntity player = default;
|
||||
NetEntity entity = default;
|
||||
EntityCoordinates coords = default;
|
||||
NetCoordinates nCoords = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var map = server.System<SharedMapSystem>().CreateMap();
|
||||
coords = new(map, default);
|
||||
|
||||
var playerUid = sEntMan.SpawnEntity(null, coords);
|
||||
var entUid = sEntMan.SpawnEntity(null, coords);
|
||||
entity = sEntMan.GetNetEntity(entUid);
|
||||
player = sEntMan.GetNetEntity(playerUid);
|
||||
nCoords = sEntMan.GetNetCoordinates(coords);
|
||||
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, playerUid);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
Assert.That(entity, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
|
||||
// Check player got properly attached, and has received the other entity.
|
||||
Assert.That(cEntMan.TryGetEntityData(entity, out _, out var meta));
|
||||
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
|
||||
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
|
||||
Assert.That(server.Transform(player).Coordinates, Is.EqualTo(coords));
|
||||
Assert.That(client.Transform(player).Coordinates, Is.EqualTo(client.EntMan.GetCoordinates(nCoords)));
|
||||
Assert.That(client.Transform(entity).ParentUid.IsValid(), Is.True);
|
||||
Assert.That(client.MetaData(entity).Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
|
||||
// Spawn 20 new entities
|
||||
NetEntity first = default;
|
||||
NetEntity last = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
first = sEntMan.GetNetEntity(sEntMan.SpawnEntity(null, coords));
|
||||
for (var i = 0; i < 18; i++)
|
||||
{
|
||||
sEntMan.SpawnEntity(null, coords);
|
||||
}
|
||||
last = sEntMan.GetNetEntity(sEntMan.SpawnEntity(null, coords));
|
||||
});
|
||||
|
||||
// Wait for the client to receive some, but not all, of the entities
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
Assert.That(cEntMan.TryGetEntity(first, out _), Is.True);
|
||||
Assert.That(cEntMan.TryGetEntity(last, out _), Is.False);
|
||||
|
||||
// Re-parent the known entity to an entity that the client has not received yet.
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var newCoords = new EntityCoordinates(sEntMan.GetEntity(last), default);
|
||||
server.System<SharedTransformSystem>().SetCoordinates(sEntMan.GetEntity(entity), newCoords);
|
||||
});
|
||||
|
||||
// Wait a few more ticks
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Client should still not have received the new parent, however this shouldn't cause any issues.
|
||||
// The already known entity should just have been moved to nullspace.
|
||||
Assert.That(cEntMan.TryGetEntity(last, out _), Is.False);
|
||||
Assert.That(client.Transform(entity).ParentUid.IsValid(), Is.False);
|
||||
Assert.That(client.MetaData(entity).Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
|
||||
// Wait untill the client receives the parent entity
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// now that the parent was received the entity should no longer be in nullspace.
|
||||
Assert.That(cEntMan.TryGetEntity(last, out var newParent), Is.True);
|
||||
Assert.That(client.Transform(entity).ParentUid.IsValid(), Is.True);
|
||||
Assert.That(client.Transform(entity).ParentUid, Is.EqualTo(newParent));
|
||||
Assert.That(client.MetaData(entity).Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class PvsChunkTest : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestGridMapChange()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sEntMan = server.ResolveDependency<IEntityManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var xforms = sEntMan.System<SharedTransformSystem>();
|
||||
var mapSys = sEntMan.System<MapSystem>();
|
||||
|
||||
var cEntMan = client.ResolveDependency<IEntityManager>();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Ensure client & server ticks are synced.
|
||||
// Client runs 1 tick ahead
|
||||
{
|
||||
var sTick = (int)server.Timing.CurTick.Value;
|
||||
var cTick = (int)client.Timing.CurTick.Value;
|
||||
var delta = cTick - sTick;
|
||||
|
||||
if (delta > 1)
|
||||
await server.WaitRunTicks(delta - 1);
|
||||
else if (delta < 1)
|
||||
await client.WaitRunTicks(1 - delta);
|
||||
|
||||
sTick = (int)server.Timing.CurTick.Value;
|
||||
cTick = (int)client.Timing.CurTick.Value;
|
||||
delta = cTick - sTick;
|
||||
Assert.That(delta, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
// Set up entities
|
||||
EntityUid map1 = default;
|
||||
EntityUid map2 = default;
|
||||
EntityUid grid = default;
|
||||
EntityUid player = default;
|
||||
EntityUid entity = default;
|
||||
EntityCoordinates mapCoords = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
map1 = server.System<SharedMapSystem>().CreateMap();
|
||||
mapCoords = new(map1, default);
|
||||
|
||||
map2 = server.System<SharedMapSystem>().CreateMap();
|
||||
var gridComp = mapMan.CreateGridEntity(map2);
|
||||
grid = gridComp.Owner;
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
var gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
|
||||
player = sEntMan.SpawnEntity(null, mapCoords);
|
||||
entity = sEntMan.SpawnEntity(null, gridCoords);
|
||||
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, player);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
var nEntity = sEntMan.GetNetEntity(entity);
|
||||
var nGrid = sEntMan.GetNetEntity(grid);
|
||||
var nMap1 = sEntMan.GetNetEntity(map1);
|
||||
var nMap2 = sEntMan.GetNetEntity(map2);
|
||||
|
||||
var xform = sEntMan.GetComponent<TransformComponent>(entity);
|
||||
Assert.That(xform.ParentUid, Is.EqualTo(grid));
|
||||
Assert.That(xform.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(xform.MapUid, Is.EqualTo(map2));
|
||||
|
||||
Assert.That(!cEntMan.TryGetEntity(nEntity, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nMap1, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nMap2, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nGrid, out _));
|
||||
|
||||
// Teleport grid to new map
|
||||
await server.WaitPost(() => xforms.SetCoordinates(grid, mapCoords));
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
Assert.That(xform.ParentUid, Is.EqualTo(grid));
|
||||
Assert.That(xform.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(xform.MapUid, Is.EqualTo(map1));
|
||||
|
||||
Assert.That(cEntMan.TryGetEntity(nEntity, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nMap1, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nMap2, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nGrid, out _));
|
||||
|
||||
// Delete the original map.
|
||||
await server.WaitPost(() => sEntMan.DeleteEntity(map2));
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
Assert.That(xform.ParentUid, Is.EqualTo(grid));
|
||||
Assert.That(xform.GridUid, Is.EqualTo(grid));
|
||||
Assert.That(xform.MapUid, Is.EqualTo(map1));
|
||||
|
||||
Assert.That(cEntMan.TryGetEntity(nEntity, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nMap1, out _));
|
||||
Assert.That(!cEntMan.TryGetEntity(nMap2, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nGrid, out _));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class PvsPauseTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that the client "pauses" entities that have left their PVS range.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task PauseTest()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var sEntMan = server.EntMan;
|
||||
var confMan = server.CfgMan;
|
||||
var sPlayerMan = server.PlayerMan;
|
||||
var xforms = sEntMan.System<SharedTransformSystem>();
|
||||
var metaSys = sEntMan.System<MetaDataSystem>();
|
||||
var stateMan = (ClientGameStateManager) client.ResolveDependency<IClientGameStateManager>();
|
||||
|
||||
var cEntMan = client.EntMan;
|
||||
var cPlayerMan = client.PlayerMan;
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
async Task RunTicks()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
}
|
||||
|
||||
await RunTicks();
|
||||
|
||||
// Set up map and spawn player
|
||||
EntityUid map = default;
|
||||
EntityUid playerUid = default;
|
||||
EntityUid sEnt = default;
|
||||
EntityCoordinates coords = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
map = server.System<SharedMapSystem>().CreateMap();
|
||||
coords = new(map, default);
|
||||
|
||||
playerUid = sEntMan.SpawnEntity(null, coords);
|
||||
sEnt = sEntMan.SpawnEntity(null, coords);
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, playerUid);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
await RunTicks();
|
||||
var farAway = new EntityCoordinates(map, new Vector2(100, 100));
|
||||
var netEnt = sEntMan.GetNetEntity(sEnt);
|
||||
var player = sEntMan.GetNetEntity(playerUid);
|
||||
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
Assert.That(netEnt, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
|
||||
// Check player got properly attached, and has received the other entity.
|
||||
Assert.That(cEntMan.TryGetEntity(netEnt, out var uid));
|
||||
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
|
||||
var cEnt = uid!.Value;
|
||||
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
|
||||
|
||||
void AssertEnt(bool paused, bool detached, bool clientPaused)
|
||||
{
|
||||
var cMeta = client.MetaData(cEnt);
|
||||
var sMeta = server.MetaData(sEnt);
|
||||
|
||||
Assert.That(cMeta.Flags.HasFlag(MetaDataFlags.Detached), Is.EqualTo(detached));
|
||||
Assert.That(sMeta.Flags.HasFlag(MetaDataFlags.Detached), Is.False);
|
||||
|
||||
Assert.That(stateMan.IsQueuedForDetach(netEnt), Is.False);
|
||||
Assert.That(sMeta.EntityPaused, Is.EqualTo(paused));
|
||||
Assert.That(cMeta.EntityPaused, Is.EqualTo(clientPaused));
|
||||
|
||||
if (detached)
|
||||
Assert.That(cMeta.PauseTime, Is.EqualTo(TimeSpan.Zero));
|
||||
if (clientPaused)
|
||||
Assert.That(cMeta.PauseTime, Is.GreaterThanOrEqualTo(TimeSpan.Zero));
|
||||
else
|
||||
Assert.That(cMeta.PauseTime, Is.Null);
|
||||
|
||||
if (paused)
|
||||
Assert.That(sMeta.PauseTime, Is.GreaterThan(TimeSpan.Zero));
|
||||
else
|
||||
Assert.That(sMeta.PauseTime, Is.Null);
|
||||
}
|
||||
|
||||
// Entity is initially in view and not paused.
|
||||
AssertEnt(paused: false, detached: false, clientPaused: false);
|
||||
|
||||
// Move the player out of the entity's PVS range
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
|
||||
await RunTicks();
|
||||
|
||||
// Client should now have detached & locally paused the entity.
|
||||
AssertEnt(paused: false, detached: true, clientPaused: true);
|
||||
|
||||
// Move the player back into range
|
||||
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: false, detached: false, clientPaused: false);
|
||||
|
||||
// Actually pause the entity.
|
||||
await server.WaitPost(() => metaSys.SetEntityPaused(sEnt, true));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: true, detached: false, clientPaused: true);
|
||||
|
||||
// Out of range
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: true, detached: true, clientPaused: true);
|
||||
|
||||
// Back in range
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: true, detached: false, clientPaused: true);
|
||||
|
||||
// Unpause the entity while out of range
|
||||
{
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: true, detached: true, clientPaused: true);
|
||||
|
||||
await server.WaitPost(() => metaSys.SetEntityPaused(sEnt, false));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: false, detached: true, clientPaused: true);
|
||||
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: false, detached: false, clientPaused: false);
|
||||
}
|
||||
|
||||
// Pause the entity while out of range
|
||||
{
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: false, detached: true, clientPaused: true);
|
||||
|
||||
await server.WaitPost(() => metaSys.SetEntityPaused(sEnt, true));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: true, detached: true, clientPaused: true);
|
||||
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
|
||||
await RunTicks();
|
||||
AssertEnt(paused: true, detached: false, clientPaused: true);
|
||||
}
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class PvsReEntryTest : RobustIntegrationTest
|
||||
{
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Checks that there are no issues when an entity enters, leaves, then enters pvs while the client drops packets.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestLossyReEntry()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sEntMan = server.ResolveDependency<IEntityManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var xforms = sEntMan.System<SharedTransformSystem>();
|
||||
var stateMan = (ClientGameStateManager) client.ResolveDependency<IClientGameStateManager>();
|
||||
|
||||
var cEntMan = client.ResolveDependency<IEntityManager>();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Ensure client & server ticks are synced.
|
||||
// Client runs 1 tick ahead
|
||||
{
|
||||
var sTick = (int)server.Timing.CurTick.Value;
|
||||
var cTick = (int)client.Timing.CurTick.Value;
|
||||
var delta = cTick - sTick;
|
||||
|
||||
if (delta > 1)
|
||||
await server.WaitRunTicks(delta - 1);
|
||||
else if (delta < 1)
|
||||
await client.WaitRunTicks(1 - delta);
|
||||
|
||||
sTick = (int)server.Timing.CurTick.Value;
|
||||
cTick = (int)client.Timing.CurTick.Value;
|
||||
delta = cTick - sTick;
|
||||
Assert.That(delta, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
// Set up map and spawn player
|
||||
EntityUid map = default;
|
||||
NetEntity player = default;
|
||||
NetEntity entity = default;
|
||||
EntityCoordinates coords = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
map = server.System<SharedMapSystem>().CreateMap();
|
||||
coords = new(map, default);
|
||||
|
||||
var playerUid = sEntMan.SpawnEntity(null, coords);
|
||||
var entUid = sEntMan.SpawnEntity(null, coords);
|
||||
entity = sEntMan.GetNetEntity(entUid);
|
||||
player = sEntMan.GetNetEntity(playerUid);
|
||||
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, playerUid);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
Assert.That(entity, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
|
||||
// Check player got properly attached, and has received the other entity.
|
||||
MetaDataComponent? meta = default!;
|
||||
await client.WaitPost(() =>
|
||||
{
|
||||
Assert.That(cEntMan.TryGetEntityData(entity, out _, out meta));
|
||||
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
|
||||
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
|
||||
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
|
||||
});
|
||||
|
||||
var lastDirty = meta.LastModifiedTick;
|
||||
Assert.That(lastDirty, Is.GreaterThan(GameTick.Zero));
|
||||
|
||||
// Move the player outside of the entity's PVS range
|
||||
// note that we move the PLAYER not the entity, as we don't want to dirty the entity.
|
||||
var farAway = new EntityCoordinates(map, new Vector2(100, 100));
|
||||
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Client should have detached the entity to null space.
|
||||
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.Detached));
|
||||
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
|
||||
|
||||
// Move the player back into range
|
||||
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Entity is back in pvs range
|
||||
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
|
||||
|
||||
// Oh no, the client is going through a tunnel!
|
||||
stateMan.DropStates = true;
|
||||
var timing = client.ResolveDependency<IClientGameTiming>();
|
||||
var lastRealTick = timing.LastRealTick;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Even though the client is receiving no new states, it will still have applied some from the state buffer.
|
||||
Assert.That(timing.LastRealTick, Is.GreaterThan(lastRealTick));
|
||||
lastRealTick = timing.LastRealTick;
|
||||
|
||||
// Move the player outside of pvs range.
|
||||
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Client should have exhausted the buffer -- client has not been applying any states.
|
||||
Assert.That(timing.LastRealTick, Is.EqualTo(lastRealTick));
|
||||
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
|
||||
// However pvs-leave messages are sent separately, and are sent reliably. So they are still being received.
|
||||
// Though in a realistic scenario they should probably be delayed somewhat.
|
||||
Assert.That(stateMan.IsQueuedForDetach(entity), Is.True);
|
||||
|
||||
// Move the entity back into range
|
||||
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Still hasn't been applying states.
|
||||
Assert.That(timing.LastRealTick, Is.EqualTo(lastRealTick));
|
||||
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
|
||||
// The pvs-leave message has not yet been processed, detaching is still queued.
|
||||
Assert.That(stateMan.IsQueuedForDetach(entity), Is.True);
|
||||
|
||||
// Client clears the tunnel, starts receiving states again.
|
||||
stateMan.DropStates = false;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Entity should be in PVS range, client should know about it:
|
||||
Assert.That(timing.LastRealTick, Is.GreaterThan(lastRealTick));
|
||||
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
|
||||
|
||||
// The entity itself should not have been dirtied since creation -- only the player has been moving.
|
||||
// If the test moves the entity instead of the player, then the test doesn't actually work.
|
||||
Assert.That(meta.LastModifiedTick, Is.EqualTo(lastDirty));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class PvsResetTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that the client doesn't reset dirty detached entities. They should remain in nullspace.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ResetTest()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var sEntMan = server.EntMan;
|
||||
var confMan = server.CfgMan;
|
||||
var sPlayerMan = server.PlayerMan;
|
||||
var xforms = sEntMan.System<SharedTransformSystem>();
|
||||
|
||||
var cEntMan = client.EntMan;
|
||||
var cPlayerMan = client.PlayerMan;
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
async Task RunTicks()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
}
|
||||
|
||||
await RunTicks();
|
||||
|
||||
// Set up map and spawn player
|
||||
EntityUid sMap = default;
|
||||
EntityUid playerUid = default;
|
||||
EntityUid sEnt = default;
|
||||
EntityCoordinates coords = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
sMap = server.System<SharedMapSystem>().CreateMap();
|
||||
coords = new(sMap, default);
|
||||
|
||||
playerUid = sEntMan.SpawnEntity(null, coords);
|
||||
sEnt = sEntMan.SpawnEntity(null, coords);
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, playerUid);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
await RunTicks();
|
||||
var farAway = new EntityCoordinates(sMap, new Vector2(100, 100));
|
||||
var netEnt = sEntMan.GetNetEntity(sEnt);
|
||||
var player = sEntMan.GetNetEntity(playerUid);
|
||||
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
Assert.That(netEnt, Is.Not.EqualTo(NetEntity.Invalid));
|
||||
|
||||
// Check player got properly attached, and has received the other entity.
|
||||
Assert.That(cEntMan.TryGetEntity(netEnt, out var uid));
|
||||
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
|
||||
var cEnt = uid!.Value;
|
||||
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
|
||||
var cMap = cEntMan.GetEntity(sEntMan.GetNetEntity(sMap));
|
||||
|
||||
void AssertDetached(bool detached)
|
||||
{
|
||||
var cXform = client.Transform(cEnt);
|
||||
var sXform = server.Transform(sEnt);
|
||||
var meta = client.MetaData(cEnt);
|
||||
|
||||
Assert.That(sXform.MapUid, Is.EqualTo(sMap));
|
||||
Assert.That(sXform.ParentUid, Is.EqualTo(sMap));
|
||||
|
||||
if (detached)
|
||||
{
|
||||
Assert.That(meta.Flags.HasFlag(MetaDataFlags.Detached));
|
||||
Assert.That(cXform.MapUid, Is.Null);
|
||||
Assert.That(cXform.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.That(!meta.Flags.HasFlag(MetaDataFlags.Detached));
|
||||
Assert.That(cXform.MapUid, Is.EqualTo(cMap));
|
||||
Assert.That(cXform.ParentUid, Is.EqualTo(cMap));
|
||||
}
|
||||
}
|
||||
|
||||
// Entity is initially in view
|
||||
AssertDetached(false);
|
||||
|
||||
// Move the player out of the entity's PVS range
|
||||
await server.WaitPost(() => xforms.SetCoordinates(playerUid, farAway));
|
||||
await RunTicks();
|
||||
|
||||
// Client should now have detached the entity, moving it into nullspace
|
||||
AssertDetached(true);
|
||||
|
||||
// Marking the entity as dirty due to client-side prediction should have effect
|
||||
await client.WaitPost(() => client.EntMan.Dirty(cEnt, client.Transform(cEnt)));
|
||||
await RunTicks();
|
||||
AssertDetached(true);
|
||||
|
||||
// Move the player back into range
|
||||
await server.WaitPost( () => xforms.SetCoordinates(playerUid, coords));
|
||||
await RunTicks();
|
||||
AssertDetached(false);
|
||||
|
||||
// Marking the entity as dirty due to client-side prediction should have no real effect
|
||||
await client.WaitPost(() => client.EntMan.Dirty(cEnt, client.Transform(cEnt)));
|
||||
await RunTicks();
|
||||
AssertDetached(false);
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.UnitTesting.Server.GameStates;
|
||||
|
||||
public sealed class PvsSystemTests : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks that there are no issues when an entity changes PVS chunk location multiple times in a single tick.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestMultipleIndexChange()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sEntMan = server.ResolveDependency<IEntityManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var xforms = sEntMan.System<SharedTransformSystem>();
|
||||
var maps = sEntMan.System<SharedMapSystem>();
|
||||
|
||||
var cEntMan = client.ResolveDependency<IEntityManager>();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Set up map and grid
|
||||
EntityUid grid = default;
|
||||
EntityUid map = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
map = server.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
var gridComp = mapMan.CreateGridEntity(mapId);
|
||||
maps.SetTile(gridComp, Vector2i.Zero, new Tile(1));
|
||||
grid = gridComp.Owner;
|
||||
});
|
||||
|
||||
// Spawn player entity on grid 1
|
||||
EntityUid player = default;
|
||||
EntityUid other = default;
|
||||
TransformComponent otherXform = default!;
|
||||
var gridCoords = new EntityCoordinates(grid, new Vector2(0.5f, 0.5f));
|
||||
var mapCoords = new EntityCoordinates(map, new Vector2(2, 2));
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
player = sEntMan.SpawnEntity(null, gridCoords);
|
||||
other = sEntMan.SpawnEntity(null, gridCoords);
|
||||
otherXform = sEntMan.GetComponent<TransformComponent>(other);
|
||||
|
||||
// Ensure map PVS chunk is not empty
|
||||
sEntMan.SpawnEntity(null, mapCoords);
|
||||
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, player);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Check player got properly attached
|
||||
await client.WaitPost(() =>
|
||||
{
|
||||
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity);
|
||||
Assert.That(ent, Is.EqualTo(sEntMan.GetNetEntity(player)));
|
||||
});
|
||||
|
||||
// Move the player off-grid and back onto the grid in the same tick
|
||||
xforms.SetCoordinates(other, otherXform, mapCoords);
|
||||
xforms.SetCoordinates(other, otherXform, gridCoords);
|
||||
|
||||
// Run for a few ticks. The test just checks that no PVS asserts/errors happen.
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Repeat but in the opposite direction ( map -> grid -> map )
|
||||
// first move to map and wait a bit.
|
||||
xforms.SetCoordinates(other, otherXform, mapCoords);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Move to and off grid in the same tick
|
||||
xforms.SetCoordinates(other, otherXform, gridCoords);
|
||||
xforms.SetCoordinates(other, otherXform, mapCoords);
|
||||
|
||||
// wait for errors.
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Server.Maps
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed partial class MapLoaderTest : RobustIntegrationTest
|
||||
{
|
||||
private const string MapData = @"
|
||||
meta:
|
||||
format: 7
|
||||
category: Grid
|
||||
engineVersion: 238.0.0
|
||||
forkId: """"
|
||||
forkVersion: """"
|
||||
time: 12/22/2024 04:08:12
|
||||
entityCount: 3
|
||||
maps: []
|
||||
grids:
|
||||
- 1
|
||||
orphans:
|
||||
- 1
|
||||
nullspace: []
|
||||
tilemap: {}
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 1
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: MapGrid
|
||||
chunks: {}
|
||||
- type: Broadphase
|
||||
- type: Physics
|
||||
canCollide: False
|
||||
- type: Fixtures
|
||||
fixtures: {}
|
||||
- type: MapSaveTileMap
|
||||
- proto: MapDeserializeTest
|
||||
entities:
|
||||
- uid: 2
|
||||
mapInit: true
|
||||
components:
|
||||
- type: Transform
|
||||
parent: 1
|
||||
- type: MapDeserializeTest
|
||||
foo: 3
|
||||
";
|
||||
|
||||
private const string Prototype = @"
|
||||
- type: entity
|
||||
id: MapDeserializeTest
|
||||
components:
|
||||
- type: MapDeserializeTest
|
||||
foo: 1
|
||||
bar: 2
|
||||
";
|
||||
|
||||
[Test]
|
||||
public async Task TestDataLoadPriority()
|
||||
{
|
||||
var opts = new ServerIntegrationOptions()
|
||||
{
|
||||
ExtraPrototypes = Prototype
|
||||
};
|
||||
|
||||
var server = StartServer(opts);
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
|
||||
resourceManager.MountString("/TestMap.yml", MapData);
|
||||
|
||||
var traversal = server.System<SharedGridTraversalSystem>();
|
||||
traversal.Enabled = false;
|
||||
var mapLoad = server.System<MapLoaderSystem>();
|
||||
|
||||
Entity<MapGridComponent>? grid = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
server.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
Assert.That(mapLoad.TryLoadGrid(mapId, new ResPath("/TestMap.yml"), out grid));
|
||||
});
|
||||
|
||||
var geid = grid!.Value.Owner;
|
||||
|
||||
var entity = server.EntMan.GetComponent<TransformComponent>(geid)._children.Single();
|
||||
var c = server.EntMan.GetComponent<MapDeserializeTestComponent>(entity);
|
||||
traversal.Enabled = true;
|
||||
|
||||
Assert.That(c.Bar, Is.EqualTo(2));
|
||||
Assert.That(c.Foo, Is.EqualTo(3));
|
||||
Assert.That(c.Baz, Is.EqualTo(-1));
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
private sealed partial class MapDeserializeTestComponent : Component
|
||||
{
|
||||
[DataField("foo")] public int Foo { get; set; } = -1;
|
||||
[DataField("bar")] public int Bar { get; set; } = -1;
|
||||
[DataField("baz")] public int Baz { get; set; } = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Moq;
|
||||
using Robust.Client.HWId;
|
||||
using Robust.Server;
|
||||
using Robust.Server.Configuration;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Server.Debugging;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Server.Localization;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Server.Prototypes;
|
||||
using Robust.Server.Reflection;
|
||||
using Robust.Server.Replays;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Controllers;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Server
|
||||
{
|
||||
[PublicAPI]
|
||||
public interface ISimulationFactory
|
||||
{
|
||||
ISimulationFactory RegisterComponents(CompRegistrationDelegate factory);
|
||||
ISimulationFactory RegisterDependencies(DiContainerDelegate factory);
|
||||
ISimulationFactory RegisterEntitySystems(EntitySystemRegistrationDelegate factory);
|
||||
ISimulationFactory RegisterPrototypes(PrototypeRegistrationDelegate factory);
|
||||
ISimulation InitializeInstance();
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public interface ISimulation
|
||||
{
|
||||
IDependencyCollection Collection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a dependency directly out of IoC collection.
|
||||
/// </summary>
|
||||
T Resolve<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new map directly to the map manager.
|
||||
/// </summary>
|
||||
(EntityUid Uid, MapId MapId) CreateMap();
|
||||
EntityUid SpawnEntity(string? protoId, EntityCoordinates coordinates);
|
||||
EntityUid SpawnEntity(string? protoId, MapCoordinates coordinates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods for working with <see cref="ISimulation"/>.
|
||||
/// </summary>
|
||||
internal static class SimulationExtensions
|
||||
{
|
||||
public static T System<T>(this ISimulation simulation) where T : IEntitySystem
|
||||
{
|
||||
return simulation.Resolve<IEntitySystemManager>().GetEntitySystem<T>();
|
||||
}
|
||||
|
||||
public static bool HasComp<T>(this ISimulation simulation, EntityUid entity) where T : IComponent
|
||||
{
|
||||
return simulation.Resolve<IEntityManager>().HasComponent<T>(entity);
|
||||
}
|
||||
|
||||
public static T Comp<T>(this ISimulation simulation, EntityUid entity) where T : IComponent
|
||||
{
|
||||
return simulation.Resolve<IEntityManager>().GetComponent<T>(entity);
|
||||
}
|
||||
|
||||
public static TransformComponent Transform(this ISimulation simulation, EntityUid entity)
|
||||
{
|
||||
return simulation.Comp<TransformComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void DiContainerDelegate(IDependencyCollection diContainer);
|
||||
|
||||
public delegate void CompRegistrationDelegate(IComponentFactory factory);
|
||||
|
||||
public delegate void EntitySystemRegistrationDelegate(IEntitySystemManager systemMan);
|
||||
|
||||
public delegate void PrototypeRegistrationDelegate(IPrototypeManager protoMan);
|
||||
|
||||
public sealed class RobustServerSimulation : ISimulation, ISimulationFactory
|
||||
{
|
||||
private DiContainerDelegate? _diFactory;
|
||||
private CompRegistrationDelegate? _regDelegate;
|
||||
private EntitySystemRegistrationDelegate? _systemDelegate;
|
||||
private PrototypeRegistrationDelegate? _protoDelegate;
|
||||
|
||||
public IDependencyCollection Collection { get; private set; } = default!;
|
||||
|
||||
public T Resolve<T>()
|
||||
{
|
||||
return Collection.Resolve<T>();
|
||||
}
|
||||
|
||||
public (EntityUid Uid, MapId MapId) CreateMap()
|
||||
{
|
||||
var uid = Collection.Resolve<IEntityManager>().System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
return (uid, mapId);
|
||||
}
|
||||
|
||||
public EntityUid SpawnEntity(string? protoId, EntityCoordinates coordinates)
|
||||
{
|
||||
var entMan = Collection.Resolve<IEntityManager>();
|
||||
return entMan.SpawnEntity(protoId, coordinates);
|
||||
}
|
||||
|
||||
public EntityUid SpawnEntity(string? protoId, MapCoordinates coordinates)
|
||||
{
|
||||
var entMan = Collection.Resolve<IEntityManager>();
|
||||
return entMan.SpawnEntity(protoId, coordinates);
|
||||
}
|
||||
|
||||
private RobustServerSimulation() { }
|
||||
|
||||
public ISimulationFactory RegisterDependencies(DiContainerDelegate factory)
|
||||
{
|
||||
_diFactory += factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISimulationFactory RegisterComponents(CompRegistrationDelegate factory)
|
||||
{
|
||||
_regDelegate += factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISimulationFactory RegisterEntitySystems(EntitySystemRegistrationDelegate factory)
|
||||
{
|
||||
_systemDelegate += factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISimulationFactory RegisterPrototypes(PrototypeRegistrationDelegate factory)
|
||||
{
|
||||
_protoDelegate += factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISimulation InitializeInstance()
|
||||
{
|
||||
var container = new DependencyCollection();
|
||||
Collection = container;
|
||||
|
||||
IoCManager.InitThread(container, true);
|
||||
|
||||
//TODO: This is a long term project that should eventually have parity with the actual server/client/SP IoC registration.
|
||||
// The goal is to be able to pull out all networking and frontend dependencies, and only have a core simulation running.
|
||||
// This does NOT replace the full RobustIntegrationTest, or regular unit testing. This simulation sits in the middle
|
||||
// and allows you to run integration testing only on the simulation.
|
||||
|
||||
//Tier 1: System
|
||||
container.Register<ILogManager, LogManager>();
|
||||
container.Register<IRuntimeLog, RuntimeLog>();
|
||||
container.Register<IConfigurationManager, ServerNetConfigurationManager>();
|
||||
container.Register<INetConfigurationManager, ServerNetConfigurationManager>();
|
||||
container.Register<IConfigurationManagerInternal, ServerNetConfigurationManager>();
|
||||
container.Register<IServerNetConfigurationManager, ServerNetConfigurationManager>();
|
||||
container.Register<INetConfigurationManagerInternal, ServerNetConfigurationManager>();
|
||||
container.Register<IDynamicTypeFactory, DynamicTypeFactory>();
|
||||
container.Register<IDynamicTypeFactoryInternal, DynamicTypeFactory>();
|
||||
container.Register<ILocalizationManager, ServerLocalizationManager>();
|
||||
container.Register<IModLoader, TestingModLoader>();
|
||||
container.Register<IModLoaderInternal, TestingModLoader>();
|
||||
container.Register<ProfManager, ProfManager>();
|
||||
container.RegisterInstance<ITaskManager>(new Mock<ITaskManager>().Object);
|
||||
container.Register<HttpClientHolder>();
|
||||
container.Register<IHttpClientHolder, HttpClientHolder>();
|
||||
container.Register<IHWId, DummyHWId>();
|
||||
|
||||
var realReflection = new ServerReflectionManager();
|
||||
realReflection.LoadAssemblies(new List<Assembly>(2)
|
||||
{
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"),
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Server"),
|
||||
});
|
||||
|
||||
var reflectionManager = new Mock<IReflectionManager>();
|
||||
reflectionManager
|
||||
.Setup(x => x.FindTypesWithAttribute<MeansDataDefinitionAttribute>())
|
||||
.Returns(() => new[]
|
||||
{
|
||||
typeof(DataDefinitionAttribute)
|
||||
});
|
||||
|
||||
reflectionManager
|
||||
.Setup(x => x.FindTypesWithAttribute(typeof(DataDefinitionAttribute)))
|
||||
.Returns(() => new[]
|
||||
{
|
||||
typeof(EntityPrototype),
|
||||
typeof(TransformComponent),
|
||||
typeof(MetaDataComponent)
|
||||
});
|
||||
|
||||
reflectionManager
|
||||
.Setup(x => x.FindTypesWithAttribute<TypeSerializerAttribute>())
|
||||
.Returns(() => realReflection.FindTypesWithAttribute<TypeSerializerAttribute>());
|
||||
|
||||
reflectionManager
|
||||
.Setup(x => x.FindAllTypes())
|
||||
.Returns(() => realReflection.FindAllTypes());
|
||||
|
||||
container.RegisterInstance<IBaseServerInternal>(new Mock<IBaseServerInternal>().Object);
|
||||
container.RegisterInstance<IReflectionManager>(reflectionManager.Object); // tests should not be searching for types
|
||||
container.RegisterInstance<IRobustSerializer>(new Mock<IRobustSerializer>().Object);
|
||||
container.RegisterInstance<IResourceManager>(new Mock<IResourceManager>().Object); // no disk access for tests
|
||||
container.RegisterInstance<IGameTiming>(new Mock<IGameTiming>().Object); // TODO: get timing working similar to RobustIntegrationTest
|
||||
|
||||
//Tier 2: Simulation
|
||||
container.RegisterInstance<IConsoleHost>(new Mock<IConsoleHost>().Object); //Console is technically a frontend, we want to run headless
|
||||
container.Register<IEntityManager, ServerEntityManager>();
|
||||
container.Register<IServerEntityNetworkManager, ServerEntityManager>();
|
||||
container.Register<EntityManager, ServerEntityManager>();
|
||||
container.Register<IMapManager, NetworkedMapManager>();
|
||||
container.Register<INetworkedMapManager, NetworkedMapManager>();
|
||||
container.Register<IMapManagerInternal, NetworkedMapManager>();
|
||||
container.Register<ISerializationManager, SerializationManager>();
|
||||
container.Register<IRobustRandom, RobustRandom>();
|
||||
container.Register<IPrototypeManager, ServerPrototypeManager>();
|
||||
container.Register<IComponentFactory, ComponentFactory>();
|
||||
container.Register<IEntitySystemManager, EntitySystemManager>();
|
||||
container.Register<IManifoldManager, CollisionManager>();
|
||||
container.Register<INetManager, NetManager>();
|
||||
container.Register<IAuthManager, AuthManager>();
|
||||
container.Register<ITileDefinitionManager, TileDefinitionManager>();
|
||||
container.Register<IParallelManager, TestingParallelManager>();
|
||||
container.Register<IParallelManagerInternal, TestingParallelManager>();
|
||||
// Needed for grid fixture debugging.
|
||||
container.Register<IConGroupController, ConGroupController>();
|
||||
container.Register<EntityConsoleHost>();
|
||||
|
||||
// I just wanted to load pvs system
|
||||
container.Register<IServerEntityManager, ServerEntityManager>();
|
||||
container.Register<IServerNetManager, NetManager>();
|
||||
// god help you if you actually need to test pvs functions
|
||||
container.RegisterInstance<IPlayerManager>(new Mock<IPlayerManager>().Object);
|
||||
container.RegisterInstance<ISharedPlayerManager>(new Mock<ISharedPlayerManager>().Object);
|
||||
container.RegisterInstance<IServerGameStateManager>(new Mock<IServerGameStateManager>().Object);
|
||||
container.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
|
||||
container.RegisterInstance<IServerReplayRecordingManager>(new Mock<IServerReplayRecordingManager>().Object);
|
||||
|
||||
_diFactory?.Invoke(container);
|
||||
container.BuildGraph();
|
||||
|
||||
// Because of CVarDef, we have to load every one through reflection
|
||||
// just in case a system needs one of them.
|
||||
var configMan = container.Resolve<IConfigurationManagerInternal>();
|
||||
configMan.Initialize(true);
|
||||
configMan.LoadCVarsFromAssembly(typeof(Program).Assembly); // Server
|
||||
configMan.LoadCVarsFromAssembly(typeof(ProgramShared).Assembly); // Shared
|
||||
configMan.LoadCVarsFromAssembly(typeof(RobustServerSimulation).Assembly); // Tests
|
||||
|
||||
var logMan = container.Resolve<ILogManager>();
|
||||
logMan.RootSawmill.AddHandler(new TestLogHandler(configMan, "SIM"));
|
||||
|
||||
var compFactory = container.Resolve<IComponentFactory>();
|
||||
|
||||
// if only we had some sort of attribute for automatically registering components.
|
||||
compFactory.RegisterClass<MetaDataComponent>();
|
||||
compFactory.RegisterClass<TransformComponent>();
|
||||
compFactory.RegisterClass<MapGridComponent>();
|
||||
compFactory.RegisterClass<MapComponent>();
|
||||
compFactory.RegisterClass<MapLightComponent>();
|
||||
compFactory.RegisterClass<PhysicsComponent>();
|
||||
compFactory.RegisterClass<JointComponent>();
|
||||
compFactory.RegisterClass<EyeComponent>();
|
||||
compFactory.RegisterClass<GridTreeComponent>();
|
||||
compFactory.RegisterClass<JointRelayTargetComponent>();
|
||||
compFactory.RegisterClass<BroadphaseComponent>();
|
||||
compFactory.RegisterClass<ContainerManagerComponent>();
|
||||
compFactory.RegisterClass<FixturesComponent>();
|
||||
compFactory.RegisterClass<CollisionWakeComponent>();
|
||||
compFactory.RegisterClass<OccluderComponent>();
|
||||
compFactory.RegisterClass<OccluderTreeComponent>();
|
||||
compFactory.RegisterClass<CollideOnAnchorComponent>();
|
||||
compFactory.RegisterClass<ActorComponent>();
|
||||
|
||||
_regDelegate?.Invoke(compFactory);
|
||||
|
||||
compFactory.GenerateNetIds();
|
||||
|
||||
var entityMan = container.Resolve<IEntityManager>();
|
||||
entityMan.Initialize();
|
||||
|
||||
var entitySystemMan = container.Resolve<IEntitySystemManager>();
|
||||
|
||||
entitySystemMan.LoadExtraSystemType<PhysicsSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<SharedGridTraversalSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<ContainerSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<JointSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<MapSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<DebugPhysicsSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<DebugRayDrawingSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<BroadPhaseSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<CollisionWakeSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<FixtureSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<GridFixtureSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<TransformSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<EntityLookupSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<ServerMetaDataSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<PvsSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<InputSystem>();
|
||||
entitySystemMan.LoadExtraSystemType<PvsOverrideSystem>();
|
||||
|
||||
_systemDelegate?.Invoke(entitySystemMan);
|
||||
|
||||
var mapManager = container.Resolve<IMapManager>();
|
||||
mapManager.Initialize();
|
||||
|
||||
entityMan.Startup();
|
||||
mapManager.Startup();
|
||||
|
||||
container.Resolve<INetManager>().Initialize(true);
|
||||
container.Resolve<ISerializationManager>().Initialize();
|
||||
|
||||
var protoMan = container.Resolve<IPrototypeManager>();
|
||||
protoMan.Initialize();
|
||||
protoMan.RegisterKind(typeof(EntityPrototype), typeof(EntityCategoryPrototype));
|
||||
_protoDelegate?.Invoke(protoMan);
|
||||
|
||||
// This just exists to set protoMan._hasEverBeenReloaded to True
|
||||
// The code is perfect.
|
||||
protoMan.LoadString("");
|
||||
|
||||
protoMan.ResolveResults();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static ISimulationFactory NewSimulation()
|
||||
{
|
||||
return new RobustServerSimulation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.ViewVariables;
|
||||
using Robust.Server.ViewVariables.Traits;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.UnitTesting.Server.ViewVariables
|
||||
{
|
||||
[Parallelizable]
|
||||
[TestFixture]
|
||||
internal sealed class ViewVariablesTraitMembersTest
|
||||
{
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
var ser = new Mock<IRobustSerializer>();
|
||||
ser.Setup(p => p.CanSerialize(It.IsAny<Type>())).Returns(true);
|
||||
|
||||
var logger = new Mock<ISawmill>();
|
||||
var session = new Mock<IViewVariablesSession>();
|
||||
session.SetupGet(p => p.Object).Returns(new C());
|
||||
session.SetupGet(p => p.ObjectType).Returns(typeof(C));
|
||||
session.SetupGet(p => p.RobustSerializer).Returns(ser.Object);
|
||||
|
||||
var blob = new ViewVariablesTraitMembers(session.Object, logger.Object).DataRequest(new ViewVariablesRequestMembers());
|
||||
Assert.That(blob, Is.TypeOf<ViewVariablesBlobMembers>());
|
||||
|
||||
var blobM = (ViewVariablesBlobMembers) blob!;
|
||||
Assert.That(blobM.MemberGroups, Has.Count.EqualTo(2));
|
||||
|
||||
var group0 = blobM.MemberGroups[0];
|
||||
var group1 = blobM.MemberGroups[1];
|
||||
|
||||
Assert.That(group0.groupName, Does.EndWith("+C"));
|
||||
Assert.That(group1.groupName, Does.EndWith("+A"));
|
||||
|
||||
Assert.That(group0.groupMembers, Has.Count.EqualTo(2));
|
||||
Assert.That(group1.groupMembers, Has.Count.EqualTo(1));
|
||||
|
||||
Assert.That(group0.groupMembers[0].Name, Is.EqualTo("Y"));
|
||||
Assert.That(group0.groupMembers[1].Name, Is.EqualTo("Z"));
|
||||
|
||||
Assert.That(group1.groupMembers[0].Name, Is.EqualTo("X"));
|
||||
}
|
||||
|
||||
#pragma warning disable 649
|
||||
[Virtual]
|
||||
private class A
|
||||
{
|
||||
[ViewVariables] public int X;
|
||||
}
|
||||
|
||||
[Virtual]
|
||||
private class B : A
|
||||
{
|
||||
public int Hidden { get; set; }
|
||||
}
|
||||
|
||||
private sealed class C : B
|
||||
{
|
||||
[ViewVariables] public int Y { get; set; }
|
||||
[ViewVariables] public string? Z { get; set; }
|
||||
}
|
||||
#pragma warning restore 649
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Audio;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class AudioGainVolume_Test
|
||||
{
|
||||
private static float[] _gainValues = new[]
|
||||
{
|
||||
1f,
|
||||
0.5f,
|
||||
0f,
|
||||
};
|
||||
|
||||
private static float[] _volumeValues = new[]
|
||||
{
|
||||
-100f,
|
||||
-3f,
|
||||
0f,
|
||||
1f,
|
||||
100f,
|
||||
};
|
||||
|
||||
[Test, TestCaseSource(nameof(_gainValues))]
|
||||
public void GainCalculationTest(float value)
|
||||
{
|
||||
var volume = SharedAudioSystem.GainToVolume(value);
|
||||
var gain = SharedAudioSystem.VolumeToGain(volume);
|
||||
|
||||
Assert.That(MathHelper.CloseTo(value, gain, 0.01f), $"Expected {value} and found {volume}");
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(_volumeValues))]
|
||||
public void VolumeCalculationTest(float value)
|
||||
{
|
||||
var gain = SharedAudioSystem.VolumeToGain(value);
|
||||
var volume = SharedAudioSystem.GainToVolume(gain);
|
||||
|
||||
Assert.That(MathHelper.CloseTo(value, volume, 0.01f), $"Expected {value} and found {volume}");
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Collections;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Collections;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture, TestOf(typeof(OverflowQueue<>))]
|
||||
public sealed class OverflowDictionary_Test
|
||||
{
|
||||
private static IEnumerable<(int size, int iterations)> TestParams => new[]
|
||||
{
|
||||
(10, 17),
|
||||
(10, 4)
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void ValueDisposerTest()
|
||||
{
|
||||
var disposedCalled = 0;
|
||||
var dict = new OverflowDictionary<int, int>(1, (_) => disposedCalled++);
|
||||
dict.Add(0,0);
|
||||
dict.Add(1,0);
|
||||
Assert.That(dict.ContainsKey(0), Is.False);
|
||||
Assert.That(dict.ContainsKey(1));
|
||||
Assert.That(disposedCalled, Is.EqualTo(1));
|
||||
Assert.That(dict.Count, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestQueue([ValueSource(nameof(TestParams))] (int size, int iterations) test)
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var _ = new OverflowDictionary<int, int>(0);
|
||||
});
|
||||
|
||||
var dict = new OverflowDictionary<int, int>(test.size);
|
||||
|
||||
for (int i = 0; i < test.iterations; i++)
|
||||
{
|
||||
dict.Add(i, i+1);
|
||||
}
|
||||
|
||||
Assert.That(dict.ContainsKey(test.iterations-1));
|
||||
Assert.Throws<InvalidOperationException>(() => dict.Add(test.iterations - 1, 0));
|
||||
|
||||
var overlap = Math.Max(test.iterations - test.size, 0);
|
||||
for (int i = overlap; i < test.iterations; i++)
|
||||
{
|
||||
Assert.That(dict.TryGetValue(i, out var val));
|
||||
Assert.That(val, Is.EqualTo(i+1));
|
||||
}
|
||||
|
||||
if(overlap > 0)
|
||||
{
|
||||
Assert.That(dict.ContainsKey(0), Is.False);
|
||||
Assert.That(dict.Count, Is.EqualTo(test.size));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.That(dict.Count, Is.EqualTo(test.iterations));
|
||||
}
|
||||
|
||||
dict.Clear();
|
||||
Assert.That(dict.Count, Is.EqualTo(0));
|
||||
|
||||
//assert that the clear didnt mess up our internal ordering & array. just to make sure we didnt forget to reset something
|
||||
dict.Add(0, 1);
|
||||
Assert.That(dict.TryGetValue(0, out var value));
|
||||
Assert.That(value, Is.EqualTo(1));
|
||||
|
||||
Assert.Throws<NotImplementedException>(() => dict.Remove(0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Collections;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Collections;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture, TestOf(typeof(OverflowQueue<>))]
|
||||
public sealed class OverflowQueue_Test
|
||||
{
|
||||
private static IEnumerable<(int size, int iterations)> TestParams => new[]
|
||||
{
|
||||
(10, 17),
|
||||
(10, 4)
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestQueue([ValueSource(nameof(TestParams))] (int size, int iterations) test)
|
||||
{
|
||||
var queue = new OverflowQueue<int>(test.size);
|
||||
|
||||
Assert.That(queue.Contains(0), Is.False);
|
||||
|
||||
for (int i = 0; i < test.iterations; i++)
|
||||
{
|
||||
queue.Enqueue(i);
|
||||
}
|
||||
|
||||
var overlap = Math.Max(test.iterations - test.size, 0);
|
||||
for (int i = overlap; i < test.iterations; i++)
|
||||
{
|
||||
Assert.That(queue.Contains(i));
|
||||
}
|
||||
|
||||
Assert.That(queue.Contains(test.iterations-1));
|
||||
Assert.That(queue.Contains(-1), Is.False);
|
||||
|
||||
Assert.That(queue.Peek(), Is.EqualTo(overlap));
|
||||
var array = queue.ToArray();
|
||||
Assert.That(array.Length, Is.EqualTo(Math.Min(test.size, test.iterations)));
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
{
|
||||
Assert.That(array[i], Is.EqualTo(i + overlap));
|
||||
}
|
||||
|
||||
for (int i = overlap; i < test.iterations; i++)
|
||||
{
|
||||
Assert.That(queue.TryDequeue(out var item));
|
||||
Assert.That(item, Is.EqualTo(i));
|
||||
}
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => queue.Dequeue());
|
||||
Assert.That(queue.TryDequeue(out _), Is.False);
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Collections;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Collections;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture, TestOf(typeof(RingBufferList<>))]
|
||||
public sealed class RingBufferListTest
|
||||
{
|
||||
[Test]
|
||||
public void TestBasicAdd()
|
||||
{
|
||||
var list = new RingBufferList<int>();
|
||||
list.Add(1);
|
||||
list.Add(2);
|
||||
list.Add(3);
|
||||
|
||||
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 3}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicAddAfterWrap()
|
||||
{
|
||||
var list = new RingBufferList<int>(6);
|
||||
list.Add(1);
|
||||
list.Add(2);
|
||||
list.Add(3);
|
||||
list.RemoveAt(0);
|
||||
list.Add(4);
|
||||
list.Add(5);
|
||||
list.Add(6);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Ensure wrapping properly happened and we didn't expand.
|
||||
// (one slot is wasted by nature of implementation)
|
||||
Assert.That(list.Capacity, NUnit.Framework.Is.EqualTo(6));
|
||||
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] { 2, 3, 4, 5, 6 }));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMiddleRemoveAtScenario1()
|
||||
{
|
||||
var list = new RingBufferList<int>(6);
|
||||
list.Add(-1);
|
||||
list.Add(-1);
|
||||
list.Add(-1);
|
||||
list.Add(-1);
|
||||
list.Add(1);
|
||||
list.RemoveAt(0);
|
||||
list.RemoveAt(0);
|
||||
list.RemoveAt(0);
|
||||
list.RemoveAt(0);
|
||||
list.Add(2);
|
||||
list.Add(3);
|
||||
list.Add(4);
|
||||
list.Add(5);
|
||||
list.Remove(4);
|
||||
|
||||
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 3, 5}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMiddleRemoveAtScenario2()
|
||||
{
|
||||
var list = new RingBufferList<int>(6);
|
||||
list.Add(-1);
|
||||
list.Add(-1);
|
||||
list.Add(1);
|
||||
list.RemoveAt(0);
|
||||
list.RemoveAt(0);
|
||||
list.Add(2);
|
||||
list.Add(3);
|
||||
list.Add(4);
|
||||
list.Add(5);
|
||||
list.Remove(3);
|
||||
|
||||
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 4, 5}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMiddleRemoveAtScenario3()
|
||||
{
|
||||
var list = new RingBufferList<int>(6);
|
||||
list.Add(1);
|
||||
list.Add(2);
|
||||
list.Add(3);
|
||||
list.Add(4);
|
||||
list.Add(5);
|
||||
list.Remove(4);
|
||||
|
||||
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 3, 5}));
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using Robust.Shared.Maths;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Robust.UnitTesting.Shared
|
||||
{
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
public sealed class ColorUtils_Test
|
||||
{
|
||||
[Test]
|
||||
public void TestInterpolateBetween()
|
||||
{
|
||||
var black = Color.Black;
|
||||
var white = Color.White;
|
||||
|
||||
Assert.That(Color.InterpolateBetween(black, white, 1), Is.EqualTo(white));
|
||||
Assert.That(Color.InterpolateBetween(black, white, 0), Is.EqualTo(black));
|
||||
// Should be grey but floating points hate us so...
|
||||
// The byte conversion shouldn't have issues because the error marging should be small enough.
|
||||
var grey = Color.InterpolateBetween(black, white, 0.5f);
|
||||
Assert.That(grey.RByte, Is.EqualTo(127));
|
||||
Assert.That(grey.GByte, Is.EqualTo(127));
|
||||
Assert.That(grey.BByte, Is.EqualTo(127));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWithAlpha()
|
||||
{
|
||||
Assert.That(Color.White.WithAlpha(128), Is.EqualTo(new Color(255, 255, 255, 128)));
|
||||
Assert.That(Color.White.WithAlpha(0.5f), Is.EqualTo(new Color(1, 1, 1, 0.5f)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFromHex()
|
||||
{
|
||||
// Test fallback.
|
||||
Assert.That(Color.FromHex("honk", Color.AliceBlue), Is.EqualTo(Color.AliceBlue));
|
||||
Assert.That(() => Color.FromHex("honk"), Throws.ArgumentException);
|
||||
Assert.That(() => Color.FromHex("#FFFFFFFF00"), Throws.ArgumentException);
|
||||
|
||||
Assert.That(Color.FromHex("#FFFFFF"), Is.EqualTo(Color.White));
|
||||
Assert.That(Color.FromHex("#FFFFFFFF"), Is.EqualTo(Color.White));
|
||||
Assert.That(Color.FromHex("#FFFFFF69"), Is.EqualTo(Color.White.WithAlpha(0x69)));
|
||||
Assert.That(Color.FromHex("#FFF"), Is.EqualTo(Color.White));
|
||||
Assert.That(Color.FromHex("#FFF8"), Is.EqualTo(Color.White.WithAlpha(0x88)));
|
||||
Assert.That(Color.FromHex("#ABCDEF"), Is.EqualTo(new Color(0xAB, 0xCD, 0xEF)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestByteParts()
|
||||
{
|
||||
var color = new Color(23, 38, 18, 20);
|
||||
Assert.That(color.RByte, Is.EqualTo(23));
|
||||
Assert.That(color.GByte, Is.EqualTo(38));
|
||||
Assert.That(color.BByte, Is.EqualTo(18));
|
||||
Assert.That(color.AByte, Is.EqualTo(20));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Configuration;
|
||||
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ConfigurationManagerTest))]
|
||||
internal sealed class ConfigurationIntegrationTest : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestSaveNoWarningServer()
|
||||
{
|
||||
using var server = StartServer(new ServerIntegrationOptions
|
||||
{
|
||||
FailureLogLevel = LogLevel.Warning
|
||||
});
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
var cfg = server.Resolve<IConfigurationManager>();
|
||||
cfg.SaveToFile();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestSaveNoWarningClient()
|
||||
{
|
||||
using var server = StartClient(new ClientIntegrationOptions
|
||||
{
|
||||
FailureLogLevel = LogLevel.Warning
|
||||
});
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
var cfg = server.Resolve<IConfigurationManager>();
|
||||
cfg.SaveToFile();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.Configuration;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Configuration
|
||||
{
|
||||
[TestFixture]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
[TestOf(typeof(ConfigurationManager))]
|
||||
public sealed class ConfigurationManagerTest
|
||||
{
|
||||
[Test]
|
||||
public void TestSubscribeUnsubscribe()
|
||||
{
|
||||
var mgr = MakeCfg();
|
||||
|
||||
mgr.RegisterCVar("foo.bar", 5);
|
||||
|
||||
var lastValue = 0;
|
||||
var timesRan = 0;
|
||||
|
||||
void ValueChanged(int value)
|
||||
{
|
||||
timesRan += 1;
|
||||
lastValue = value;
|
||||
}
|
||||
|
||||
mgr.OnValueChanged<int>("foo.bar", ValueChanged);
|
||||
|
||||
mgr.SetCVar("foo.bar", 2);
|
||||
|
||||
Assert.That(timesRan, Is.EqualTo(1), "OnValueChanged did not run!");
|
||||
Assert.That(lastValue, Is.EqualTo(2), "OnValueChanged value was wrong!");
|
||||
|
||||
mgr.UnsubValueChanged<int>("foo.bar", ValueChanged);
|
||||
|
||||
Assert.That(timesRan, Is.EqualTo(1), "UnsubValueChanged did not unsubscribe!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSubscribe_SubscribeMultipleThenUnsubscribe()
|
||||
{
|
||||
var mgr = MakeCfg();
|
||||
|
||||
mgr.RegisterCVar("foo.bar", 5);
|
||||
|
||||
var lastValueBar1 = 0;
|
||||
var lastValueBar2 = 0;
|
||||
var lastValueBar3 = 0;
|
||||
var lastValueBar4 = 0;
|
||||
|
||||
var subscription = mgr.SubscribeMultiple()
|
||||
.OnValueChanged<int>("foo.bar", value => lastValueBar1 = value)
|
||||
.OnValueChanged<int>("foo.bar", value => lastValueBar2 = value)
|
||||
.OnValueChanged<int>("foo.bar", value => lastValueBar3 = value)
|
||||
.OnValueChanged<int>("foo.bar", value => lastValueBar4 = value);
|
||||
|
||||
mgr.SetCVar("foo.bar", 1);
|
||||
|
||||
Assert.That(lastValueBar1, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueBar2, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueBar3, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueBar4, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
|
||||
subscription.Dispose();
|
||||
|
||||
mgr.SetCVar("foo.bar", 10);
|
||||
|
||||
Assert.That(lastValueBar1, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueBar2, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueBar3, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueBar4, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSubscribe_Unsubscribe()
|
||||
{
|
||||
var mgr = MakeCfg();
|
||||
|
||||
mgr.RegisterCVar("foo.bar", 5);
|
||||
mgr.RegisterCVar("foo.foo", 2);
|
||||
|
||||
var lastValueBar = 0;
|
||||
var lastValueFoo = 0;
|
||||
|
||||
var subscription = mgr.SubscribeMultiple()
|
||||
.OnValueChanged<int>("foo.bar", value => lastValueBar = value)
|
||||
.OnValueChanged<int>("foo.foo", value => lastValueFoo = value);
|
||||
|
||||
mgr.SetCVar("foo.bar", 1);
|
||||
mgr.SetCVar("foo.foo", 3);
|
||||
|
||||
Assert.That(lastValueBar, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueFoo, Is.EqualTo(3), "OnValueChanged value was wrong!");
|
||||
|
||||
subscription.Dispose();
|
||||
|
||||
mgr.SetCVar("foo.bar", 10);
|
||||
mgr.SetCVar("foo.foo", 30);
|
||||
|
||||
Assert.That(lastValueBar, Is.EqualTo(1), "OnValueChanged value was wrong!");
|
||||
Assert.That(lastValueFoo, Is.EqualTo(3), "OnValueChanged value was wrong!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOverrideDefaultValue()
|
||||
{
|
||||
var mgr = MakeCfg();
|
||||
|
||||
mgr.RegisterCVar("foo.bar", 5);
|
||||
|
||||
var value = 0;
|
||||
mgr.OnValueChanged<int>("foo.bar", v => value = v);
|
||||
|
||||
// Change default value, this fires the value changed callback.
|
||||
mgr.OverrideDefault("foo.bar", 10);
|
||||
|
||||
Assert.That(value, Is.EqualTo(10));
|
||||
Assert.That(mgr.GetCVar<int>("foo.bar"), Is.EqualTo(10));
|
||||
|
||||
// Modify the cvar programmatically, also fires the callback.
|
||||
mgr.SetCVar("foo.bar", 7);
|
||||
|
||||
Assert.That(value, Is.EqualTo(7));
|
||||
Assert.That(mgr.GetCVar<int>("foo.bar"), Is.EqualTo(7));
|
||||
|
||||
// We have a value set now, so changing the default won't do anything.
|
||||
mgr.OverrideDefault("foo.bar", 15);
|
||||
|
||||
Assert.That(value, Is.EqualTo(7));
|
||||
Assert.That(mgr.GetCVar<int>("foo.bar"), Is.EqualTo(7));
|
||||
}
|
||||
|
||||
private IConfigurationManager MakeCfg()
|
||||
{
|
||||
var collection = new DependencyCollection();
|
||||
collection.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
|
||||
collection.RegisterInstance<INetManager>(new Mock<INetManager>().Object);
|
||||
collection.Register<ConfigurationManager, ServerNetConfigurationManager>();
|
||||
collection.Register<IServerNetConfigurationManager, ServerNetConfigurationManager>();
|
||||
collection.Register<IGameTiming, GameTiming>();
|
||||
collection.Register<ILogManager, LogManager>();
|
||||
collection.BuildGraph();
|
||||
|
||||
return collection.Resolve<ConfigurationManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection.Metadata;
|
||||
using NUnit.Framework;
|
||||
using Pidgin;
|
||||
using static Robust.Shared.ContentPack.AssemblyTypeChecker;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.ContentPack
|
||||
{
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
[TestFixture]
|
||||
public sealed class AssemblyTypeCheckerParsingTest
|
||||
{
|
||||
[Test]
|
||||
public void TestMethod()
|
||||
{
|
||||
var res = MethodParser.ParseOrThrow(" void foo ( int , string )");
|
||||
|
||||
Assert.That(res.Name, Is.EqualTo("foo"));
|
||||
Assert.That(res.ReturnType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.Void)));
|
||||
Assert.That(res.ParameterTypes,
|
||||
Is.EquivalentTo(new[]
|
||||
{
|
||||
new MTypePrimitive(PrimitiveTypeCode.Int32),
|
||||
new MTypePrimitive(PrimitiveTypeCode.String)
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestParseConstructor()
|
||||
{
|
||||
var res = MethodParser.ParseOrThrow(" void .ctor ( int , string )");
|
||||
|
||||
Assert.That(res.Name, Is.EqualTo(".ctor"));
|
||||
Assert.That(res.ReturnType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.Void)));
|
||||
Assert.That(res.ParameterTypes,
|
||||
Is.EquivalentTo(new[]
|
||||
{
|
||||
new MTypePrimitive(PrimitiveTypeCode.Int32),
|
||||
new MTypePrimitive(PrimitiveTypeCode.String)
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMethodInvalid()
|
||||
{
|
||||
Assert.That(() => MethodParser.ParseOrThrow("foo ( int , string)"), Throws.InstanceOf<ParseException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestField()
|
||||
{
|
||||
var res = FieldParser.ParseOrThrow("string bar");
|
||||
|
||||
Assert.That(res.Name, Is.EqualTo("bar"));
|
||||
Assert.That(res.FieldType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.String)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGenericMethod()
|
||||
{
|
||||
var res = MethodParser.ParseOrThrow("string Concat(System.Collections.Generic.IEnumerable`1<!!0>)");
|
||||
|
||||
Assert.That(res.Name, Is.EqualTo("Concat"));
|
||||
Assert.That(res.ReturnType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.String)));
|
||||
Assert.That(res.ParameterTypes,
|
||||
Is.EquivalentTo(new MType[]
|
||||
{
|
||||
new MTypeGeneric(
|
||||
new MTypeParsed("System.Collections.Generic.IEnumerable`1"),
|
||||
new MType[] {new MTypeGenericMethodPlaceHolder(0)}.ToImmutableArray())
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.ContentPack
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class ResourceManagerTest : RobustUnitTest
|
||||
{
|
||||
private static Stream ZipStream => typeof(ResourceManagerTest).Assembly
|
||||
.GetManifestResourceStream("Robust.UnitTesting.Shared.ContentPack.ZipTest.zip")!;
|
||||
|
||||
private static readonly byte[] Data =
|
||||
{
|
||||
0x56, 0x75, 0x6c, 0x6b, 0x61, 0x6e, 0x20, 0x57, 0x68, 0x65, 0x6e
|
||||
};
|
||||
|
||||
private static readonly string[] InvalidPaths =
|
||||
{
|
||||
// Reserved filenames like COM ports.
|
||||
"/foo/COM1",
|
||||
"COM2",
|
||||
"/COM3/foo",
|
||||
"COM4",
|
||||
"COM5",
|
||||
"COM6",
|
||||
"COM7",
|
||||
"COM8",
|
||||
"COM9",
|
||||
"LPT1",
|
||||
"LPT2",
|
||||
"LPT3",
|
||||
"LPT4",
|
||||
"LPT5",
|
||||
"LPT6",
|
||||
"LPT7",
|
||||
"LPT8",
|
||||
"LPT9",
|
||||
"NUL",
|
||||
"AUX",
|
||||
"CON",
|
||||
"PRN",
|
||||
// ? is banned.
|
||||
"/foo/bar?",
|
||||
// * is banned.
|
||||
"/foo*/baz",
|
||||
// | is banned.
|
||||
"/yay|bugs",
|
||||
// : is banned.
|
||||
"/yay: bugs",
|
||||
// " is banned.
|
||||
"/yay... \"bugs\"",
|
||||
// \0 is banned.
|
||||
"/\0",
|
||||
// \x01-\x1f are banned.
|
||||
"/\n",
|
||||
};
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
componentFactory.GenerateNetIds();
|
||||
|
||||
var stream = new MemoryStream(Data);
|
||||
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
|
||||
resourceManager.MountStreamAt(stream, new ("/a/b/c.dat"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMountedStreamRead()
|
||||
{
|
||||
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
|
||||
using (var stream = resourceManager.ContentFileRead("/a/b/c.dat"))
|
||||
{
|
||||
Assert.That(stream.CopyToArray(), Is.EqualTo(Data));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidPaths([ValueSource(nameof(InvalidPaths))] string path)
|
||||
{
|
||||
Assert.That(ResourceManager.IsPathValid(new (path)), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidPathsLowerCase([ValueSource(nameof(InvalidPaths))] string path)
|
||||
{
|
||||
Assert.That(ResourceManager.IsPathValid(new (path.ToLowerInvariant())), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestZipRead()
|
||||
{
|
||||
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
|
||||
resourceManager.MountContentPack(ZipStream);
|
||||
|
||||
var stream = resourceManager.ContentFileRead("/foo.txt");
|
||||
Assert.That(ReadString(stream), Is.EqualTo("Honk!! \n"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestZipFind()
|
||||
{
|
||||
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
|
||||
resourceManager.MountContentPack(ZipStream);
|
||||
|
||||
var found = resourceManager.ContentFindFiles("/bar/");
|
||||
|
||||
Assert.That(found, Is.EquivalentTo(new[]
|
||||
{
|
||||
new ResPath("/bar/a.txt"),
|
||||
new ResPath("/bar/b.txt"),
|
||||
}));
|
||||
}
|
||||
|
||||
private static string ReadString(Stream stream)
|
||||
{
|
||||
using var streamReader = new StreamReader(stream);
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.ContentPack;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ResourceManager))]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public sealed class ResourceManagerTest2
|
||||
{
|
||||
[Test]
|
||||
public void TestMemoryRootReadFiles()
|
||||
{
|
||||
var (_, res) = CreateRes();
|
||||
|
||||
var testRoot = new MemoryContentRoot();
|
||||
testRoot.AddOrUpdateFile(new ResPath("/a.txt"), "a"u8.ToArray());
|
||||
testRoot.AddOrUpdateFile(new ResPath("/b.txt"), "b"u8.ToArray());
|
||||
testRoot.AddOrUpdateFile(new ResPath("/c.txt"), "c"u8.ToArray());
|
||||
testRoot.AddOrUpdateFile(new ResPath("/d/foo.txt"), "foo"u8.ToArray());
|
||||
|
||||
res.AddRoot(new ResPath("/"), testRoot);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(res.ContentFileReadAllText("/a.txt"), Is.EqualTo("a"));
|
||||
Assert.That(res.ContentFileReadAllText("/b.txt"), Is.EqualTo("b"));
|
||||
Assert.That(res.ContentFileReadAllText("/c.txt"), Is.EqualTo("c"));
|
||||
Assert.That(res.ContentFileReadAllText("/d/foo.txt"), Is.EqualTo("foo"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMemoryRootGetDirectoryEntries()
|
||||
{
|
||||
var (_, res) = CreateRes();
|
||||
|
||||
var testRoot = new MemoryContentRoot();
|
||||
testRoot.AddOrUpdateFile(new ResPath("/a.txt"), "a"u8.ToArray());
|
||||
testRoot.AddOrUpdateFile(new ResPath("/b.txt"), "b"u8.ToArray());
|
||||
testRoot.AddOrUpdateFile(new ResPath("/c.txt"), "c"u8.ToArray());
|
||||
testRoot.AddOrUpdateFile(new ResPath("/d/foo.txt"), "foo"u8.ToArray());
|
||||
|
||||
res.AddRoot(new ResPath("/"), testRoot);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/")), Is.EquivalentTo(new[]
|
||||
{
|
||||
"a.txt",
|
||||
"b.txt",
|
||||
"c.txt",
|
||||
"d/"
|
||||
}));
|
||||
|
||||
// Listing should work both with and without a trailing /
|
||||
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/d")), Is.EquivalentTo(new[]
|
||||
{
|
||||
"foo.txt"
|
||||
}));
|
||||
|
||||
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/d/")), Is.EquivalentTo(new[]
|
||||
{
|
||||
"foo.txt"
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that a mount path is properly shown as a directory entry.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetDirectoryEntriesTwoMounts()
|
||||
{
|
||||
var (_, res) = CreateRes();
|
||||
|
||||
var testRoot = new MemoryContentRoot();
|
||||
testRoot.AddOrUpdateFile(new ResPath("foo.txt"), Array.Empty<byte>());
|
||||
testRoot.AddOrUpdateFile(new ResPath("bar/foo.txt"), Array.Empty<byte>());
|
||||
|
||||
res.AddRoot(new ResPath("/"), testRoot);
|
||||
res.AddRoot(new ResPath("/second"), testRoot);
|
||||
res.AddRoot(new ResPath("/third/fourth"), testRoot);
|
||||
|
||||
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/")), Is.EquivalentTo(new []
|
||||
{
|
||||
"foo.txt",
|
||||
"bar/",
|
||||
"second/",
|
||||
"third/"
|
||||
}));
|
||||
}
|
||||
|
||||
private static (IDependencyCollection, IResourceManagerInternal) CreateRes()
|
||||
{
|
||||
var deps = new DependencyCollection();
|
||||
deps.Register<IResourceManager, ResourceManager>();
|
||||
deps.Register<IResourceManagerInternal, ResourceManager>();
|
||||
deps.Register<IConfigurationManager, ConfigurationManager>();
|
||||
deps.Register<ILogManager, LogManager>();
|
||||
deps.Register<IGameTiming, GameTiming>();
|
||||
|
||||
deps.BuildGraph();
|
||||
return (deps, deps.Resolve<IResourceManagerInternal>());
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,99 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.UnitTesting.Shared
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class EngineIntegrationTest_Test : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public void ServerStartsCorrectlyTest()
|
||||
{
|
||||
ServerIntegrationInstance? server = null;
|
||||
Assert.DoesNotThrow(() => server = StartServer());
|
||||
Assert.That(server, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClientStartsCorrectlyTest()
|
||||
{
|
||||
ClientIntegrationInstance? client = null;
|
||||
Assert.DoesNotThrow(() => client = StartClient());
|
||||
Assert.That(client, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ConsoleErrorsFailTest()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
// test missing commands
|
||||
await client.WaitPost(() => Assert.Throws<AssertionException>(() => client.ConsoleHost.ExecuteCommand("aaaaaaaa")));
|
||||
|
||||
// test invalid commands / missing arguments
|
||||
await client.WaitPost(() => Assert.Throws<AssertionException>(() => client.ConsoleHost.ExecuteCommand("cvar")));
|
||||
|
||||
// and repeat for the server
|
||||
await server.WaitPost(() => Assert.Throws<AssertionException>(() => server.ConsoleHost.ExecuteCommand("aaaaaaaa")));
|
||||
await server.WaitPost(() => Assert.Throws<AssertionException>(() => server.ConsoleHost.ExecuteCommand("cvar")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ServerClientPairConnectCorrectlyTest()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
Assert.That(server, Is.Not.Null);
|
||||
Assert.That(client, Is.Not.Null);
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
// Connect client to the server...
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
|
||||
// Run 10 synced ticks...
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
|
||||
// There must be a player connected.
|
||||
Assert.That(playerManager.PlayerCount, Is.EqualTo(1));
|
||||
|
||||
// Get the only player...
|
||||
var player = playerManager.Sessions.Single();
|
||||
|
||||
Assert.That(player.Status, Is.EqualTo(SessionStatus.Connected));
|
||||
Assert.That(player.Channel.IsConnected, Is.True);
|
||||
});
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
var netManager = IoCManager.Resolve<IClientNetManager>();
|
||||
|
||||
Assert.That(netManager.IsConnected, Is.True);
|
||||
Assert.That(netManager.ServerChannel, Is.Not.Null);
|
||||
Assert.That(netManager.ServerChannel!.IsConnected, Is.True);
|
||||
});
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,389 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared
|
||||
{
|
||||
[TestFixture, TestOf(typeof(EntityLookupSystem))]
|
||||
public sealed class EntityLookupTest
|
||||
{
|
||||
private static readonly MapId MapId = new MapId(1);
|
||||
|
||||
private static readonly TestCaseData[] IntersectingCases = new[]
|
||||
{
|
||||
// Big offset
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(10.5f, 10.5f), MapId), new MapCoordinates(new Vector2(10.5f, 10.5f), MapId), 0.25f, true),
|
||||
};
|
||||
|
||||
private static readonly TestCaseData[] InRangeCases = new[]
|
||||
{
|
||||
new TestCaseData(true, new MapCoordinates(Vector2.One, MapId), new MapCoordinates(Vector2.Zero, MapId), 0.5f, false),
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9.5f, 9.5f), MapId), 0.5f, true),
|
||||
// Close but no cigar
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9f, 9f), MapId), 0.5f, false),
|
||||
// Large area so useboundsquery
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(0f, 0f), MapId), new MapCoordinates(new Vector2(0f, 1000f), MapId), 999f, false),
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(0f, 0f), MapId), new MapCoordinates(new Vector2(0f, 999f), MapId), 999f, true),
|
||||
|
||||
// NoFixturecases
|
||||
new TestCaseData(false, new MapCoordinates(Vector2.One, MapId), new MapCoordinates(Vector2.Zero, MapId), 0.5f, false),
|
||||
new TestCaseData(false, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9.5f, 9.5f), MapId), 0.5f, false),
|
||||
// Close but no cigar
|
||||
new TestCaseData(false, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9f, 9f), MapId), 0.5f, false),
|
||||
};
|
||||
|
||||
// Remember this test data is relative.
|
||||
private static readonly TestCaseData[] Box2Cases = new[]
|
||||
{
|
||||
new TestCaseData(true, new MapCoordinates(Vector2.One, MapId), Box2.UnitCentered, false),
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), Box2.UnitCentered, true),
|
||||
};
|
||||
|
||||
private static readonly TestCaseData[] TileCases = new[]
|
||||
{
|
||||
new TestCaseData(true, new MapCoordinates(Vector2.One, MapId), Vector2i.Zero, false),
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), Vector2i.Zero, true),
|
||||
// Need to make sure we don't pull out neighbor fixtures even if they barely touch our tile
|
||||
new TestCaseData(true, new MapCoordinates(new Vector2(11f + 0.35f, 10f), MapId), Vector2i.Zero, false),
|
||||
};
|
||||
|
||||
private EntityUid GetPhysicsEntity(IEntityManager entManager, MapCoordinates spawnPos)
|
||||
{
|
||||
var ent = entManager.SpawnEntity(null, spawnPos);
|
||||
var physics = entManager.AddComponent<PhysicsComponent>(ent);
|
||||
entManager.System<FixtureSystem>().TryCreateFixture(ent, new PhysShapeCircle(0.35f, Vector2.Zero), "fix1");
|
||||
entManager.System<SharedPhysicsSystem>().SetCanCollide(ent, true, body: physics);
|
||||
return ent;
|
||||
}
|
||||
|
||||
private Entity<MapGridComponent> SetupGrid(MapId mapId, SharedMapSystem mapSystem, IEntityManager entManager, IMapManager mapManager)
|
||||
{
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
entManager.System<SharedTransformSystem>().SetLocalPosition(grid.Owner, new Vector2(10f, 10f));
|
||||
mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
return grid;
|
||||
}
|
||||
|
||||
#region Entity
|
||||
|
||||
/*
|
||||
* We double these tests just because these have slightly different codepaths at the moment.
|
||||
*
|
||||
*/
|
||||
|
||||
[Test, TestCaseSource(nameof(Box2Cases))]
|
||||
public void TestEntityAnyIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
|
||||
|
||||
Assert.That(outcome, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(Box2Cases))]
|
||||
public void TestEntityAnyLocalIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
|
||||
|
||||
Assert.That(outcome, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests Box2 local queries for a particular lookup ID.
|
||||
/// </summary>
|
||||
[Test, TestCaseSource(nameof(Box2Cases))]
|
||||
public void TestEntityGridLocalIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
var entities = new HashSet<Entity<TransformComponent>>();
|
||||
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryBounds, entities);
|
||||
|
||||
Assert.That(entities.Count > 0, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests Box2 local queries for a particular lookup ID.
|
||||
/// </summary>
|
||||
[Test, TestCaseSource(nameof(TileCases))]
|
||||
public void TestEntityGridTileIntersecting(bool physics, MapCoordinates spawnPos, Vector2i queryTile, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
var entities = new HashSet<Entity<TransformComponent>>();
|
||||
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryTile, entities);
|
||||
|
||||
Assert.That(entities.Count > 0, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EntityUid
|
||||
|
||||
[Test, TestCaseSource(nameof(InRangeCases))]
|
||||
public void TestMapInRange(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
entManager.System<SharedMapSystem>().CreateMap(spawnPos.MapId);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(IntersectingCases))]
|
||||
public void TestGridIntersecting(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
_ = entManager.SpawnEntity(null, spawnPos);
|
||||
var bounds = new Box2Rotated(Box2.CenteredAround(queryPos.Position, new Vector2(range, range)));
|
||||
|
||||
Assert.That(lookup.GetEntitiesIntersecting(queryPos.MapId, bounds).Count > 0, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(InRangeCases))]
|
||||
public void TestGridInRange(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
_ = entManager.SpawnEntity(null, spawnPos);
|
||||
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(InRangeCases))]
|
||||
public void TestMapNoFixtureInRange(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
entManager.System<SharedMapSystem>().CreateMap(spawnPos.MapId);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests Box2 local queries for a particular lookup ID.
|
||||
/// </summary>
|
||||
[Test, TestCaseSource(nameof(Box2Cases))]
|
||||
public void TestGridAnyIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
|
||||
|
||||
Assert.That(outcome, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests Box2 local queries for a particular lookup ID.
|
||||
/// </summary>
|
||||
[Test, TestCaseSource(nameof(Box2Cases))]
|
||||
public void TestGridLocalIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(spawnPos.MapId);
|
||||
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
|
||||
|
||||
if (physics)
|
||||
GetPhysicsEntity(entManager, spawnPos);
|
||||
else
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
var entities = new HashSet<EntityUid>();
|
||||
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryBounds, entities);
|
||||
|
||||
Assert.That(entities.Count > 0, Is.EqualTo(result));
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Is the entity correctly removed / added to EntityLookup when anchored
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAnchoring()
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
var transformSystem = entManager.System<SharedTransformSystem>();
|
||||
|
||||
var mapId = server.CreateMap().MapId;
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
var theMapSpotBeingUsed = new Box2(Vector2.Zero, Vector2.One);
|
||||
mapSystem.SetTile(grid, new Vector2i(), new Tile(1));
|
||||
|
||||
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList(), Is.Empty);
|
||||
|
||||
// Setup and check it actually worked
|
||||
var dummy = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList(), Has.Count.EqualTo(1));
|
||||
|
||||
var xform = entManager.GetComponent<TransformComponent>(dummy);
|
||||
|
||||
// When anchoring it should still get returned.
|
||||
transformSystem.AnchorEntity(dummy, xform);
|
||||
Assert.That(xform.Anchored, Is.True);
|
||||
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList(), Has.Count.EqualTo(1));
|
||||
|
||||
transformSystem.Unanchor(dummy, xform);
|
||||
Assert.That(xform.Anchored, Is.False);
|
||||
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList().Count, Is.EqualTo(1));
|
||||
|
||||
entManager.DeleteEntity(dummy);
|
||||
entManager.DeleteEntity(grid);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class AlwaysPushSerializationTest : RobustIntegrationTest
|
||||
{
|
||||
private const string Prototype = @"
|
||||
- type: entity
|
||||
id: TestEntityCompositionParent
|
||||
components:
|
||||
- type: EntitySaveTest
|
||||
list: [ 1, 2 ]
|
||||
|
||||
- type: entity
|
||||
id: TestEntityCompositionChild
|
||||
parent: TestEntityCompositionParent
|
||||
components:
|
||||
- type: EntitySaveTest
|
||||
list: [ 3 , 4 ]
|
||||
";
|
||||
|
||||
/// <summary>
|
||||
/// This test checks that deserializing an entity with some component that has the
|
||||
/// <see cref="AlwaysPushInheritanceAttribute"/> works as intended. Previously the attribute would cause the entity
|
||||
/// prototype to **always** append it's contents to the loaded entity, effectively causing
|
||||
/// the <see cref="TestAlwaysPushComponent.List"/> data-field to grow each time a map was loaded and saved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[TestOf(typeof(AlwaysPushInheritanceAttribute))]
|
||||
public async Task TestAlwaysPushSerialization()
|
||||
{
|
||||
var opts = new ServerIntegrationOptions
|
||||
{
|
||||
ExtraPrototypes = Prototype
|
||||
};
|
||||
|
||||
var server = StartServer(opts);
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
|
||||
// Create a new map and spawn in some entities.
|
||||
MapId mapId = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> parent1 = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> parent2 = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> parent3 = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> child1 = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> child2 = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> child3 = default;
|
||||
|
||||
var path = new ResPath($"{nameof(TestAlwaysPushSerialization)}.yml");
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
server.System<SharedMapSystem>().CreateMap(out mapId);
|
||||
var coords = new MapCoordinates(0, 0, mapId);
|
||||
var parent1Uid = entMan.Spawn("TestEntityCompositionParent", coords);
|
||||
var parent2Uid = entMan.Spawn("TestEntityCompositionParent", coords);
|
||||
var parent3Uid = entMan.Spawn("TestEntityCompositionParent", coords);
|
||||
var child1Uid = entMan.Spawn("TestEntityCompositionChild", coords);
|
||||
var child2Uid = entMan.Spawn("TestEntityCompositionChild", coords);
|
||||
var child3Uid = entMan.Spawn("TestEntityCompositionChild", coords);
|
||||
|
||||
parent1 = Get(parent1Uid, entMan);
|
||||
parent2 = Get(parent2Uid, entMan);
|
||||
parent3 = Get(parent3Uid, entMan);
|
||||
child1 = Get(child1Uid, entMan);
|
||||
child2 = Get(child2Uid, entMan);
|
||||
child3 = Get(child3Uid, entMan);
|
||||
});
|
||||
|
||||
// Assign a unique id to each entity (so they can be identified after saving & loading a map)
|
||||
parent1.Comp2!.Id = nameof(parent1);
|
||||
parent2.Comp2!.Id = nameof(parent2);
|
||||
parent3.Comp2!.Id = nameof(parent3);
|
||||
child1.Comp2!.Id = nameof(child1);
|
||||
child2.Comp2!.Id = nameof(child2);
|
||||
child3.Comp2!.Id = nameof(child3);
|
||||
|
||||
// The inheritance pushing for the prototypes should ensure that the parent & child prototype's lists were merged.
|
||||
Assert.That(parent1.Comp2.List.SequenceEqual(new[] {1, 2}));
|
||||
Assert.That(parent2.Comp2.List.SequenceEqual(new[] {1, 2}));
|
||||
Assert.That(parent3.Comp2.List.SequenceEqual(new[] {1, 2}));
|
||||
Assert.That(child1.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
|
||||
Assert.That(child2.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
|
||||
Assert.That(child3.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
|
||||
|
||||
// Modify data on some components.
|
||||
parent2.Comp2.List.Add(-1);
|
||||
child2.Comp2.List.Add(-1);
|
||||
parent3.Comp2.List.RemoveAt(1);
|
||||
child3.Comp2.List.RemoveAt(1);
|
||||
|
||||
Assert.That(parent1.Comp2.List.SequenceEqual(new[] {1, 2}));
|
||||
Assert.That(parent2.Comp2.List.SequenceEqual(new[] {1, 2, -1}));
|
||||
Assert.That(parent3.Comp2.List.SequenceEqual(new[] {1}));
|
||||
Assert.That(child1.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
|
||||
Assert.That(child2.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2, -1}));
|
||||
Assert.That(child3.Comp2.List.SequenceEqual(new[] {3, 1, 2}));
|
||||
|
||||
// Save map to yaml
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var map = server.System<SharedMapSystem>();
|
||||
Assert.That(loader.TrySaveMap(mapId, path));
|
||||
|
||||
// Delete the entities
|
||||
await server.WaitPost(() => map.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load the map
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
Assert.That(loader.TryLoadMap(path, out var ent, out _));
|
||||
mapId = ent!.Value.Comp.MapId;
|
||||
});
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(6));
|
||||
|
||||
// Find the deserialized entities
|
||||
parent1 = Find(nameof(parent1), entMan);
|
||||
parent2 = Find(nameof(parent2), entMan);
|
||||
parent3 = Find(nameof(parent3), entMan);
|
||||
child1 = Find(nameof(child1), entMan);
|
||||
child2 = Find(nameof(child2), entMan);
|
||||
child3 = Find(nameof(child3), entMan);
|
||||
|
||||
// Verify that the entity data has not changed.
|
||||
Assert.That(parent1.Comp2.List.SequenceEqual(new[] {1, 2}));
|
||||
Assert.That(parent2.Comp2.List.SequenceEqual(new[] {1, 2, -1}));
|
||||
Assert.That(parent3.Comp2.List.SequenceEqual(new[] {1}));
|
||||
Assert.That(child1.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
|
||||
Assert.That(child2.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2, -1}));
|
||||
Assert.That(child3.Comp2.List.SequenceEqual(new[] {3, 1, 2}));
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
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
|
||||
{
|
||||
private const string TestTileDefId = "a";
|
||||
private const string TestPrototypes = $@"
|
||||
- type: testTileDef
|
||||
id: space
|
||||
|
||||
- type: testTileDef
|
||||
id: {TestTileDefId}
|
||||
";
|
||||
|
||||
[Test]
|
||||
public async Task TestAutoIncludeSerialization()
|
||||
{
|
||||
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
|
||||
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");
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
|
||||
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
|
||||
|
||||
// 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));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
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);
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
@@ -1,450 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
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.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class BackwardsCompatibilityTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that v3 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
|
||||
/// See also the comments around <see cref="EntityDeserializer.OldestSupportedVersion"/> that point out that v3
|
||||
/// isn't even really loadable anymore.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task TestLoadV3()
|
||||
{
|
||||
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV3});
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var meta = server.System<MetaDataSystem>();
|
||||
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan);
|
||||
var gridPath = new ResPath($"{nameof(MapDataV3Grid)}.yml");
|
||||
resourceManager.MountString(gridPath.ToString(), MapDataV3Grid);
|
||||
|
||||
MapId mapId = default;
|
||||
EntityUid mapUid = default;
|
||||
|
||||
Entity<TransformComponent, EntitySaveTestComponent> map;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> ent;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> grid;
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(mapUid), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV3Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV3Map);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.True);
|
||||
Assert.That(meta.EntityPaused(grid), Is.True);
|
||||
Assert.That(meta.EntityPaused(map), Is.True);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Repeat test, but with the initialize maps option enabled.
|
||||
// Apparently mounted strings can only be read a single time.
|
||||
// So have to re-mount them.
|
||||
var mapPath2 = new ResPath($"{nameof(MapDataV3Map)}2.yml");
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV3Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(map), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV3Grid = @"
|
||||
meta:
|
||||
format: 3
|
||||
name: DemoStation
|
||||
author: Space-Wizards
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
10: 10
|
||||
11: 11
|
||||
12: 12
|
||||
13: 13
|
||||
14: 14
|
||||
15: 15
|
||||
16: 16
|
||||
17: 17
|
||||
18: 18
|
||||
19: 19
|
||||
20: 20
|
||||
21: 21
|
||||
22: 22
|
||||
23: 23
|
||||
24: 24
|
||||
25: 25
|
||||
26: 26
|
||||
27: 27
|
||||
28: 28
|
||||
29: 29
|
||||
30: 30
|
||||
31: 31
|
||||
32: 32
|
||||
33: 33
|
||||
34: 34
|
||||
35: 35
|
||||
36: 36
|
||||
37: 37
|
||||
38: 38
|
||||
39: 39
|
||||
40: 40
|
||||
41: 41
|
||||
42: 42
|
||||
43: 43
|
||||
44: 44
|
||||
45: 45
|
||||
46: 46
|
||||
47: 47
|
||||
48: 48
|
||||
49: 49
|
||||
50: 50
|
||||
51: 51
|
||||
52: 52
|
||||
53: 53
|
||||
54: 54
|
||||
55: 55
|
||||
56: 56
|
||||
57: 57
|
||||
58: 58
|
||||
59: 59
|
||||
60: 60
|
||||
61: 61
|
||||
62: 62
|
||||
63: 63
|
||||
64: 64
|
||||
65: 65
|
||||
66: 66
|
||||
67: 67
|
||||
68: 68
|
||||
69: 69
|
||||
70: 70
|
||||
71: 71
|
||||
72: 72
|
||||
73: 73
|
||||
74: 74
|
||||
75: 75
|
||||
76: 76
|
||||
77: 77
|
||||
78: 78
|
||||
79: 79
|
||||
80: 80
|
||||
81: 81
|
||||
82: 82
|
||||
83: 83
|
||||
84: 84
|
||||
85: 85
|
||||
86: 86
|
||||
87: 87
|
||||
88: 88
|
||||
entities:
|
||||
- uid: 0
|
||||
components:
|
||||
- type: MetaData
|
||||
- parent: null
|
||||
type: Transform
|
||||
- chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAPgAAAA==
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
type: MapGrid
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
type: Physics
|
||||
- fixtures: {}
|
||||
type: Fixtures
|
||||
- type: OccluderTree
|
||||
- id: grid
|
||||
type: EntitySaveTest
|
||||
- uid: 1
|
||||
type: V3TestProto
|
||||
components:
|
||||
- pos: 0.5,0.5
|
||||
parent: 0
|
||||
type: Transform
|
||||
- id: ent
|
||||
type: EntitySaveTest
|
||||
...
|
||||
";
|
||||
|
||||
private const string MapDataV3Map = @"
|
||||
meta:
|
||||
format: 3
|
||||
name: DemoStation
|
||||
author: Space-Wizards
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
10: 10
|
||||
11: 11
|
||||
12: 12
|
||||
13: 13
|
||||
14: 14
|
||||
15: 15
|
||||
16: 16
|
||||
17: 17
|
||||
18: 18
|
||||
19: 19
|
||||
20: 20
|
||||
21: 21
|
||||
22: 22
|
||||
23: 23
|
||||
24: 24
|
||||
25: 25
|
||||
26: 26
|
||||
27: 27
|
||||
28: 28
|
||||
29: 29
|
||||
30: 30
|
||||
31: 31
|
||||
32: 32
|
||||
33: 33
|
||||
34: 34
|
||||
35: 35
|
||||
36: 36
|
||||
37: 37
|
||||
38: 38
|
||||
39: 39
|
||||
40: 40
|
||||
41: 41
|
||||
42: 42
|
||||
43: 43
|
||||
44: 44
|
||||
45: 45
|
||||
46: 46
|
||||
47: 47
|
||||
48: 48
|
||||
49: 49
|
||||
50: 50
|
||||
51: 51
|
||||
52: 52
|
||||
53: 53
|
||||
54: 54
|
||||
55: 55
|
||||
56: 56
|
||||
57: 57
|
||||
58: 58
|
||||
59: 59
|
||||
60: 60
|
||||
61: 61
|
||||
62: 62
|
||||
63: 63
|
||||
64: 64
|
||||
65: 65
|
||||
66: 66
|
||||
67: 67
|
||||
68: 68
|
||||
69: 69
|
||||
70: 70
|
||||
71: 71
|
||||
72: 72
|
||||
73: 73
|
||||
74: 74
|
||||
75: 75
|
||||
76: 76
|
||||
77: 77
|
||||
78: 78
|
||||
79: 79
|
||||
80: 80
|
||||
81: 81
|
||||
82: 82
|
||||
83: 83
|
||||
84: 84
|
||||
85: 85
|
||||
86: 86
|
||||
87: 87
|
||||
88: 88
|
||||
entities:
|
||||
- uid: 123
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: Map
|
||||
- type: EntitySaveTest
|
||||
id: map
|
||||
- uid: 0
|
||||
components:
|
||||
- type: MetaData
|
||||
- parent: 123
|
||||
type: Transform
|
||||
- chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAPgAAAA==
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
type: MapGrid
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
type: Physics
|
||||
- fixtures: {}
|
||||
type: Fixtures
|
||||
- type: OccluderTree
|
||||
- id: grid
|
||||
type: EntitySaveTest
|
||||
- uid: 1
|
||||
type: V3TestProto
|
||||
components:
|
||||
- pos: 0.5,0.5
|
||||
parent: 0
|
||||
type: Transform
|
||||
- id: ent
|
||||
type: EntitySaveTest
|
||||
...
|
||||
";
|
||||
|
||||
private static string GenerateTileDefs(int count)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
sb.Append($@"
|
||||
- type: testTileDef
|
||||
id: {i}"
|
||||
);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static readonly string PrototypeV3 = $@"
|
||||
- type: entity
|
||||
id: V3TestProto
|
||||
components:
|
||||
- type: EntitySaveTest
|
||||
list: [ 1, 2 ]
|
||||
|
||||
- type: testTileDef
|
||||
id: Space
|
||||
|
||||
{GenerateTileDefs(88)}
|
||||
";
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
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.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class BackwardsCompatibilityTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that v4 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task TestLoadV4()
|
||||
{
|
||||
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV4});
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var meta = server.System<MetaDataSystem>();
|
||||
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "Space");
|
||||
var gridPath = new ResPath($"{nameof(MapDataV4Grid)}.yml");
|
||||
resourceManager.MountString(gridPath.ToString(), MapDataV4Grid);
|
||||
|
||||
MapId mapId = default;
|
||||
EntityUid mapUid = default;
|
||||
|
||||
Entity<TransformComponent, EntitySaveTestComponent> map;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> ent;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> grid;
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(mapUid), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV4Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV4Map);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.True);
|
||||
Assert.That(meta.EntityPaused(grid), Is.True);
|
||||
Assert.That(meta.EntityPaused(map), Is.True);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Repeat test, but with the initialize maps option enabled.
|
||||
// Apparently mounted strings can only be read a single time.
|
||||
// So have to re-mount them.
|
||||
var mapPath2 = new ResPath($"{nameof(MapDataV4Map)}2.yml");
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV4Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(map), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV4Grid = @"
|
||||
meta:
|
||||
format: 4
|
||||
name: DemoStation
|
||||
author: Space-Wizards
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
11: A
|
||||
68: B
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 2
|
||||
components:
|
||||
- type: MetaData
|
||||
- parent: invalid
|
||||
type: Transform
|
||||
- chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARAAAAA==
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
type: MapGrid
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
type: Physics
|
||||
- type: OccluderTree
|
||||
- id: grid
|
||||
type: EntitySaveTest
|
||||
- proto: V4TestProto
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- pos: 0.5,0.5
|
||||
parent: 2
|
||||
type: Transform
|
||||
- id: ent
|
||||
type: EntitySaveTest
|
||||
...
|
||||
";
|
||||
|
||||
private const string MapDataV4Map = @"
|
||||
meta:
|
||||
format: 4
|
||||
name: DemoStation
|
||||
author: Space-Wizards
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
11: A
|
||||
68: B
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 123
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: Map
|
||||
- type: EntitySaveTest
|
||||
id: map
|
||||
- uid: 2
|
||||
components:
|
||||
- type: MetaData
|
||||
- parent: 123
|
||||
type: Transform
|
||||
- chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARAAAAA==
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
type: MapGrid
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
type: Physics
|
||||
- type: OccluderTree
|
||||
- id: grid
|
||||
type: EntitySaveTest
|
||||
- proto: V4TestProto
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- pos: 0.5,0.5
|
||||
parent: 2
|
||||
type: Transform
|
||||
- id: ent
|
||||
type: EntitySaveTest
|
||||
";
|
||||
|
||||
private const string PrototypeV4 = @"
|
||||
- type: entity
|
||||
id: V4TestProto
|
||||
components:
|
||||
- type: EntitySaveTest
|
||||
list: [ 1, 2 ]
|
||||
|
||||
- type: testTileDef
|
||||
id: Space
|
||||
|
||||
- type: testTileDef
|
||||
id: A
|
||||
|
||||
- type: testTileDef
|
||||
id: B
|
||||
";
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
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.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class BackwardsCompatibilityTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that v5 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task TestLoadV5()
|
||||
{
|
||||
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV5});
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var meta = server.System<MetaDataSystem>();
|
||||
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "Space");
|
||||
var gridPath = new ResPath($"{nameof(MapDataV5Grid)}.yml");
|
||||
resourceManager.MountString(gridPath.ToString(), MapDataV5Grid);
|
||||
|
||||
MapId mapId = default;
|
||||
EntityUid mapUid = default;
|
||||
|
||||
Entity<TransformComponent, EntitySaveTestComponent> map;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> ent;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> grid;
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(mapUid), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV5Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV5Map);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.True);
|
||||
Assert.That(meta.EntityPaused(grid), Is.True);
|
||||
Assert.That(meta.EntityPaused(map), Is.True);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Repeat test, but with the initialize maps option enabled.
|
||||
// Apparently mounted strings can only be read a single time.
|
||||
// So have to re-mount them.
|
||||
var mapPath2 = new ResPath($"{nameof(MapDataV5Map)}2.yml");
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV5Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(map), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV5Grid = @"
|
||||
meta:
|
||||
format: 5
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
11: A
|
||||
69: B
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 2
|
||||
components:
|
||||
- type: MetaData
|
||||
- parent: invalid
|
||||
type: Transform
|
||||
- chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARQAAAA==
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
type: MapGrid
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
type: Physics
|
||||
- fixtures: {}
|
||||
type: Fixtures
|
||||
- type: OccluderTree
|
||||
- id: grid
|
||||
type: EntitySaveTest
|
||||
- proto: V5TestProto
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- pos: 0.5,0.5
|
||||
parent: 2
|
||||
type: Transform
|
||||
- id: ent
|
||||
type: EntitySaveTest
|
||||
";
|
||||
|
||||
private const string MapDataV5Map = @"
|
||||
meta:
|
||||
format: 5
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
11: A
|
||||
69: B
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 123
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: Map
|
||||
- type: EntitySaveTest
|
||||
id: map
|
||||
- uid: 2
|
||||
components:
|
||||
- type: MetaData
|
||||
- parent: 123
|
||||
type: Transform
|
||||
- chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARQAAAA==
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
type: MapGrid
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
type: Physics
|
||||
- fixtures: {}
|
||||
type: Fixtures
|
||||
- type: OccluderTree
|
||||
- id: grid
|
||||
type: EntitySaveTest
|
||||
- proto: V5TestProto
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- pos: 0.5,0.5
|
||||
parent: 2
|
||||
type: Transform
|
||||
- id: ent
|
||||
type: EntitySaveTest
|
||||
";
|
||||
|
||||
private const string PrototypeV5 = @"
|
||||
- type: entity
|
||||
id: V5TestProto
|
||||
components:
|
||||
- type: EntitySaveTest
|
||||
list: [ 1, 2 ]
|
||||
|
||||
- type: testTileDef
|
||||
id: Space
|
||||
|
||||
- type: testTileDef
|
||||
id: A
|
||||
|
||||
- type: testTileDef
|
||||
id: B
|
||||
";
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
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.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class BackwardsCompatibilityTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that v6 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task TestLoadV6()
|
||||
{
|
||||
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV6});
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var meta = server.System<MetaDataSystem>();
|
||||
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "Space");
|
||||
var gridPath = new ResPath($"{nameof(MapDataV6Grid)}.yml");
|
||||
resourceManager.MountString(gridPath.ToString(), MapDataV6Grid);
|
||||
|
||||
MapId mapId = default;
|
||||
EntityUid mapUid = default;
|
||||
|
||||
Entity<TransformComponent, EntitySaveTestComponent> map;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> ent;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> grid;
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(mapUid), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV6Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV6Map);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.True);
|
||||
Assert.That(meta.EntityPaused(grid), Is.True);
|
||||
Assert.That(meta.EntityPaused(map), Is.True);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.Initialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Repeat test, but with the initialize maps option enabled.
|
||||
// Apparently mounted strings can only be read a single time.
|
||||
// So have to re-mount them.
|
||||
var mapPath2 = new ResPath($"{nameof(MapDataV6Map)}2.yml");
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV6Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
map = Find(nameof(map), entMan);
|
||||
|
||||
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
|
||||
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
Assert.That(meta.EntityPaused(ent), Is.False);
|
||||
Assert.That(meta.EntityPaused(grid), Is.False);
|
||||
Assert.That(meta.EntityPaused(map), Is.False);
|
||||
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
|
||||
Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV6Grid = @"
|
||||
meta:
|
||||
format: 6
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
11: A
|
||||
89: B
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 2
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
parent: invalid
|
||||
- type: MapGrid
|
||||
chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAWQAAAAAA
|
||||
version: 6
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
version: 6
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
version: 6
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
version: 6
|
||||
- type: Broadphase
|
||||
- type: Physics
|
||||
bodyStatus: InAir
|
||||
angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
- type: OccluderTree
|
||||
- type: EntitySaveTest
|
||||
id: grid
|
||||
- proto: V6TestProto
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 0.5,0.5
|
||||
parent: 2
|
||||
- type: EntitySaveTest
|
||||
id: ent
|
||||
";
|
||||
|
||||
private const string MapDataV6Map = @"
|
||||
meta:
|
||||
format: 6
|
||||
postmapinit: false
|
||||
tilemap:
|
||||
0: Space
|
||||
11: A
|
||||
89: B
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 123
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: Map
|
||||
mapPaused: True
|
||||
- type: EntitySaveTest
|
||||
id: map
|
||||
- uid: 2
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
parent: 123
|
||||
- type: MapGrid
|
||||
chunks:
|
||||
-1,-1:
|
||||
ind: -1,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAWQAAAAAA
|
||||
version: 6
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
version: 6
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
version: 6
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
version: 6
|
||||
- type: Broadphase
|
||||
- type: Physics
|
||||
bodyStatus: InAir
|
||||
angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
bodyType: Dynamic
|
||||
- type: OccluderTree
|
||||
- type: EntitySaveTest
|
||||
id: grid
|
||||
- proto: V6TestProto
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 0.5,0.5
|
||||
parent: 2
|
||||
- type: EntitySaveTest
|
||||
id: ent
|
||||
";
|
||||
|
||||
private const string PrototypeV6 = @"
|
||||
- type: entity
|
||||
id: V6TestProto
|
||||
components:
|
||||
- type: EntitySaveTest
|
||||
list: [ 1, 2 ]
|
||||
|
||||
- type: testTileDef
|
||||
id: Space
|
||||
|
||||
- type: testTileDef
|
||||
id: A
|
||||
|
||||
- type: testTileDef
|
||||
id: B
|
||||
";
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
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.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
/// <summary>
|
||||
/// Test that older file formats can still be loaded.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed partial class BackwardsCompatibilityTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that v7 maps can be loaded. This just re-uses some map files that are generated by other tests, and then
|
||||
/// checks that the post-load debug asserts still pass. specifically, it uses the second to last file from
|
||||
/// <see cref="AutoIncludeSerializationTest"/> and the initial file from
|
||||
/// <see cref="LifestageSerializationTest.TestMixedLifestageSerialization"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// At the time of writing, v7 is the current version, but this is here for when the version increases in the future.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task TestLoadV7()
|
||||
{
|
||||
var server = StartServer(new ServerIntegrationOptions { ExtraPrototypes = PrototypeV7 });
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
|
||||
|
||||
void AssertCount(int expected) => Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(expected));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
|
||||
var mapLoadOpts = MapLoadOptions.Default with
|
||||
{
|
||||
DeserializationOptions = DeserializationOptions.Default with {LogOrphanedGrids = false}
|
||||
};
|
||||
|
||||
// Test the file from AutoIncludeSerializationTest
|
||||
{
|
||||
var path = new ResPath($"{nameof(MapDataV7)}.yml");
|
||||
resourceManager.MountString(path.ToString(), MapDataV7);
|
||||
|
||||
Entity<TransformComponent, EntitySaveTestComponent> grid;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> onGrid;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> otherMap;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> otherEnt;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> nullSpace;
|
||||
|
||||
LoadResult? result = default;
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(path, out result, mapLoadOpts)));
|
||||
|
||||
Assert.That(result!.Version, Is.EqualTo(7));
|
||||
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);
|
||||
await server.WaitPost(() => loader.Delete(result));
|
||||
}
|
||||
|
||||
|
||||
// Test the file from LifestageSerializationTest.TestMixedLifestageSerialization
|
||||
{
|
||||
var pathLifestage = new ResPath($"{nameof(MapDataV7Lifestage)}.yml");
|
||||
resourceManager.MountString(pathLifestage.ToString(), MapDataV7Lifestage);
|
||||
|
||||
Entity<TransformComponent, EntitySaveTestComponent> mapA; // preinit Map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> mapB; // postinit unpaused Map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entA; // postinit entity on preinit map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entB; // paused entity on postinit unpaused map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entC; // preinit entity on postinit map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> nullA; // postinit nullspace entity
|
||||
Entity<TransformComponent, EntitySaveTestComponent> nullB; // preinit nullspace entity
|
||||
Entity<TransformComponent, EntitySaveTestComponent> nullC; // paused postinit nullspace entity
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
LoadResult? result = default;
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(pathLifestage, out result)));
|
||||
Assert.That(result!.Version, Is.EqualTo(7));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(8));
|
||||
|
||||
mapA = Find(nameof(mapA), entMan);
|
||||
mapB = Find(nameof(mapB), entMan);
|
||||
entA = Find(nameof(entA), entMan);
|
||||
entB = Find(nameof(entB), entMan);
|
||||
entC = Find(nameof(entC), entMan);
|
||||
nullA = Find(nameof(nullA), entMan);
|
||||
nullB = Find(nameof(nullB), entMan);
|
||||
nullC = Find(nameof(nullC), entMan);
|
||||
|
||||
AssertPaused(true, mapA, entB, nullC);
|
||||
AssertPaused(false, mapB, entA, entC, nullA, nullB);
|
||||
AssertPreInit(true, mapA, entC, nullB);
|
||||
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
void AssertPaused(bool expected, params EntityUid[] uids)
|
||||
{
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected));
|
||||
}
|
||||
}
|
||||
|
||||
void AssertPreInit(bool expected, params EntityUid[] uids)
|
||||
{
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
|
||||
expected
|
||||
? Is.LessThan(EntityLifeStage.MapInitialized)
|
||||
: Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
}
|
||||
}
|
||||
|
||||
await server.WaitPost(() => loader.Delete(result));
|
||||
}
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV7 = @"
|
||||
meta:
|
||||
format: 7
|
||||
category: Unknown
|
||||
engineVersion: 238.0.0
|
||||
forkId: """"
|
||||
forkVersion: """"
|
||||
time: 12/25/2024 00:40:09
|
||||
entityCount: 5
|
||||
maps:
|
||||
- 3
|
||||
grids:
|
||||
- 1
|
||||
orphans:
|
||||
- 1
|
||||
nullspace:
|
||||
- 5
|
||||
tilemap:
|
||||
1: space
|
||||
0: a
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 1
|
||||
paused: false
|
||||
components:
|
||||
- type: MetaData
|
||||
name: grid
|
||||
- type: Transform
|
||||
parent: invalid
|
||||
- type: MapGrid
|
||||
chunks:
|
||||
0,0:
|
||||
ind: 0,0
|
||||
tiles: AAAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAA
|
||||
version: 6
|
||||
- type: Broadphase
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures: {}
|
||||
- type: OccluderTree
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: grid
|
||||
- uid: 2
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
pos: 0.5,0.5
|
||||
parent: 1
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
entity: 3
|
||||
id: onGrid
|
||||
- uid: 3
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
name: Map Entity
|
||||
- type: Transform
|
||||
- type: Map
|
||||
mapInitialized: True
|
||||
- type: GridTree
|
||||
- type: Broadphase
|
||||
- type: OccluderTree
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: otherMap
|
||||
- uid: 4
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
parent: 3
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
entity: 5
|
||||
id: otherEnt
|
||||
- uid: 5
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: nullSpace
|
||||
";
|
||||
|
||||
private const string MapDataV7Lifestage = @"
|
||||
meta:
|
||||
format: 7
|
||||
category: Unknown
|
||||
engineVersion: 238.0.0
|
||||
forkId: """"
|
||||
forkVersion: """"
|
||||
time: 12/25/2024 00:50:59
|
||||
entityCount: 8
|
||||
maps:
|
||||
- 1
|
||||
- 3
|
||||
grids: []
|
||||
orphans: []
|
||||
nullspace:
|
||||
- 6
|
||||
- 7
|
||||
- 8
|
||||
tilemap: {}
|
||||
entities:
|
||||
- proto: """"
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- type: MetaData
|
||||
name: Map Entity
|
||||
- type: Transform
|
||||
- type: Map
|
||||
mapPaused: True
|
||||
- type: GridTree
|
||||
- type: Broadphase
|
||||
- type: OccluderTree
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: mapA
|
||||
- uid: 2
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
parent: 1
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: entA
|
||||
- uid: 3
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
name: Map Entity
|
||||
- type: Transform
|
||||
- type: Map
|
||||
mapInitialized: True
|
||||
- type: GridTree
|
||||
- type: Broadphase
|
||||
- type: OccluderTree
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: mapB
|
||||
- uid: 4
|
||||
mapInit: true
|
||||
paused: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
parent: 3
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: entB
|
||||
- uid: 5
|
||||
paused: false
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
parent: 3
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: entC
|
||||
- uid: 6
|
||||
mapInit: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: nullA
|
||||
- uid: 7
|
||||
paused: false
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: nullB
|
||||
- uid: 8
|
||||
mapInit: true
|
||||
paused: true
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
- type: EntitySaveTest
|
||||
list: []
|
||||
id: nullC
|
||||
";
|
||||
private const string PrototypeV7 = @"
|
||||
- type: testTileDef
|
||||
id: space
|
||||
|
||||
- type: testTileDef
|
||||
id: a
|
||||
";
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class CategorizationTest : RobustIntegrationTest
|
||||
{
|
||||
private const string TestTileDefId = "a";
|
||||
private const string TestPrototypes = $@"
|
||||
- type: testTileDef
|
||||
id: space
|
||||
|
||||
- type: testTileDef
|
||||
id: {TestTileDefId}
|
||||
";
|
||||
|
||||
/// <summary>
|
||||
/// Check that file categories are correctly assigned when saving & loading different combinations of entites.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[TestOf(typeof(FileCategory))]
|
||||
public async Task TestCategorization()
|
||||
{
|
||||
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var meta = server.System<MetaDataSystem>();
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||
var path = new ResPath($"{nameof(TestCategorization)}.yml");
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
|
||||
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
|
||||
|
||||
EntityUid mapA = default;
|
||||
EntityUid mapB = default;
|
||||
EntityUid gridA = default; // grid on map A
|
||||
EntityUid gridB = default; // grid on map B
|
||||
EntityUid entA = default; // ent on grid A
|
||||
EntityUid entB = default; // ent on grid B
|
||||
EntityUid entC = default; // a separate entity on grid B
|
||||
EntityUid child = default; // child of entB
|
||||
EntityUid @null = default; // nullspace entity
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapA = mapSys.CreateMap(out var mapIdA);
|
||||
mapB = mapSys.CreateMap(out var mapIdB);
|
||||
var gridEntA = mapMan.CreateGridEntity(mapIdA);
|
||||
var gridEntB = mapMan.CreateGridEntity(mapIdB);
|
||||
mapSys.SetTile(gridEntA, Vector2i.Zero, new Tile(tDef.TileId));
|
||||
mapSys.SetTile(gridEntB, Vector2i.Zero, new Tile(tDef.TileId));
|
||||
gridA = gridEntA.Owner;
|
||||
gridB = gridEntB.Owner;
|
||||
entA = entMan.SpawnEntity(null, new EntityCoordinates(gridA, 0.5f, 0.5f));
|
||||
entB = entMan.SpawnEntity(null, new EntityCoordinates(gridB, 0.5f, 0.5f));
|
||||
entC = entMan.SpawnEntity(null, new EntityCoordinates(gridB, 0.5f, 0.5f));
|
||||
child = entMan.SpawnEntity(null, new EntityCoordinates(entB, 0.5f, 0.5f));
|
||||
@null = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
});
|
||||
|
||||
FileCategory Save(params EntityUid[] ents)
|
||||
{
|
||||
FileCategory cat = FileCategory.Unknown;
|
||||
Assert.That(loader.TrySaveGeneric(ents.ToHashSet(), path, out cat));
|
||||
return cat;
|
||||
}
|
||||
|
||||
async Task<LoadResult> Load(FileCategory expected, int count)
|
||||
{
|
||||
var opts = MapLoadOptions.Default with
|
||||
{
|
||||
ExpectedCategory = expected,
|
||||
DeserializationOptions = DeserializationOptions.Default with { LogOrphanedGrids = false}
|
||||
};
|
||||
LoadResult? result = null;
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(path, out result, opts)));
|
||||
Assert.That(result!.Category, Is.EqualTo(expected));
|
||||
Assert.That(result.Entities, Has.Count.EqualTo(count));
|
||||
return result;
|
||||
}
|
||||
|
||||
async Task SaveAndLoad(FileCategory expected, int count, params EntityUid[] ents)
|
||||
{
|
||||
var cat = Save(ents);
|
||||
Assert.That(cat, Is.EqualTo(expected));
|
||||
var result = await Load(expected, count);
|
||||
await server.WaitPost(() => loader.Delete(result));
|
||||
}
|
||||
|
||||
// Saving a single entity works as expected, even if it also serializes their children
|
||||
await SaveAndLoad(FileCategory.Entity, 1, entA);
|
||||
await SaveAndLoad(FileCategory.Entity, 2, entB);
|
||||
await SaveAndLoad(FileCategory.Entity, 1, child);
|
||||
|
||||
// Including nullspace entities doesn't change the category, though a file containing only null-space entities
|
||||
// is "unkown". Maybe in future they will get their own category
|
||||
await SaveAndLoad(FileCategory.Entity, 2, entA, @null);
|
||||
await SaveAndLoad(FileCategory.Entity, 3, entB, @null);
|
||||
await SaveAndLoad(FileCategory.Entity, 2, child, @null);
|
||||
await SaveAndLoad(FileCategory.Unknown, 1, @null);
|
||||
|
||||
// More than one entity is unknown
|
||||
await SaveAndLoad(FileCategory.Unknown, 3, entA, entB);
|
||||
await SaveAndLoad(FileCategory.Unknown, 4, entA, entB, @null);
|
||||
|
||||
// Saving grids works as expected. All counts are 1 higher than expected due to a map being automatically created.
|
||||
await SaveAndLoad(FileCategory.Grid, 3, gridA);
|
||||
await SaveAndLoad(FileCategory.Grid, 5, gridB);
|
||||
await SaveAndLoad(FileCategory.Grid, 4, gridA, @null);
|
||||
await SaveAndLoad(FileCategory.Grid, 6, gridB, @null);
|
||||
|
||||
// And saving maps also works
|
||||
await SaveAndLoad(FileCategory.Map, 3, mapA);
|
||||
await SaveAndLoad(FileCategory.Map, 5, mapB);
|
||||
await SaveAndLoad(FileCategory.Map, 4, mapA, @null);
|
||||
await SaveAndLoad(FileCategory.Map, 6, mapB, @null);
|
||||
|
||||
// Combinations of grids, entities, and maps, are unknown
|
||||
await SaveAndLoad(FileCategory.Unknown, 4, mapA, child);
|
||||
await SaveAndLoad(FileCategory.Unknown, 4, gridA, child);
|
||||
await SaveAndLoad(FileCategory.Unknown, 8, gridA, mapB);
|
||||
await SaveAndLoad(FileCategory.Unknown, 5, mapA, child, @null);
|
||||
await SaveAndLoad(FileCategory.Unknown, 5, gridA, child, @null);
|
||||
await SaveAndLoad(FileCategory.Unknown, 9, gridA, mapB, @null);
|
||||
}
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class LifestageSerializationTest : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that whether or not an entity has been paused or map-initialized is preserved across saves & loads.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestLifestageSerialization()
|
||||
{
|
||||
var server = StartServer();
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var preInitPath = new ResPath($"{nameof(TestLifestageSerialization)}_preInit.yml");
|
||||
var postInitPath = new ResPath($"{nameof(TestLifestageSerialization)}_postInit.yml");
|
||||
var pausedPostInitPath = new ResPath($"{nameof(TestLifestageSerialization)}_paused.yml");
|
||||
|
||||
// Create a pre-init map, and spawn multiple entities on it
|
||||
Entity<TransformComponent, EntitySaveTestComponent> map = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entA = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entB = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> childA = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> childB = default;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapUid = mapSys.CreateMap(out var mapId, runMapInit: false);
|
||||
var entAUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var entBUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var childAUid = entMan.SpawnEntity(null, new EntityCoordinates(entAUid, 0, 0));
|
||||
var childBUid = entMan.SpawnEntity(null, new EntityCoordinates(entBUid, 0, 0));
|
||||
map = Get(mapUid, entMan);
|
||||
entA = Get(entAUid, entMan);
|
||||
entB = Get(entBUid, entMan);
|
||||
childA = Get(childAUid, entMan);
|
||||
childB = Get(childBUid, entMan);
|
||||
map.Comp2.Id = nameof(map);
|
||||
entA.Comp2.Id = nameof(entA);
|
||||
entB.Comp2.Id = nameof(entB);
|
||||
childA.Comp2.Id = nameof(childA);
|
||||
childB.Comp2.Id = nameof(childB);
|
||||
});
|
||||
|
||||
void AssertPaused(bool expected, params EntityUid[] uids)
|
||||
{
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected));
|
||||
}
|
||||
}
|
||||
|
||||
void AssertPreInit(bool expected, params EntityUid[] uids)
|
||||
{
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
|
||||
expected
|
||||
? Is.LessThan(EntityLifeStage.MapInitialized)
|
||||
: Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
}
|
||||
}
|
||||
|
||||
// All entities should initially be un-initialized and paused.
|
||||
AssertPaused(true, map, entA, entB, childA, childB);
|
||||
AssertPreInit(true, map, entA, entB, childA, childB);
|
||||
Assert.That(loader.TrySaveMap(map, preInitPath));
|
||||
|
||||
async Task Delete()
|
||||
{
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(5));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
async Task Load(ResPath f, DeserializationOptions? o)
|
||||
{
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(f, out _, out _, o)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(5));
|
||||
}
|
||||
|
||||
void FindAll()
|
||||
{
|
||||
map = Find(nameof(map), entMan);
|
||||
entA = Find(nameof(entA), entMan);
|
||||
entB = Find(nameof(entB), entMan);
|
||||
childA = Find(nameof(childA), entMan);
|
||||
childB = Find(nameof(childB), entMan);
|
||||
}
|
||||
|
||||
async Task Reload(ResPath f, DeserializationOptions? o = null)
|
||||
{
|
||||
await Delete();
|
||||
await Load(f, o);
|
||||
FindAll();
|
||||
}
|
||||
|
||||
// Saving and loading the pre-init map should have no effect.
|
||||
await Reload(preInitPath);
|
||||
AssertPaused(true, map, entA, entB, childA, childB);
|
||||
AssertPreInit(true, map, entA, entB, childA, childB);
|
||||
|
||||
// Saving and loading with the map-init option set to true should initialize & unpause all entities
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await Reload(preInitPath, opts);
|
||||
AssertPaused(false, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
Assert.That(loader.TrySaveMap(map, postInitPath));
|
||||
|
||||
// re-loading the post-init map should keep everything initialized, even without explicitly asking to initialize maps.
|
||||
await Reload(postInitPath);
|
||||
AssertPaused(false, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
|
||||
// Load & initialize a pre-init map, but with the pause maps option enabled.
|
||||
opts = DeserializationOptions.Default with {InitializeMaps = true, PauseMaps = true};
|
||||
await Reload(preInitPath, opts);
|
||||
AssertPaused(true, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
Assert.That(loader.TrySaveMap(map, pausedPostInitPath));
|
||||
|
||||
// The pause map option also works when loading un-paused post-init maps
|
||||
opts = DeserializationOptions.Default with {PauseMaps = true};
|
||||
await Reload(postInitPath, opts);
|
||||
AssertPaused(true, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
|
||||
// loading & initializing a post-init map should cause no issues.
|
||||
opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await Reload(postInitPath, opts);
|
||||
AssertPaused(false, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
|
||||
// Loading a paused post init map does NOT automatically un-pause entities
|
||||
await Reload(pausedPostInitPath);
|
||||
AssertPaused(true, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
|
||||
// The above holds even if we are explicitly initialising maps.
|
||||
opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await Reload(pausedPostInitPath, opts);
|
||||
AssertPaused(true, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
|
||||
// And re-paused an already paused map should have no impact.
|
||||
opts = DeserializationOptions.Default with {InitializeMaps = true, PauseMaps = true};
|
||||
await Reload(pausedPostInitPath, opts);
|
||||
AssertPaused(true, map, entA, entB, childA, childB);
|
||||
AssertPreInit(false, map, entA, entB, childA, childB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TestLifestageSerialization"/> that has multiple maps and combinations. E.g., a single
|
||||
/// paused entity on an un-paused map.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestMixedLifestageSerialization()
|
||||
{
|
||||
var server = StartServer();
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var meta = server.System<MetaDataSystem>();
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var path = new ResPath($"{nameof(TestMixedLifestageSerialization)}.yml");
|
||||
var altPath = new ResPath($"{nameof(TestMixedLifestageSerialization)}_alt.yml");
|
||||
|
||||
Entity<TransformComponent, EntitySaveTestComponent> mapA = default; // preinit Map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> mapB = default; // postinit unpaused Map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entA = default; // postinit entity on preinit map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entB = default; // paused entity on postinit unpaused map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entC = default; // preinit entity on postinit map
|
||||
Entity<TransformComponent, EntitySaveTestComponent> nullA = default; // postinit nullspace entity
|
||||
Entity<TransformComponent, EntitySaveTestComponent> nullB = default; // preinit nullspace entity
|
||||
Entity<TransformComponent, EntitySaveTestComponent> nullC = default; // paused postinit nullspace entity
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapAUid = mapSys.CreateMap(out var mapIdA, runMapInit: false);
|
||||
var mapBUid = mapSys.CreateMap(out var mapIdB, runMapInit: true);
|
||||
|
||||
var entAUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapIdA));
|
||||
entMan.RunMapInit(entAUid, entMan.GetComponent<MetaDataComponent>(entAUid));
|
||||
meta.SetEntityPaused(entAUid, false);
|
||||
|
||||
var entBUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapIdB));
|
||||
meta.SetEntityPaused(entBUid, true);
|
||||
|
||||
var entCUid = entMan.CreateEntityUninitialized(null, new MapCoordinates(0, 0, mapIdB));
|
||||
entMan.InitializeAndStartEntity(entCUid, doMapInit: false);
|
||||
|
||||
var nullAUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
|
||||
var nullBUid = entMan.CreateEntityUninitialized(null, MapCoordinates.Nullspace);
|
||||
entMan.InitializeAndStartEntity(nullBUid, doMapInit: false);
|
||||
|
||||
var nullCUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
meta.SetEntityPaused(nullCUid, true);
|
||||
|
||||
mapA = Get(mapAUid, entMan);
|
||||
mapB = Get(mapBUid, entMan);
|
||||
entA = Get(entAUid, entMan);
|
||||
entB = Get(entBUid, entMan);
|
||||
entC = Get(entCUid, entMan);
|
||||
nullA = Get(nullAUid, entMan);
|
||||
nullB = Get(nullBUid, entMan);
|
||||
nullC = Get(nullCUid, entMan);
|
||||
|
||||
mapA.Comp2.Id = nameof(mapA);
|
||||
mapB.Comp2.Id = nameof(mapB);
|
||||
entA.Comp2.Id = nameof(entA);
|
||||
entB.Comp2.Id = nameof(entB);
|
||||
entC.Comp2.Id = nameof(entC);
|
||||
nullA.Comp2.Id = nameof(nullA);
|
||||
nullB.Comp2.Id = nameof(nullB);
|
||||
nullC.Comp2.Id = nameof(nullC);
|
||||
});
|
||||
|
||||
string? Name(EntityUid uid)
|
||||
{
|
||||
return entMan.GetComponentOrNull<EntitySaveTestComponent>(uid)?.Id;
|
||||
}
|
||||
|
||||
void AssertPaused(bool expected, params EntityUid[] uids)
|
||||
{
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected), Name(uid));
|
||||
}
|
||||
}
|
||||
|
||||
void AssertPreInit(bool expected, params EntityUid[] uids)
|
||||
{
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
|
||||
expected
|
||||
? Is.LessThan(EntityLifeStage.MapInitialized)
|
||||
: Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
}
|
||||
}
|
||||
|
||||
void Save(ResPath f)
|
||||
{
|
||||
Assert.That(loader.TrySaveGeneric([mapA, mapB, nullA, nullB, nullC], f, out _));
|
||||
}
|
||||
|
||||
async Task Delete()
|
||||
{
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(8));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(mapA));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(mapB));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(nullA));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(nullB));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(nullC));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
async Task Load(ResPath f, DeserializationOptions? o)
|
||||
{
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
var oo = MapLoadOptions.Default with
|
||||
{
|
||||
DeserializationOptions = o ?? DeserializationOptions.Default
|
||||
};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(f, out _, oo)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(8));
|
||||
}
|
||||
|
||||
void FindAll()
|
||||
{
|
||||
mapA = Find(nameof(mapA), entMan);
|
||||
mapB = Find(nameof(mapB), entMan);
|
||||
entA = Find(nameof(entA), entMan);
|
||||
entB = Find(nameof(entB), entMan);
|
||||
entC = Find(nameof(entC), entMan);
|
||||
nullA = Find(nameof(nullA), entMan);
|
||||
nullB = Find(nameof(nullB), entMan);
|
||||
nullC = Find(nameof(nullC), entMan);
|
||||
}
|
||||
|
||||
async Task Reload(ResPath f, DeserializationOptions? o = null)
|
||||
{
|
||||
await Delete();
|
||||
await Load(f, o);
|
||||
FindAll();
|
||||
}
|
||||
|
||||
// All entities should initially be in their respective expected states.
|
||||
// entC (pre-mapinit entity on a post-mapinit map) is a bit fucky, and I don't know if that should even be allowed.
|
||||
// Note that its just pre-init, not paused, as pre-mapinit entities get paused due to the maps state, not as a general result of being pre-mapinit.
|
||||
// If this ever changes, these assers need fixing.
|
||||
AssertPaused(true, mapA, entB, nullC);
|
||||
AssertPaused(false, mapB, entA, entC, nullA, nullB);
|
||||
AssertPreInit(true, mapA, entC, nullB);
|
||||
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
// Saving and re-loading entities should leave their metadata unchanged.
|
||||
Save(path);
|
||||
await Reload(path);
|
||||
AssertPaused(true, mapA, entB, nullC);
|
||||
AssertPaused(false, mapB, entA, entC, nullA, nullB);
|
||||
AssertPreInit(true, mapA, entC, nullB);
|
||||
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
// reload maps with the mapinit option. This should only affect mapA, as entA is the only one on the map and it
|
||||
// is already initialized,
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await Reload(path, opts);
|
||||
AssertPaused(true, entB, nullC);
|
||||
AssertPaused(false, mapA, mapB, entA, entC, nullA, nullB);
|
||||
AssertPreInit(true, entC, nullB);
|
||||
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
// Reloading the new configuration changes nothing
|
||||
Save(altPath);
|
||||
await Reload(altPath, opts);
|
||||
AssertPaused(true, entB, nullC);
|
||||
AssertPaused(false, mapA, mapB, entA, entC, nullA, nullB);
|
||||
AssertPreInit(true, entC, nullB);
|
||||
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
// Pause all maps. This will not actually pause entityA, as mapA is already paused (due to being pre-init), so
|
||||
// it will not iterate through its children. Maybe this will change in future, but I don't think we should even
|
||||
// be trying to actively support having post-init entities on a pre-init map. This is subject to maybe change
|
||||
// one day, though if it does the option should be changed to PauseEntities to clarify that it will pause ALL
|
||||
// entities, not just maps.
|
||||
opts = DeserializationOptions.Default with {PauseMaps = true};
|
||||
await Reload(path, opts);
|
||||
AssertPaused(true, mapA, mapB, entC, entB, nullC);
|
||||
AssertPaused(false, entA, nullA, nullB);
|
||||
AssertPreInit(true, mapA, entC, nullB);
|
||||
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
// Reloading the new configuration changes nothing
|
||||
Save(altPath);
|
||||
await Reload(altPath, opts);
|
||||
AssertPaused(true, mapA, mapB, entC, entB, nullC);
|
||||
AssertPaused(false, entA, nullA, nullB);
|
||||
AssertPreInit(true, mapA, entC, nullB);
|
||||
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
// Initialise and pause all maps. Similar to the previous test with entA, this will not affect entC even
|
||||
// though it is pre-init, because it is on a post-init map. Again, this is subject to maybe change one day.
|
||||
// Though if it does, the option should be changed to MapInitializeEntities to clarify that it will mapinit ALL
|
||||
// entities, not just maps.
|
||||
opts = DeserializationOptions.Default with {InitializeMaps = true, PauseMaps = true};
|
||||
await Reload(path, opts);
|
||||
AssertPaused(true, mapA, mapB, entB, entC, nullC);
|
||||
AssertPaused(false, entA, nullA, nullB);
|
||||
AssertPreInit(true, entC, nullB);
|
||||
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
|
||||
|
||||
// Reloading the new configuration changes nothing
|
||||
Save(altPath);
|
||||
await Reload(altPath, opts);
|
||||
AssertPaused(true, mapA, mapB, entB, entC, nullC);
|
||||
AssertPaused(false, entA, nullA, nullB);
|
||||
AssertPreInit(true, entC, nullB);
|
||||
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
/// <summary>
|
||||
/// Test that loading a pre-init map/grid onto a post-init map should initialize, while loading a post-init map/grid
|
||||
/// onto a paused map should pause it.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed partial class MapMergeTest : RobustIntegrationTest
|
||||
{
|
||||
private const string TestTileDefId = "a";
|
||||
private const string TestPrototypes = $@"
|
||||
- type: testTileDef
|
||||
id: space
|
||||
|
||||
- type: testTileDef
|
||||
id: {TestTileDefId}
|
||||
";
|
||||
|
||||
[Test]
|
||||
public async Task TestMapMerge()
|
||||
{
|
||||
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
|
||||
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(TestMapMerge)}_map.yml");
|
||||
var gridPath = new ResPath($"{nameof(TestMapMerge)}_grid.yml");
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
|
||||
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
|
||||
|
||||
MapId mapId = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> map = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> ent = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> grid = default;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapUid = mapSys.CreateMap(out mapId, runMapInit: false);
|
||||
var gridEnt = mapMan.CreateGridEntity(mapId);
|
||||
mapSys.SetTile(gridEnt, Vector2i.Zero, new Tile(tDef.TileId));
|
||||
var entUid = entMan.SpawnEntity(null, new MapCoordinates(10, 10, mapId));
|
||||
map = Get(mapUid, entMan);
|
||||
ent = Get(entUid, entMan);
|
||||
grid = Get(gridEnt.Owner, entMan);
|
||||
});
|
||||
|
||||
void AssertPaused(EntityUid uid, bool expected = true)
|
||||
{
|
||||
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected));
|
||||
}
|
||||
|
||||
void AssertPreInit(EntityUid uid, bool expected = true)
|
||||
{
|
||||
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
|
||||
expected
|
||||
? Is.LessThan(EntityLifeStage.MapInitialized)
|
||||
: Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
}
|
||||
|
||||
map.Comp2!.Id = nameof(map);
|
||||
ent.Comp2!.Id = nameof(ent);
|
||||
grid.Comp2!.Id = nameof(grid);
|
||||
|
||||
AssertPaused(map);
|
||||
AssertPreInit(map);
|
||||
AssertPaused(ent);
|
||||
AssertPreInit(ent);
|
||||
AssertPaused(grid);
|
||||
AssertPreInit(grid);
|
||||
|
||||
// Save then delete everything
|
||||
await server.WaitAssertion(() => Assert.That(loader.TrySaveMap(map, mapPath)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TrySaveGrid(grid, gridPath)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load a grid onto a pre-init map.
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.False);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.True);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid);
|
||||
AssertPreInit(grid);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Merge a map onto a pre-init map.
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.False);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.True);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2)); // The loaded map entity gets deleted after merging
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid);
|
||||
AssertPreInit(grid);
|
||||
AssertPaused(ent);
|
||||
AssertPreInit(ent);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load a grid onto a post-init map.
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.True);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.False);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid, false);
|
||||
AssertPreInit(grid, false);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Merge a map onto a post-init map.
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.True);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.False);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid, false);
|
||||
AssertPreInit(grid, false);
|
||||
AssertPaused(ent, false);
|
||||
AssertPreInit(ent, false);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load a grid onto a paused post-init map.
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
|
||||
await server.WaitPost(() => mapSys.SetPaused(mapId, true));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.True);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.True);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid);
|
||||
AssertPreInit(grid, false);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Merge a map onto a paused post-init map.
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
|
||||
await server.WaitPost(() => mapSys.SetPaused(mapId, true));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.True);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.True);
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid);
|
||||
AssertPreInit(grid, false);
|
||||
AssertPaused(ent);
|
||||
AssertPreInit(ent, false);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Check that the map initialization deserialziation options have no effect.
|
||||
// We are loading onto an existing map, deserialization shouldn't modify it directly.
|
||||
|
||||
|
||||
// Load a grid onto a pre-init map, with InitializeMaps = true
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.False);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.True);
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _, opts)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid);
|
||||
AssertPreInit(grid);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Merge a map onto a pre-init map, with InitializeMaps = true
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.False);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.True);
|
||||
opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _, opts)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2)); // The loaded map entity gets deleted after merging
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid);
|
||||
AssertPreInit(grid);
|
||||
AssertPaused(ent);
|
||||
AssertPreInit(ent);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load a grid onto a post-init map, with PauseMaps = true
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.True);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.False);
|
||||
opts = DeserializationOptions.Default with {PauseMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _, opts)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid, false);
|
||||
AssertPreInit(grid, false);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load a grid onto a post-init map, with PauseMaps = true
|
||||
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
|
||||
Assert.That(mapSys.IsInitialized(mapId), Is.True);
|
||||
Assert.That(mapSys.IsPaused(mapId), Is.False);
|
||||
opts = DeserializationOptions.Default with {PauseMaps = true};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _, opts)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
ent = Find(nameof(ent), entMan);
|
||||
grid = Find(nameof(grid), entMan);
|
||||
AssertPaused(grid, false);
|
||||
AssertPreInit(grid, false);
|
||||
AssertPaused(ent, false);
|
||||
AssertPreInit(ent, false);
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
using System.Numerics;
|
||||
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.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class OrphanSerializationTest : RobustIntegrationTest
|
||||
{
|
||||
private const string TestTileDefId = "a";
|
||||
private const string TestPrototypes = $@"
|
||||
- type: testTileDef
|
||||
id: space
|
||||
|
||||
- type: testTileDef
|
||||
id: {TestTileDefId}
|
||||
";
|
||||
|
||||
/// <summary>
|
||||
/// Check that we can save & load a file containing multiple orphaned (non-grid) entities.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestMultipleOrphanSerialization()
|
||||
{
|
||||
var server = StartServer();
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var xform = server.System<SharedTransformSystem>();
|
||||
var pathA = new ResPath($"{nameof(TestMultipleOrphanSerialization)}_A.yml");
|
||||
var pathB = new ResPath($"{nameof(TestMultipleOrphanSerialization)}_B.yml");
|
||||
var pathCombined = new ResPath($"{nameof(TestMultipleOrphanSerialization)}_C.yml");
|
||||
|
||||
// Spawn multiple entities on a map
|
||||
MapId mapId = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entA = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> entB = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> child = default;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapSys.CreateMap(out mapId);
|
||||
var entAUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var entBUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var childUid = entMan.SpawnEntity(null, new EntityCoordinates(entBUid, 0, 0));
|
||||
entA = Get(entAUid, entMan);
|
||||
entB = Get(entBUid, entMan);
|
||||
child = Get(childUid, entMan);
|
||||
entA.Comp2.Id = nameof(entA);
|
||||
entB.Comp2.Id = nameof(entB);
|
||||
child.Comp2.Id = nameof(child);
|
||||
xform.SetLocalPosition(entB.Owner, new (100,100));
|
||||
});
|
||||
|
||||
// Entities are not in null-space
|
||||
Assert.That(entA.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(entB.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(entB.Owner));
|
||||
|
||||
// Save the entities without their map
|
||||
Assert.That(loader.TrySaveEntity(entA, pathA));
|
||||
Assert.That(loader.TrySaveEntity(entB, pathB));
|
||||
Assert.That(loader.TrySaveGeneric([entA.Owner, entB.Owner], pathCombined, out var cat));
|
||||
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
|
||||
|
||||
// Delete all the entities.
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load in the file containing only entA.
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadEntity(pathA, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
|
||||
entA = Find(nameof(entA), entMan);
|
||||
Assert.That(entA.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(entA));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load in the file containing entB and its child
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadEntity(pathB, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
entB = Find(nameof(entB), entMan);
|
||||
child = Find(nameof(child), entMan);
|
||||
// Even though the entities are in null-space their local position is preserved.
|
||||
// This is so that you can save multiple entities on a map, without saving the map, while still preserving
|
||||
// relative positions for loading them onto some other map.
|
||||
Assert.That(entB.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
|
||||
Assert.That(entB.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(entB.Owner));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(entB));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load the file that contains both of them
|
||||
LoadResult? result = null;
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(pathCombined, out result)));
|
||||
Assert.That(result!.Category, Is.EqualTo(FileCategory.Unknown));
|
||||
Assert.That(result.Orphans, Has.Count.EqualTo(2));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
entA = Find(nameof(entA), entMan);
|
||||
entB = Find(nameof(entB), entMan);
|
||||
child = Find(nameof(child), entMan);
|
||||
Assert.That(entA.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(entB.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(entB.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
|
||||
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(entB.Owner));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(entA));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(entB));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that we can save & load a file containing multiple orphaned grid entities.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestOrphanedGridSerialization()
|
||||
{
|
||||
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
var xform = server.System<SharedTransformSystem>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
|
||||
var pathA = new ResPath($"{nameof(TestOrphanedGridSerialization)}_A.yml");
|
||||
var pathB = new ResPath($"{nameof(TestOrphanedGridSerialization)}_B.yml");
|
||||
var pathCombined = new ResPath($"{nameof(TestOrphanedGridSerialization)}_C.yml");
|
||||
|
||||
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
|
||||
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
|
||||
|
||||
// Spawn multiple entities on a map
|
||||
MapId mapId = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> map = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> gridA = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> gridB = default;
|
||||
Entity<TransformComponent, EntitySaveTestComponent> child = default;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapUid = mapSys.CreateMap(out mapId);
|
||||
map = Get(mapUid, entMan);
|
||||
|
||||
var gridAUid = mapMan.CreateGridEntity(mapId);
|
||||
mapSys.SetTile(gridAUid, Vector2i.Zero, new Tile(tDef.TileId));
|
||||
gridA = Get(gridAUid, entMan);
|
||||
xform.SetLocalPosition(gridA.Owner, new(100, 100));
|
||||
|
||||
var gridBUid = mapMan.CreateGridEntity(mapId);
|
||||
mapSys.SetTile(gridBUid, Vector2i.Zero, new Tile(tDef.TileId));
|
||||
gridB = Get(gridBUid, entMan);
|
||||
|
||||
var childUid = entMan.SpawnEntity(null, new EntityCoordinates(gridBUid, 0.5f, 0.5f));
|
||||
child = Get(childUid, entMan);
|
||||
|
||||
map.Comp2.Id = nameof(map);
|
||||
gridA.Comp2.Id = nameof(gridA);
|
||||
gridB.Comp2.Id = nameof(gridB);
|
||||
child.Comp2.Id = nameof(child);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
|
||||
// grids are not in null-space
|
||||
Assert.That(gridA.Comp1!.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(gridB.Comp1!.ParentUid, Is.EqualTo(map.Owner));
|
||||
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(gridB.Owner));
|
||||
Assert.That(map.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
|
||||
|
||||
// Save the grids without their map
|
||||
await server.WaitAssertion(() => Assert.That(loader.TrySaveGrid(gridA, pathA)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TrySaveGrid(gridB, pathB)));
|
||||
FileCategory cat = default;
|
||||
await server.WaitAssertion(() => Assert.That(loader.TrySaveGeneric([gridA.Owner, gridB.Owner], pathCombined, out cat)));
|
||||
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
|
||||
|
||||
// Delete all the entities.
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(4));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load in the file containing only gridA.
|
||||
EntityUid newMap = default;
|
||||
await server.WaitPost(() => newMap = mapSys.CreateMap(out mapId));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, pathA, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
|
||||
gridA = Find(nameof(gridA), entMan);
|
||||
Assert.That(gridA.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
|
||||
Assert.That(gridA.Comp1!.ParentUid, Is.EqualTo(newMap));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load in the file containing gridB and its child
|
||||
await server.WaitPost(() => newMap = mapSys.CreateMap(out mapId));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, pathB, out _)));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
gridB = Find(nameof(gridB), entMan);
|
||||
child = Find(nameof(child), entMan);
|
||||
Assert.That(gridB.Comp1!.ParentUid, Is.EqualTo(newMap));
|
||||
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(gridB.Owner));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
|
||||
// Load the file that contains both of them.
|
||||
// This uses the generic loader, and should automatically create maps for both grids.
|
||||
LoadResult? result = null;
|
||||
var opts = MapLoadOptions.Default with
|
||||
{
|
||||
DeserializationOptions = DeserializationOptions.Default with {LogOrphanedGrids = false}
|
||||
};
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(pathCombined, out result, opts)));
|
||||
Assert.That(result!.Category, Is.EqualTo(FileCategory.Unknown));
|
||||
Assert.That(result.Grids, Has.Count.EqualTo(2));
|
||||
Assert.That(result.Maps, Has.Count.EqualTo(2));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
gridA = Find(nameof(gridA), entMan);
|
||||
gridB = Find(nameof(gridB), entMan);
|
||||
child = Find(nameof(child), entMan);
|
||||
Assert.That(gridA.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
|
||||
Assert.That(gridA.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(gridB.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(gridB.Owner));
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
foreach (var ent in result.Maps)
|
||||
{
|
||||
entMan.DeleteEntity(ent.Owner);
|
||||
}
|
||||
});
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class RobustCloneableTestClass : IRobustCloneable<RobustCloneableTestClass>
|
||||
{
|
||||
[DataField]
|
||||
public int IntValue;
|
||||
|
||||
public RobustCloneableTestClass Clone()
|
||||
{
|
||||
return new RobustCloneableTestClass
|
||||
{
|
||||
IntValue = IntValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public partial struct RobustCloneableTestStruct : IRobustCloneable<RobustCloneableTestStruct>
|
||||
{
|
||||
[DataField]
|
||||
public int IntValue;
|
||||
|
||||
public RobustCloneableTestStruct Clone()
|
||||
{
|
||||
return new RobustCloneableTestStruct
|
||||
{
|
||||
IntValue = IntValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class RobustCloneableTestComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public RobustCloneableTestClass TestClass = new();
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public RobustCloneableTestStruct TestStruct = new();
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public RobustCloneableTestStruct? NullableTestStruct;
|
||||
}
|
||||
|
||||
public sealed class RobustCloneableTest() : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestClone()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(server.WaitIdleAsync(), client.WaitIdleAsync());
|
||||
|
||||
var sEntMan = server.EntMan;
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var cEntMan = client.EntMan;
|
||||
var cNetMan = client.ResolveDependency<IClientNetManager>();
|
||||
|
||||
MapId mapId = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
server.System<SharedMapSystem>().CreateMap(out mapId);
|
||||
var coords = new MapCoordinates(0, 0, mapId);
|
||||
var uid = sEntMan.SpawnEntity(null, coords);
|
||||
var comp = sEntMan.EnsureComponent<RobustCloneableTestComponent>(uid);
|
||||
comp.TestClass.IntValue = 50;
|
||||
comp.TestStruct.IntValue = 60;
|
||||
comp.NullableTestStruct = new() { IntValue = 70 };
|
||||
});
|
||||
|
||||
// Connect client.
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
await client.WaitPost(() => cNetMan.ClientConnect(null!, 0, null!));
|
||||
|
||||
async Task RunTicks()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
}
|
||||
await RunTicks();
|
||||
|
||||
EntityUid player = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var coords = new MapCoordinates(0, 0, mapId);
|
||||
player = sEntMan.SpawnEntity(null, coords);
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, player);
|
||||
sPlayerMan.JoinGame(session);
|
||||
});
|
||||
|
||||
await RunTicks();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(cNetMan.IsConnected, Is.True);
|
||||
var ents = cEntMan.AllEntities<RobustCloneableTestComponent>().ToList();
|
||||
Assert.That(ents, Has.Count.EqualTo(1));
|
||||
var testEnt = ents[0];
|
||||
|
||||
Assert.That(testEnt.Comp.TestClass.IntValue, Is.EqualTo(50));
|
||||
Assert.That(testEnt.Comp.TestStruct.IntValue, Is.EqualTo(60));
|
||||
Assert.That(testEnt.Comp.NullableTestStruct, Is.Not.Null);
|
||||
Assert.That(testEnt.Comp.NullableTestStruct!.Value.IntValue, Is.EqualTo(70));
|
||||
});
|
||||
|
||||
// Disconnect client
|
||||
await client.WaitPost(() => cNetMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
public static class SerializationTestHelper
|
||||
{
|
||||
public static void LoadTileDefs(IPrototypeManager protoMan, ITileDefinitionManager tileMan, string? spaceId = "Space")
|
||||
{
|
||||
var prototypeList = new List<TileDef>();
|
||||
foreach (var tileDef in protoMan.EnumeratePrototypes<TileDef>())
|
||||
{
|
||||
if (tileDef.ID == spaceId)
|
||||
{
|
||||
// Filter out the space tile def and register it first
|
||||
tileMan.Register(tileDef);
|
||||
continue;
|
||||
}
|
||||
|
||||
prototypeList.Add(tileDef);
|
||||
}
|
||||
|
||||
prototypeList.Sort((a, b) => string.Compare(a.ID, b.ID, StringComparison.Ordinal));
|
||||
|
||||
// Register the rest
|
||||
foreach (var tileDef in prototypeList)
|
||||
{
|
||||
tileMan.Register(tileDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.EntitySerialization;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class EntitySaveTestComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Give each entity a unique id to identify them across map saves & loads.
|
||||
/// </summary>
|
||||
[DataField] public string? Id;
|
||||
|
||||
[DataField] public EntityUid? Entity;
|
||||
|
||||
[DataField, AlwaysPushInheritance] public List<int> List = [];
|
||||
|
||||
/// <summary>
|
||||
/// Find an entity with a <see cref="EntitySaveTestComponent"/> with the matching id.
|
||||
/// </summary>
|
||||
public static Entity<TransformComponent, EntitySaveTestComponent> Find(string id, IEntityManager entMan)
|
||||
{
|
||||
var ents = entMan.AllEntities<EntitySaveTestComponent>();
|
||||
var matching = ents.Where(x => x.Comp.Id == id).ToArray();
|
||||
Assert.That(matching, Has.Length.EqualTo(1));
|
||||
return (matching[0].Owner, entMan.GetComponent<TransformComponent>(matching[0].Owner), matching[0].Comp);
|
||||
}
|
||||
|
||||
public static Entity<TransformComponent, EntitySaveTestComponent> Get(EntityUid uid, IEntityManager entMan)
|
||||
{
|
||||
return new Entity<TransformComponent, EntitySaveTestComponent>(
|
||||
uid,
|
||||
entMan.GetComponent<TransformComponent>(uid),
|
||||
entMan.EnsureComponent<EntitySaveTestComponent>(uid));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dummy tile definition for serializing grids.
|
||||
/// </summary>
|
||||
[Prototype("testTileDef")]
|
||||
public sealed partial class TileDef : ITileDefinition
|
||||
{
|
||||
public ushort TileId { get; set; }
|
||||
public string Name => ID;
|
||||
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
public ResPath? Sprite => null;
|
||||
public Dictionary<Direction, ResPath> EdgeSprites => new();
|
||||
public int EdgeSpritePriority => 0;
|
||||
public float Friction => 0;
|
||||
public byte Variants => 0;
|
||||
public void AssignTileId(ushort id) => TileId = id;
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ComponentFactory))]
|
||||
public sealed partial class ComponentFactory_Tests : RobustUnitTest
|
||||
{
|
||||
private const string TestComponentName = "A";
|
||||
private const string LowercaseTestComponentName = "a";
|
||||
private const string NonexistentComponentName = "B";
|
||||
protected override Type[]? ExtraComponents => new[] {typeof(TestComponent)};
|
||||
|
||||
[Test]
|
||||
public void GetComponentAvailabilityTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(NonexistentComponentName), Is.EqualTo(ComponentAvailability.Unknown));
|
||||
Assert.That(componentFactory.GetComponentAvailability(NonexistentComponentName, true), Is.EqualTo(ComponentAvailability.Unknown));
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(TestComponentName), Is.EqualTo(ComponentAvailability.Available));
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(TestComponentName, true), Is.EqualTo(ComponentAvailability.Available));
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(LowercaseTestComponentName), Is.EqualTo(ComponentAvailability.Unknown));
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponentAvailability(LowercaseTestComponentName, true), Is.EqualTo(ComponentAvailability.Available));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetComponentTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(NonexistentComponentName));
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(NonexistentComponentName, true));
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponent(TestComponentName), Is.InstanceOf<TestComponent>());
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponent(TestComponentName, true), Is.InstanceOf<TestComponent>());
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(LowercaseTestComponentName));
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.That(componentFactory.GetComponent(LowercaseTestComponentName, true), Is.InstanceOf<TestComponent>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetRegistrationTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(NonexistentComponentName));
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(NonexistentComponentName, true));
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.DoesNotThrow(() => componentFactory.GetRegistration(TestComponentName));
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.DoesNotThrow(() => componentFactory.GetRegistration(TestComponentName, true));
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(LowercaseTestComponentName));
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.DoesNotThrow(() => componentFactory.GetRegistration(LowercaseTestComponentName, true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetRegistrationTest()
|
||||
{
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
// Should not exist
|
||||
Assert.That(componentFactory.TryGetRegistration(NonexistentComponentName, out _), Is.False);
|
||||
Assert.That(componentFactory.TryGetRegistration(NonexistentComponentName, out _, true), Is.False);
|
||||
|
||||
// Normal casing, do not ignore case, should exist
|
||||
Assert.That(componentFactory.TryGetRegistration(TestComponentName, out _));
|
||||
|
||||
// Normal casing, ignore case, should exist
|
||||
Assert.That(componentFactory.TryGetRegistration(TestComponentName, out _, true));
|
||||
|
||||
// Lower casing, do not ignore case, should not exist
|
||||
Assert.That(componentFactory.TryGetRegistration(LowercaseTestComponentName, out _), Is.False);
|
||||
|
||||
// Lower casing, ignore case, should exist
|
||||
Assert.That(componentFactory.TryGetRegistration(LowercaseTestComponentName, out _, true));
|
||||
}
|
||||
|
||||
[ComponentProtoName(TestComponentName)]
|
||||
private sealed partial class TestComponent : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
public sealed class ContainerTests : RobustIntegrationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests container states with children that do not exist on the client
|
||||
/// and tests that said children are added to the container when they do arrive on the client.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Test]
|
||||
public async Task TestContainerNonexistantItems()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var cEntManager = client.ResolveDependency<IEntityManager>();
|
||||
var clientNetManager = client.ResolveDependency<IClientNetManager>();
|
||||
|
||||
var sEntManager = server.ResolveDependency<IEntityManager>();
|
||||
var sPlayerManager = server.ResolveDependency<IPlayerManager>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() =>
|
||||
{
|
||||
clientNetManager.ClientConnect(null!, 0, null!);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Setup
|
||||
var mapId = MapId.Nullspace;
|
||||
var mapPos = MapCoordinates.Nullspace;
|
||||
|
||||
EntityUid entityUid = default!;
|
||||
|
||||
var cContainerSys = cEntManager.System<ContainerSystem>();
|
||||
var sContainerSys = sEntManager.System<SharedContainerSystem>();
|
||||
var sMetadataSys = sEntManager.System<MetaDataSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
sEntManager.System<SharedMapSystem>().CreateMap(out mapId);
|
||||
mapPos = new MapCoordinates(new Vector2(0, 0), mapId);
|
||||
|
||||
entityUid = sEntManager.SpawnEntity(null, mapPos);
|
||||
sMetadataSys.SetEntityName(entityUid, "Container");
|
||||
sContainerSys.EnsureContainer<Container>(entityUid, "dummy");
|
||||
|
||||
// Setup PVS
|
||||
sEntManager.AddComponent<EyeComponent>(entityUid);
|
||||
var player = sPlayerManager.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(player, entityUid);
|
||||
sPlayerManager.JoinGame(player);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
EntityUid itemUid = default!;
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
itemUid = sEntManager.SpawnEntity(null, mapPos);
|
||||
sMetadataSys.SetEntityName(itemUid, "Item");
|
||||
var container = sContainerSys.EnsureContainer<Container>(entityUid, "dummy");
|
||||
Assert.That(sContainerSys.Insert(itemUid, container));
|
||||
|
||||
// Modify visibility layer so that the item does not get sent ot the player
|
||||
sEntManager.System<SharedVisibilitySystem>().AddLayer(itemUid, 10 );
|
||||
});
|
||||
|
||||
// Needs minimum 4 to sync to client because buffer size is 3
|
||||
await server.WaitRunTicks(4);
|
||||
await client.WaitRunTicks(10);
|
||||
|
||||
EntityUid cEntityUid = default!;
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
cEntityUid = client.EntMan.GetEntity(server.EntMan.GetNetEntity(entityUid));
|
||||
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cEntityUid, out var containerManagerComp))
|
||||
{
|
||||
Assert.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var container = cContainerSys.GetContainer(cEntityUid, "dummy", containerManagerComp);
|
||||
Assert.That(container.ContainedEntities.Count, Is.EqualTo(0));
|
||||
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(1));
|
||||
|
||||
Assert.That(cContainerSys.ExpectedEntities.ContainsKey(sEntManager.GetNetEntity(itemUid)));
|
||||
Assert.That(cContainerSys.ExpectedEntities.Count, Is.EqualTo(1));
|
||||
});
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// Modify visibility layer so it now gets sent to the client
|
||||
sEntManager.System<SharedVisibilitySystem>().RemoveLayer(itemUid, 10 );
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(4);
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cEntityUid, out var containerManagerComp))
|
||||
{
|
||||
Assert.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var container = cContainerSys.GetContainer(cEntityUid, "dummy", containerManagerComp);
|
||||
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
|
||||
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(0));
|
||||
|
||||
Assert.That(!cContainerSys.ExpectedEntities.ContainsKey(sEntManager.GetNetEntity(itemUid)));
|
||||
Assert.That(cContainerSys.ExpectedEntities, Is.Empty);
|
||||
});
|
||||
|
||||
await client.WaitPost(() => clientNetManager.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests container states with children that do not exist on the client
|
||||
/// and that if those children are deleted that they get properly removed from the expected entities list.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Test]
|
||||
public async Task TestContainerExpectedEntityDeleted()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
var cEntManager = client.ResolveDependency<IEntityManager>();
|
||||
var clientTime = client.ResolveDependency<IClientGameTiming>();
|
||||
var clientNetManager = client.ResolveDependency<IClientNetManager>();
|
||||
|
||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||
var sEntManager = server.ResolveDependency<IEntityManager>();
|
||||
var sPlayerManager = server.ResolveDependency<IPlayerManager>();
|
||||
var serverTime = server.ResolveDependency<IGameTiming>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
await client.WaitPost(() =>
|
||||
{
|
||||
clientNetManager.ClientConnect(null!, 0, null!);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
// Setup
|
||||
MapId mapId;
|
||||
var mapPos = MapCoordinates.Nullspace;
|
||||
|
||||
EntityUid sEntityUid = default!;
|
||||
EntityUid sItemUid = default!;
|
||||
NetEntity netEnt = default;
|
||||
|
||||
var cContainerSys = cEntManager.System<ContainerSystem>();
|
||||
var sContainerSys = sEntManager.System<SharedContainerSystem>();
|
||||
var sMetadataSys = sEntManager.System<MetaDataSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
sEntManager.System<SharedMapSystem>().CreateMap(out mapId);
|
||||
mapPos = new MapCoordinates(new Vector2(0, 0), mapId);
|
||||
|
||||
sEntityUid = sEntManager.SpawnEntity(null, mapPos);
|
||||
sMetadataSys.SetEntityName(sEntityUid, "Container");
|
||||
sContainerSys.EnsureContainer<Container>(sEntityUid, "dummy");
|
||||
|
||||
// Setup PVS
|
||||
sEntManager.AddComponent<EyeComponent>(sEntityUid);
|
||||
var player = sPlayerManager.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(player, sEntityUid);
|
||||
sPlayerManager.JoinGame(player);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
sItemUid = sEntManager.SpawnEntity(null, mapPos);
|
||||
netEnt = sEntManager.GetNetEntity(sItemUid);
|
||||
sMetadataSys.SetEntityName(sItemUid, "Item");
|
||||
var container = sContainerSys.GetContainer(sEntityUid, "dummy");
|
||||
sContainerSys.Insert(sItemUid, container);
|
||||
|
||||
// Modify visibility layer so that the item does not get sent ot the player
|
||||
sEntManager.System<SharedVisibilitySystem>().AddLayer(sItemUid, 10 );
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
while (clientTime.LastRealTick < serverTime.CurTick - 1)
|
||||
{
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
var cUid = cEntManager.GetEntity(sEntManager.GetNetEntity(sEntityUid));
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cUid, out var containerManagerComp))
|
||||
{
|
||||
Assert.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var container = cContainerSys.GetContainer(cUid, "dummy", containerManagerComp);
|
||||
Assert.That(container.ContainedEntities.Count, Is.EqualTo(0));
|
||||
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(1));
|
||||
|
||||
Assert.That(cContainerSys.ExpectedEntities.ContainsKey(netEnt));
|
||||
Assert.That(cContainerSys.ExpectedEntities.Count, Is.EqualTo(1));
|
||||
});
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// If possible it'd be best to only have the DeleteEntity, but right now
|
||||
// the entity deleted event is not played on the client if the entity does not exist on the client.
|
||||
if (sEntManager.EntityExists(sItemUid)
|
||||
// && itemUid.TryGetContainer(out var container))
|
||||
&& sContainerSys.TryGetContainingContainer(sItemUid, out var container))
|
||||
{
|
||||
sContainerSys.Remove(sItemUid, container, force: true);
|
||||
}
|
||||
|
||||
sEntManager.DeleteEntity(sItemUid);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(4);
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cUid, out var containerManagerComp))
|
||||
{
|
||||
Assert.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var container = cContainerSys.GetContainer(cUid, "dummy", containerManagerComp);
|
||||
Assert.That(container.ContainedEntities.Count, Is.EqualTo(0));
|
||||
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(0));
|
||||
|
||||
Assert.That(!cContainerSys.ExpectedEntities.ContainsKey(netEnt));
|
||||
Assert.That(cContainerSys.ExpectedEntities.Count, Is.EqualTo(0));
|
||||
});
|
||||
|
||||
await client.WaitPost(() => clientNetManager.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up a new container, initializes map, saves the map, then loads it again on another map. The contained entity should still
|
||||
/// be inside the container.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Container_DeserializeGrid_IsStillContained()
|
||||
{
|
||||
var server = StartServer();
|
||||
|
||||
await Task.WhenAll(server.WaitIdleAsync());
|
||||
|
||||
var sEntManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapSys = sEntManager.System<SharedMapSystem>();
|
||||
var sContainerSys = sEntManager.System<SharedContainerSystem>();
|
||||
var sMetadataSys = sEntManager.System<MetaDataSystem>();
|
||||
var path = new ResPath("container_test.yml");
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// build the map
|
||||
sEntManager.System<SharedMapSystem>().CreateMap(out var mapIdOne);
|
||||
Assert.That(mapSys.IsInitialized(mapIdOne), Is.True);
|
||||
|
||||
var containerEnt = sEntManager.SpawnEntity(null, new MapCoordinates(1, 1, mapIdOne));
|
||||
sMetadataSys.SetEntityName(containerEnt, "ContainerEnt");
|
||||
|
||||
var containeeEnt = sEntManager.SpawnEntity(null, new MapCoordinates(2, 2, mapIdOne));
|
||||
sMetadataSys.SetEntityName(containeeEnt, "ContaineeEnt");
|
||||
|
||||
var container = sContainerSys.MakeContainer<Container>(containerEnt, "testContainer");
|
||||
container.OccludesLight = true;
|
||||
container.ShowContents = true;
|
||||
sContainerSys.Insert(containeeEnt, container);
|
||||
|
||||
// save the map
|
||||
var mapLoader = sEntManager.EntitySysManager.GetEntitySystem<MapLoaderSystem>();
|
||||
|
||||
Assert.That(mapLoader.TrySaveMap(mapIdOne, path));
|
||||
mapSys.DeleteMap(mapIdOne);
|
||||
});
|
||||
|
||||
// A few moments later...
|
||||
await server.WaitRunTicks(10);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var mapLoader = sEntManager.System<MapLoaderSystem>();
|
||||
|
||||
// load the map
|
||||
Assert.That(mapLoader.TryLoadMap(path, out var map, out _));
|
||||
Assert.That(mapSys.IsInitialized(map), Is.True); // Map Initialize-ness is saved in the map file.
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// verify container
|
||||
Entity<ContainerManagerComponent> container = default;
|
||||
var query = sEntManager.EntityQueryEnumerator<ContainerManagerComponent>();
|
||||
while (query.MoveNext(out var uid, out var containerComp))
|
||||
{
|
||||
container = (uid, containerComp);
|
||||
}
|
||||
|
||||
var containerEnt = container.Owner;
|
||||
Assert.That(container.Comp, Is.Not.Null);
|
||||
|
||||
Assert.That(sEntManager.GetComponent<MetaDataComponent>(containerEnt).EntityName, Is.EqualTo("ContainerEnt"));
|
||||
|
||||
Assert.That(container.Comp!.Containers.ContainsKey("testContainer"));
|
||||
|
||||
var baseContainer = sContainerSys.GetContainer(containerEnt, "testContainer", container.Comp);
|
||||
Assert.That(baseContainer.ContainedEntities, Has.Count.EqualTo(1));
|
||||
|
||||
var containeeEnt = baseContainer.ContainedEntities[0];
|
||||
Assert.That(sEntManager.GetComponent<MetaDataComponent>(containeeEnt).EntityName, Is.EqualTo("ContaineeEnt"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects;
|
||||
|
||||
public sealed partial class DeferredEntityDeletionTest : RobustIntegrationTest
|
||||
{
|
||||
// This test ensures that deferred deletion can be used while handling events without issue, and that deleting an
|
||||
// entity after deferring component removal doesn't cause any issues.
|
||||
|
||||
[Test]
|
||||
public async Task TestDeferredEntityDeletion()
|
||||
{
|
||||
var options = new ServerIntegrationOptions();
|
||||
options.Pool = false;
|
||||
options.BeforeRegisterComponents += () =>
|
||||
{
|
||||
var fact = IoCManager.Resolve<IComponentFactory>();
|
||||
fact.RegisterClass<DeferredDeletionTestComponent>();
|
||||
fact.RegisterClass<OtherDeferredDeletionTestComponent>();
|
||||
};
|
||||
options.BeforeStart += () =>
|
||||
{
|
||||
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
|
||||
sysMan.LoadExtraSystemType<DeferredDeletionTestSystem>();
|
||||
sysMan.LoadExtraSystemType<OtherDeferredDeletionTestSystem>();
|
||||
};
|
||||
|
||||
var server = StartServer(options);
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
EntityUid uid1 = default, uid2 = default, uid3 = default, uid4 = default;
|
||||
DeferredDeletionTestComponent comp1 = default!, comp2 = default!, comp3 = default!, comp4 = default!;
|
||||
IEntityManager entMan = default!;
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
entMan = IoCManager.Resolve<IEntityManager>();
|
||||
var sys = entMan.EntitySysManager.GetEntitySystem<DeferredDeletionTestSystem>();
|
||||
|
||||
uid1 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
uid2 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
uid3 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
uid4 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
|
||||
comp1 = entMan.AddComponent<DeferredDeletionTestComponent>(uid1);
|
||||
comp2 = entMan.AddComponent<DeferredDeletionTestComponent>(uid2);
|
||||
comp3 = entMan.AddComponent<DeferredDeletionTestComponent>(uid3);
|
||||
comp4 = entMan.AddComponent<DeferredDeletionTestComponent>(uid4);
|
||||
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid1);
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid2);
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid3);
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid4);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
// first: test that deferring deletion while handling events doesn't cause issues
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(comp1.Running);
|
||||
var ev = new DeferredDeletionTestEvent();
|
||||
entMan.EventBus.RaiseLocalEvent(uid1, ev);
|
||||
Assert.That(comp1.LifeStage == ComponentLifeStage.Stopped);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
Assert.That(comp1.LifeStage == ComponentLifeStage.Deleted);
|
||||
|
||||
// next check that entity deletion doesn't cause issues:
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var ev = new DeferredDeletionTestEvent();
|
||||
entMan.EventBus.RaiseLocalEvent(uid2, ev);
|
||||
entMan.EventBus.RaiseLocalEvent(uid3, ev);
|
||||
entMan.EventBus.RaiseLocalEvent(uid4, ev);
|
||||
entMan.DeleteEntity(uid2);
|
||||
entMan.QueueDeleteEntity(uid3);
|
||||
entMan.TryQueueDeleteEntity(uid4);
|
||||
Assert.That(entMan.Deleted(uid2));
|
||||
Assert.That(!entMan.Deleted(uid3));
|
||||
Assert.That(!entMan.Deleted(uid4));
|
||||
Assert.That(comp2.LifeStage == ComponentLifeStage.Deleted);
|
||||
Assert.That(comp3.LifeStage == ComponentLifeStage.Stopped);
|
||||
Assert.That(comp4.LifeStage == ComponentLifeStage.Stopped);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
Assert.That(comp3.LifeStage == ComponentLifeStage.Deleted);
|
||||
Assert.That(comp4.LifeStage == ComponentLifeStage.Deleted);
|
||||
Assert.That(entMan.Deleted(uid3));
|
||||
Assert.That(entMan.Deleted(uid4));
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class DeferredDeletionTestSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<DeferredDeletionTestComponent, DeferredDeletionTestEvent>(OnTestEvent);
|
||||
}
|
||||
|
||||
private void OnTestEvent(EntityUid uid, DeferredDeletionTestComponent component, DeferredDeletionTestEvent args)
|
||||
{
|
||||
// remove both this component, and some other component that this entity has that also subscribes to this event.
|
||||
RemCompDeferred<DeferredDeletionTestComponent>(uid);
|
||||
RemCompDeferred<OtherDeferredDeletionTestComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class OtherDeferredDeletionTestSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize() => SubscribeLocalEvent<OtherDeferredDeletionTestComponent, DeferredDeletionTestEvent>(OnTestEvent);
|
||||
|
||||
private void OnTestEvent(EntityUid uid, OtherDeferredDeletionTestComponent component, DeferredDeletionTestEvent args)
|
||||
{
|
||||
// remove both this component, and some other component that this entity has that also subscribes to this event.
|
||||
RemCompDeferred<DeferredDeletionTestComponent>(uid);
|
||||
RemCompDeferred<OtherDeferredDeletionTestComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
[Reflect(false)]
|
||||
private sealed partial class DeferredDeletionTestComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed partial class OtherDeferredDeletionTestComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class DeferredDeletionTestEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.UnitTesting.Shared.Reflection;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
public sealed partial class EntityEventBusTests
|
||||
{
|
||||
[Test]
|
||||
public void SubscribeCompEvent()
|
||||
{
|
||||
var compFactory = new ComponentFactory(new DynamicTypeFactory(), new ReflectionManagerTest(), new SerializationManager(), new LogManager());
|
||||
|
||||
// Arrange
|
||||
var entUid = new EntityUid(7);
|
||||
var compInstance = new MetaDataComponent();
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
var reflectMock = new Mock<IReflectionManager>();
|
||||
|
||||
compFactory.RegisterClass<MetaDataComponent>();
|
||||
entManMock.Setup(m => m.ComponentFactory).Returns(compFactory);
|
||||
|
||||
IComponent? outIComponent = compInstance;
|
||||
entManMock.Setup(m => m.TryGetComponent(entUid, CompIdx.Index<MetaDataComponent>(), out outIComponent))
|
||||
.Returns(true);
|
||||
|
||||
entManMock.Setup(m => m.GetComponent(entUid, CompIdx.Index<MetaDataComponent>()))
|
||||
.Returns(compInstance);
|
||||
|
||||
entManMock.Setup(m => m.GetComponentInternal(entUid, CompIdx.Index<MetaDataComponent>()))
|
||||
.Returns(compInstance);
|
||||
|
||||
var bus = new EntityEventBus(entManMock.Object, reflectMock.Object);
|
||||
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
|
||||
|
||||
// Subscribe
|
||||
int calledCount = 0;
|
||||
bus.SubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// add a component to the system
|
||||
bus.OnEntityAdded(entUid);
|
||||
|
||||
var reg = compFactory.GetRegistration(CompIdx.Index<MetaDataComponent>());
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
bus.RaiseLocalEvent(entUid, evntArgs, true);
|
||||
|
||||
// Assert
|
||||
Assert.That(calledCount, Is.EqualTo(1));
|
||||
void HandleTestEvent(EntityUid uid, MetaDataComponent component, TestEvent args)
|
||||
{
|
||||
calledCount++;
|
||||
Assert.That(uid, Is.EqualTo(entUid));
|
||||
Assert.That(component, Is.EqualTo(compInstance));
|
||||
Assert.That(args.TestNumber, Is.EqualTo(5));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnsubscribeCompEvent()
|
||||
{
|
||||
// Arrange
|
||||
var entUid = new EntityUid(7);
|
||||
var compInstance = new MetaDataComponent();
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
|
||||
var compRegistration = new ComponentRegistration(
|
||||
"MetaData",
|
||||
typeof(MetaDataComponent),
|
||||
CompIdx.Index<MetaDataComponent>());
|
||||
|
||||
var compFacMock = new Mock<IComponentFactory>();
|
||||
var reflectMock = new Mock<IReflectionManager>();
|
||||
|
||||
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<MetaDataComponent>())).Returns(compRegistration);
|
||||
compFacMock.Setup(m => m.GetAllRegistrations()).Returns(new[] { compRegistration });
|
||||
compFacMock.Setup(m => m.GetIndex(typeof(MetaDataComponent))).Returns(CompIdx.Index<MetaDataComponent>());
|
||||
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
|
||||
|
||||
IComponent? outIComponent = compInstance;
|
||||
entManMock.Setup(m => m.TryGetComponent(entUid, typeof(MetaDataComponent), out outIComponent))
|
||||
.Returns(true);
|
||||
|
||||
entManMock.Setup(m => m.GetComponent(entUid, typeof(MetaDataComponent)))
|
||||
.Returns(compInstance);
|
||||
|
||||
var bus = new EntityEventBus(entManMock.Object, reflectMock.Object);
|
||||
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
|
||||
|
||||
// Subscribe
|
||||
int calledCount = 0;
|
||||
bus.SubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
|
||||
bus.UnsubscribeLocalEvent<MetaDataComponent, TestEvent>();
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// add a component to the system
|
||||
bus.OnEntityAdded(entUid);
|
||||
|
||||
var reg = compFacMock.Object.GetRegistration(CompIdx.Index<MetaDataComponent>());
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
bus.RaiseLocalEvent(entUid, evntArgs, true);
|
||||
|
||||
// Assert
|
||||
Assert.That(calledCount, Is.EqualTo(0));
|
||||
void HandleTestEvent(EntityUid uid, MetaDataComponent component, TestEvent args)
|
||||
{
|
||||
calledCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscribeCompLifeEvent()
|
||||
{
|
||||
// Arrange
|
||||
var entUid = new EntityUid(7);
|
||||
var compInstance = new MetaDataComponent();
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
compInstance.Owner = entUid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
var compRegistration = new ComponentRegistration(
|
||||
"MetaData",
|
||||
typeof(MetaDataComponent),
|
||||
CompIdx.Index<MetaDataComponent>());
|
||||
|
||||
var compFacMock = new Mock<IComponentFactory>();
|
||||
var reflectMock = new Mock<IReflectionManager>();
|
||||
|
||||
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<MetaDataComponent>())).Returns(compRegistration);
|
||||
compFacMock.Setup(m => m.GetAllRegistrations()).Returns(new[] { compRegistration });
|
||||
compFacMock.Setup(m => m.GetIndex(typeof(MetaDataComponent))).Returns(CompIdx.Index<MetaDataComponent>());
|
||||
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
|
||||
|
||||
IComponent? outIComponent = compInstance;
|
||||
entManMock.Setup(m => m.TryGetComponent(entUid, typeof(MetaDataComponent), out outIComponent))
|
||||
.Returns(true);
|
||||
|
||||
entManMock.Setup(m => m.GetComponent(entUid, typeof(MetaDataComponent)))
|
||||
.Returns(compInstance);
|
||||
|
||||
var bus = new EntityEventBus(entManMock.Object, reflectMock.Object);
|
||||
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
|
||||
|
||||
// Subscribe
|
||||
int calledCount = 0;
|
||||
bus.SubscribeLocalEvent<MetaDataComponent, ComponentInit>(HandleTestEvent);
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// add a component to the system
|
||||
entManMock.Raise(m => m.EntityAdded += null, entUid);
|
||||
|
||||
var reg = compFacMock.Object.GetRegistration<MetaDataComponent>();
|
||||
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg));
|
||||
|
||||
// Raise
|
||||
((IEventBus)bus).RaiseComponentEvent(entUid, compInstance, new ComponentInit());
|
||||
|
||||
// Assert
|
||||
Assert.That(calledCount, Is.EqualTo(1));
|
||||
void HandleTestEvent(EntityUid uid, MetaDataComponent component, ComponentInit args)
|
||||
{
|
||||
calledCount++;
|
||||
Assert.That(uid, Is.EqualTo(entUid));
|
||||
Assert.That(component, Is.EqualTo(compInstance));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompEventOrdered()
|
||||
{
|
||||
// Arrange
|
||||
var entUid = new EntityUid(7);
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
var compFacMock = new Mock<IComponentFactory>();
|
||||
var reflectMock = new Mock<IReflectionManager>();
|
||||
|
||||
List<ComponentRegistration> allRefTypes = new();
|
||||
void Setup<T>(out T instance) where T : IComponent, new()
|
||||
{
|
||||
IComponent? inst = instance = new T();
|
||||
var reg = new ComponentRegistration(
|
||||
typeof(T).Name,
|
||||
typeof(T),
|
||||
CompIdx.Index<T>());
|
||||
|
||||
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<T>())).Returns(reg);
|
||||
compFacMock.Setup(m => m.GetIndex(typeof(T))).Returns(CompIdx.Index<T>());
|
||||
entManMock.Setup(m => m.TryGetComponent(entUid, CompIdx.Index<T>(), out inst)).Returns(true);
|
||||
entManMock.Setup(m => m.GetComponent(entUid, CompIdx.Index<T>())).Returns(inst);
|
||||
entManMock.Setup(m => m.GetComponentInternal(entUid, CompIdx.Index<T>())).Returns(inst);
|
||||
allRefTypes.Add(reg);
|
||||
}
|
||||
|
||||
Setup<OrderAComponent>(out var instA);
|
||||
Setup<OrderBComponent>(out var instB);
|
||||
Setup<OrderCComponent>(out var instC);
|
||||
|
||||
compFacMock.Setup(m => m.GetAllRegistrations()).Returns(allRefTypes.ToArray());
|
||||
|
||||
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
|
||||
var bus = new EntityEventBus(entManMock.Object, reflectMock.Object);
|
||||
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
|
||||
|
||||
// Subscribe
|
||||
var a = false;
|
||||
var b = false;
|
||||
var c = false;
|
||||
|
||||
void HandlerA(EntityUid uid, Component comp, TestEvent ev)
|
||||
{
|
||||
Assert.That(b, Is.False, "A should run before B");
|
||||
Assert.That(c, Is.False, "A should run before C");
|
||||
|
||||
a = true;
|
||||
}
|
||||
|
||||
void HandlerB(EntityUid uid, Component comp, TestEvent ev)
|
||||
{
|
||||
Assert.That(c, Is.True, "B should run after C");
|
||||
b = true;
|
||||
}
|
||||
|
||||
void HandlerC(EntityUid uid, Component comp, TestEvent ev) => c = true;
|
||||
|
||||
bus.SubscribeLocalEvent<OrderAComponent, TestEvent>(HandlerA, typeof(OrderAComponent), before: new []{typeof(OrderBComponent), typeof(OrderCComponent)});
|
||||
bus.SubscribeLocalEvent<OrderBComponent, TestEvent>(HandlerB, typeof(OrderBComponent), after: new []{typeof(OrderCComponent)});
|
||||
bus.SubscribeLocalEvent<OrderCComponent, TestEvent>(HandlerC, typeof(OrderCComponent));
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// add a component to the system
|
||||
bus.OnEntityAdded(entUid);
|
||||
|
||||
var regA = compFacMock.Object.GetRegistration(CompIdx.Index<OrderAComponent>());
|
||||
var regB = compFacMock.Object.GetRegistration(CompIdx.Index<OrderBComponent>());
|
||||
var regC = compFacMock.Object.GetRegistration(CompIdx.Index<OrderCComponent>());
|
||||
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instC, entUid), regC));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
bus.RaiseLocalEvent(entUid, evntArgs, true);
|
||||
|
||||
// Assert
|
||||
Assert.That(a, Is.True, "A did not fire");
|
||||
Assert.That(b, Is.True, "B did not fire");
|
||||
Assert.That(c, Is.True, "C did not fire");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompEventLoop()
|
||||
{
|
||||
var entUid = new EntityUid(7);
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
var compFacMock = new Mock<IComponentFactory>();
|
||||
var reflectMock = new Mock<IReflectionManager>();
|
||||
|
||||
List<ComponentRegistration> allRefTypes = new();
|
||||
void Setup<T>(out T instance) where T : IComponent, new()
|
||||
{
|
||||
IComponent? inst = instance = new T();
|
||||
var reg = new ComponentRegistration(
|
||||
typeof(T).Name,
|
||||
typeof(T),
|
||||
CompIdx.Index<T>());
|
||||
|
||||
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<T>())).Returns(reg);
|
||||
compFacMock.Setup(m => m.GetIndex(typeof(T))).Returns(CompIdx.Index<T>());
|
||||
entManMock.Setup(m => m.TryGetComponent(entUid, CompIdx.Index<T>(), out inst)).Returns(true);
|
||||
entManMock.Setup(m => m.GetComponent(entUid, CompIdx.Index<T>())).Returns(inst);
|
||||
entManMock.Setup(m => m.GetComponentInternal(entUid, CompIdx.Index<T>())).Returns(inst);
|
||||
allRefTypes.Add(reg);
|
||||
}
|
||||
|
||||
Setup<OrderAComponent>(out var instA);
|
||||
Setup<OrderBComponent>(out var instB);
|
||||
|
||||
compFacMock.Setup(m => m.GetAllRegistrations()).Returns(allRefTypes.ToArray());
|
||||
|
||||
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
|
||||
var bus = new EntityEventBus(entManMock.Object, reflectMock.Object);
|
||||
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
|
||||
|
||||
var regA = compFacMock.Object.GetRegistration(CompIdx.Index<OrderAComponent>());
|
||||
var regB = compFacMock.Object.GetRegistration(CompIdx.Index<OrderBComponent>());
|
||||
|
||||
var handlerACount = 0;
|
||||
void HandlerA(EntityUid uid, Component comp, TestEvent ev)
|
||||
{
|
||||
Assert.That(handlerACount, Is.EqualTo(0));
|
||||
handlerACount++;
|
||||
|
||||
// add and then remove component B
|
||||
bus.OnComponentRemoved(new RemovedComponentEventArgs(new ComponentEventArgs(instB, entUid), false, default!, CompIdx.Index<OrderBComponent>()));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB));
|
||||
}
|
||||
|
||||
var handlerBCount = 0;
|
||||
void HandlerB(EntityUid uid, Component comp, TestEvent ev)
|
||||
{
|
||||
Assert.That(handlerBCount, Is.EqualTo(0));
|
||||
handlerBCount++;
|
||||
|
||||
// add and then remove component A
|
||||
bus.OnComponentRemoved(new RemovedComponentEventArgs(new ComponentEventArgs(instA, entUid), false, default!, CompIdx.Index<OrderAComponent>()));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA));
|
||||
}
|
||||
|
||||
bus.SubscribeLocalEvent<OrderAComponent, TestEvent>(HandlerA, typeof(OrderAComponent));
|
||||
bus.SubscribeLocalEvent<OrderBComponent, TestEvent>(HandlerB, typeof(OrderBComponent));
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// add a component to the system
|
||||
bus.OnEntityAdded(entUid);
|
||||
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB));
|
||||
|
||||
// Event subscriptions currently use a linked list.
|
||||
// Currently expect event subscriptions to be raised in order: handlerB -> handlerA
|
||||
// If a component gets removed and added again, it gets moved back to the front of the linked list.
|
||||
// I.e., adding and then removing compA changes the linked list order: handlerA -> handlerB
|
||||
//
|
||||
// This could in principle cause the event raising code to enter an infinite loop.
|
||||
// Adding and removing a comp in an event handler may seem silly but:
|
||||
// - it doesn't have to be the same component if you had a chain of three or more components
|
||||
// - some event handlers raise other events and can lead to convoluted chains of interactions that might inadvertently trigger something like this.
|
||||
|
||||
// Raise
|
||||
bus.RaiseLocalEvent(entUid, new TestEvent(0));
|
||||
|
||||
// Assert
|
||||
Assert.That(handlerACount, Is.LessThanOrEqualTo(1));
|
||||
Assert.That(handlerBCount, Is.LessThanOrEqualTo(1));
|
||||
Assert.That(handlerACount+handlerBCount, Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
private sealed partial class DummyComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
private sealed partial class OrderAComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
private sealed partial class OrderBComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
private sealed partial class OrderCComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class TestEvent : EntityEventArgs
|
||||
{
|
||||
public int TestNumber { get; }
|
||||
|
||||
public TestEvent(int testNumber)
|
||||
{
|
||||
TestNumber = testNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects;
|
||||
|
||||
public sealed partial class EntityEventBusTests
|
||||
{
|
||||
// Explanation of what bug this is testing:
|
||||
// Because event ordering is keyed on system type, we have a problem.
|
||||
// If you register to a directed event like FooEvent twice for different components,
|
||||
// you now have different subscriptions with the same key.
|
||||
//
|
||||
// To trigger this, at least one subscription to this event (possibly another system entirely)
|
||||
// needs to demand some ordering calculation to happen.
|
||||
|
||||
[Test]
|
||||
public void TestDifferentComponentsOrderedSameKeySub()
|
||||
{
|
||||
var simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterEntitySystems(factory =>
|
||||
{
|
||||
factory.LoadExtraSystemType<DifferentComponentsSameKeySubSystem>();
|
||||
factory.LoadExtraSystemType<DifferentComponentsSameKeySubSystem2>();
|
||||
})
|
||||
.RegisterComponents(factory => factory.RegisterClass<FooComponent>())
|
||||
.InitializeInstance();
|
||||
|
||||
var map = simulation.CreateMap().MapId;
|
||||
|
||||
var entity = simulation.SpawnEntity(null, new MapCoordinates(0, 0, map));
|
||||
simulation.Resolve<IEntityManager>().AddComponent<FooComponent>(entity);
|
||||
|
||||
var foo = new FooEvent();
|
||||
simulation.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(entity, foo, true);
|
||||
|
||||
Assert.That(foo.EventOrder, Is.EquivalentTo(new[]{"Foo", "Transform", "Metadata"}).Or.EquivalentTo(new[]{"Foo", "Metadata", "Transform"}));
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class DifferentComponentsSameKeySubSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<TransformComponent, FooEvent>((_, _, e) => { e.EventOrder.Add("Transform"); });
|
||||
SubscribeLocalEvent<MetaDataComponent, FooEvent>((_, _, e) => { e.EventOrder.Add("Metadata"); });
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class DifferentComponentsSameKeySubSystem2 : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FooComponent, FooEvent>(
|
||||
(_, _, e) => e.EventOrder.Add("Foo"),
|
||||
before: new[] {typeof(DifferentComponentsSameKeySubSystem)});
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed partial class FooComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private sealed class FooEvent
|
||||
{
|
||||
public List<string> EventOrder = new();
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class EntityEventBusTests
|
||||
{
|
||||
[Test]
|
||||
public void SubscribeCompRefBroadcastEvent()
|
||||
{
|
||||
// Arrange.
|
||||
var simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterEntitySystems(factory => factory.LoadExtraSystemType<SubscribeCompRefBroadcastSystem>())
|
||||
.InitializeInstance();
|
||||
|
||||
var ev = new TestStructEvent() {TestNumber = 5};
|
||||
simulation.Resolve<IEntityManager>().EventBus.RaiseEvent(EventSource.Local, ref ev);
|
||||
Assert.That(ev.TestNumber, Is.EqualTo(15));
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
public sealed class SubscribeCompRefBroadcastSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TestStructEvent>(OnTestEvent);
|
||||
}
|
||||
|
||||
private void OnTestEvent(ref TestStructEvent ev)
|
||||
{
|
||||
Assert.That(ev.TestNumber, Is.EqualTo(5));
|
||||
ev.TestNumber += 10;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscriptionNoMixedRefValueBroadcastEvent()
|
||||
{
|
||||
// Arrange.
|
||||
var simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterEntitySystems(factory =>
|
||||
factory.LoadExtraSystemType<SubscriptionNoMixedRefValueBroadcastEventSystem>());
|
||||
|
||||
// Act. No mixed ref and value subscriptions are allowed.
|
||||
Assert.Throws(typeof(InvalidOperationException), () => simulation.InitializeInstance());
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class SubscriptionNoMixedRefValueBroadcastEventSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
// The below is not allowed, as you're subscribing by-ref and by-value to the same event...
|
||||
#pragma warning disable RA0013
|
||||
SubscribeLocalEvent<TestStructEvent>(MyRefHandler);
|
||||
SubscribeLocalEvent<TestStructEvent>(MyValueHandler);
|
||||
#pragma warning restore RA0013
|
||||
}
|
||||
|
||||
private void MyValueHandler(TestStructEvent args) { }
|
||||
private void MyRefHandler(ref TestStructEvent args) { }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SortedBroadcastRefEvents()
|
||||
{
|
||||
// Arrange.
|
||||
var simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterEntitySystems(factory =>
|
||||
{
|
||||
factory.LoadExtraSystemType<BroadcastOrderASystem>();
|
||||
factory.LoadExtraSystemType<BroadcastOrderBSystem>();
|
||||
factory.LoadExtraSystemType<BroadcastOrderCSystem>();
|
||||
})
|
||||
.InitializeInstance();
|
||||
|
||||
// Act.
|
||||
var testEvent = new TestStructEvent {TestNumber = 5};
|
||||
var eventBus = simulation.Resolve<IEntityManager>().EventBus;
|
||||
eventBus.RaiseEvent(EventSource.Local, ref testEvent);
|
||||
|
||||
// Check that the entity systems changed the value correctly
|
||||
Assert.That(testEvent.TestNumber, Is.EqualTo(15));
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class BroadcastOrderASystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TestStructEvent>(OnA, new[]{typeof(BroadcastOrderBSystem)}, new[]{typeof(BroadcastOrderCSystem)});
|
||||
}
|
||||
|
||||
private void OnA(ref TestStructEvent args)
|
||||
{
|
||||
// Second handler being ran.
|
||||
Assert.That(args.TestNumber, Is.EqualTo(0));
|
||||
args.TestNumber = 10;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class BroadcastOrderBSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TestStructEvent>(OnB, null, new []{typeof(BroadcastOrderASystem)});
|
||||
}
|
||||
|
||||
private void OnB(ref TestStructEvent args)
|
||||
{
|
||||
// Last handler being ran.
|
||||
Assert.That(args.TestNumber, Is.EqualTo(10));
|
||||
args.TestNumber = 15;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class BroadcastOrderCSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TestStructEvent>(OnC);
|
||||
}
|
||||
|
||||
private void OnC(ref TestStructEvent args)
|
||||
{
|
||||
// First handler being ran.
|
||||
Assert.That(args.TestNumber, Is.EqualTo(5));
|
||||
args.TestNumber = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
public partial class EntityEventBusTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void SubscribeCompRefDirectedEvent()
|
||||
{
|
||||
// Arrange.
|
||||
var simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterComponents(factory => factory.RegisterClass<DummyComponent>())
|
||||
.RegisterEntitySystems(factory => factory.LoadExtraSystemType<SubscribeCompRefDirectedEventSystem>())
|
||||
.InitializeInstance();
|
||||
|
||||
var map = simulation.CreateMap().MapId;
|
||||
var entity = simulation.SpawnEntity(null, new MapCoordinates(0, 0, map));
|
||||
IoCManager.Resolve<IEntityManager>().AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act.
|
||||
var testEvent = new TestStructEvent {TestNumber = 5};
|
||||
var eventBus = simulation.Resolve<IEntityManager>().EventBus;
|
||||
eventBus.RaiseLocalEvent(entity, ref testEvent, true);
|
||||
|
||||
// Check that the entity system changed the value correctly
|
||||
Assert.That(testEvent.TestNumber, Is.EqualTo(10));
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class SubscribeCompRefDirectedEventSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<DummyComponent, TestStructEvent>(MyRefHandler);
|
||||
}
|
||||
|
||||
private void MyRefHandler(EntityUid uid, DummyComponent component, ref TestStructEvent args)
|
||||
{
|
||||
Assert.That(args.TestNumber, Is.EqualTo(5));
|
||||
args.TestNumber = 10;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class SubscriptionNoMixedRefValueDirectedEventSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
// The below is not allowed, as you're subscribing by-ref and by-value to the same event...
|
||||
SubscribeLocalEvent<DummyComponent, TestStructEvent>(MyRefHandler);
|
||||
#pragma warning disable RA0013
|
||||
SubscribeLocalEvent<DummyTwoComponent, TestStructEvent>(MyValueHandler);
|
||||
#pragma warning restore RA0013
|
||||
}
|
||||
|
||||
private void MyValueHandler(EntityUid uid, DummyTwoComponent component, TestStructEvent args) { }
|
||||
private void MyRefHandler(EntityUid uid, DummyComponent component, ref TestStructEvent args) { }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SortedDirectedRefEvents()
|
||||
{
|
||||
// Arrange.
|
||||
var simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterComponents(factory =>
|
||||
{
|
||||
factory.RegisterClass<OrderAComponent>();
|
||||
factory.RegisterClass<OrderBComponent>();
|
||||
factory.RegisterClass<OrderCComponent>();
|
||||
})
|
||||
.RegisterEntitySystems(factory =>
|
||||
{
|
||||
factory.LoadExtraSystemType<OrderASystem>();
|
||||
factory.LoadExtraSystemType<OrderBSystem>();
|
||||
factory.LoadExtraSystemType<OrderCSystem>();
|
||||
})
|
||||
.InitializeInstance();
|
||||
|
||||
var map = simulation.CreateMap().MapId;
|
||||
var entity = simulation.SpawnEntity(null, new MapCoordinates(0, 0, map));
|
||||
IoCManager.Resolve<IEntityManager>().AddComponent<OrderAComponent>(entity);
|
||||
IoCManager.Resolve<IEntityManager>().AddComponent<OrderBComponent>(entity);
|
||||
IoCManager.Resolve<IEntityManager>().AddComponent<OrderCComponent>(entity);
|
||||
|
||||
// Act.
|
||||
var testEvent = new TestStructEvent {TestNumber = 5};
|
||||
var eventBus = simulation.Resolve<IEntityManager>().EventBus;
|
||||
eventBus.RaiseLocalEvent(entity, ref testEvent, true);
|
||||
|
||||
// Check that the entity systems changed the value correctly
|
||||
Assert.That(testEvent.TestNumber, Is.EqualTo(15));
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class OrderASystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<OrderAComponent, TestStructEvent>(OnA, new[]{typeof(OrderBSystem)}, new[]{typeof(OrderCSystem)});
|
||||
}
|
||||
|
||||
private void OnA(EntityUid uid, OrderAComponent component, ref TestStructEvent args)
|
||||
{
|
||||
// Second handler being ran.
|
||||
Assert.That(args.TestNumber, Is.EqualTo(0));
|
||||
args.TestNumber = 10;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class OrderBSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<OrderBComponent, TestStructEvent>(OnB, null, new []{typeof(OrderASystem)});
|
||||
}
|
||||
|
||||
private void OnB(EntityUid uid, OrderBComponent component, ref TestStructEvent args)
|
||||
{
|
||||
// Last handler being ran.
|
||||
Assert.That(args.TestNumber, Is.EqualTo(10));
|
||||
args.TestNumber = 15;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class OrderCSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<OrderCComponent, TestStructEvent>(OnC);
|
||||
}
|
||||
|
||||
private void OnC(EntityUid uid, OrderCComponent component, ref TestStructEvent args)
|
||||
{
|
||||
// First handler being ran.
|
||||
Assert.That(args.TestNumber, Is.EqualTo(5));
|
||||
args.TestNumber = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed partial class DummyTwoComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
private struct TestStructEvent
|
||||
{
|
||||
public int TestNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
public partial class EntityEventBusTests
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,503 +0,0 @@
|
||||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture, Parallelizable, TestOf(typeof(EntityEventBus))]
|
||||
public partial class EntityEventBusTests
|
||||
{
|
||||
private static EntityEventBus BusFactory()
|
||||
{
|
||||
var compFacMock = new Mock<IComponentFactory>();
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
var reflectMock = new Mock<IReflectionManager>();
|
||||
entManMock.SetupGet(e => e.ComponentFactory).Returns(compFacMock.Object);
|
||||
var bus = new EntityEventBus(entManMock.Object, reflectMock.Object);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to subscribe a null handler causes a <see cref="ArgumentNullException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SubscribeEvent_NullHandler_NullArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
// Act
|
||||
void Code() => bus.SubscribeEvent(EventSource.Local, subscriber, (EntityEventHandler<TestEventArgs>) null!);
|
||||
|
||||
//Assert
|
||||
Assert.Throws<ArgumentNullException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to subscribe with a null subscriber causes a <see cref="ArgumentNullException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SubscribeEvent_NullSubscriber_NullArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
|
||||
// Act
|
||||
void Code() => bus.SubscribeEvent<TestEventArgs>(EventSource.Local, null!, ev => {});
|
||||
|
||||
//Assert: this should do nothing
|
||||
Assert.Throws<ArgumentNullException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate Event subscriptions are not allowed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SubscribeEvent_DuplicateSubscription_Invalid()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delegateCallCount = 0;
|
||||
void Handler(TestEventArgs ev) => delegateCallCount++;
|
||||
|
||||
// 2 subscriptions 1 handler
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler));
|
||||
}
|
||||
|
||||
// TODO if ever duplicate events are allowed, re-enable these tests.
|
||||
/*
|
||||
/// <summary>
|
||||
/// Unlike C# events, the set of event handler delegates is unique.
|
||||
/// Subscribing the same delegate multiple times will only call the handler once.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SubscribeEvent_DuplicateSubscription_RaisedOnce()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delegateCallCount = 0;
|
||||
void Handler(TestEventArgs ev) => delegateCallCount++;
|
||||
|
||||
// 2 subscriptions 1 handler
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
|
||||
// Act
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
//Assert
|
||||
Assert.That(delegateCallCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribing two different delegates to a single event type causes both events
|
||||
/// to be raised in an indeterminate order.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SubscribeEvent_MultipleDelegates_BothRaised()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delFooCount = 0;
|
||||
int delBarCount = 0;
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, ev => delFooCount++);
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, ev => delBarCount++);
|
||||
|
||||
// Act
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.That(delFooCount, Is.EqualTo(1));
|
||||
Assert.That(delBarCount, Is.EqualTo(1));
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// A subscriber's handlers are properly called only when the specified event type is raised.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SubscribeEvent_MultipleSubscriptions_IndividuallyCalled()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delFooCount = 0;
|
||||
int delBarCount = 0;
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, ev => delFooCount++);
|
||||
bus.SubscribeEvent<TestEventTwoArgs>(EventSource.Local, subscriber, ev => delBarCount++);
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// Act & Assert
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
|
||||
Assert.That(delFooCount, Is.EqualTo(1));
|
||||
Assert.That(delBarCount, Is.EqualTo(0));
|
||||
|
||||
delFooCount = delBarCount = 0;
|
||||
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventTwoArgs());
|
||||
Assert.That(delFooCount, Is.EqualTo(0));
|
||||
Assert.That(delBarCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to subscribe with <see cref="EventSource.None"/> makes no sense and causes
|
||||
/// a <see cref="ArgumentOutOfRangeException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SubscribeEvent_SourceNone_ArgOutOfRange()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
void TestEventHandler(TestEventArgs args) { }
|
||||
|
||||
// Act
|
||||
void Code() => bus.SubscribeEvent(EventSource.None, subscriber, (EntityEventHandler<TestEventArgs>)TestEventHandler);
|
||||
|
||||
//Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribing a handler twice does nothing.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnsubscribeEvent_DoubleUnsubscribe_Nop()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
void Handler(TestEventArgs ev) { }
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// Act
|
||||
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
|
||||
|
||||
// Assert: Does not throw
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribing a handler that was never subscribed in the first place does nothing.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnsubscribeEvent_NoSubscription_Nop()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
// Act
|
||||
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
|
||||
|
||||
// Assert: Does not throw
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to unsubscribe with a null subscriber causes a <see cref="ArgumentNullException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnsubscribeEvent_NullSubscriber_NullArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
|
||||
// Act
|
||||
void Code() => bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, null!);
|
||||
|
||||
// Assert
|
||||
Assert.Throws<ArgumentNullException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event cannot be subscribed to with <see cref="EventSource.None"/>, so trying to unsubscribe
|
||||
/// with an <see cref="EventSource.None"/> causes a <see cref="ArgumentOutOfRangeException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnsubscribeEvent_SourceNone_ArgOutOfRange()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
// Act
|
||||
void Code() => bus.UnsubscribeEvent<TestEventArgs>(EventSource.None, subscriber);
|
||||
|
||||
// Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raising an event with no handlers subscribed to it does nothing.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RaiseEvent_NoSubscriptions_Nop()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delCalledCount = 0;
|
||||
bus.SubscribeEvent<TestEventTwoArgs>(EventSource.Local, subscriber, ev => delCalledCount++);
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// Act
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.That(delCalledCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raising an event when a handler has been unsubscribed no longer calls the handler.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RaiseEvent_Unsubscribed_Nop()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delCallCount = 0;
|
||||
void Handler(TestEventArgs ev) => delCallCount++;
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// Act
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.That(delCallCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to raise an event with <see cref="EventSource.None"/> makes no sense and causes
|
||||
/// a <see cref="ArgumentOutOfRangeException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RaiseEvent_SourceNone_ArgOutOfRange()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
|
||||
// Act
|
||||
void Code() => bus.RaiseEvent(EventSource.None, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to unsubscribe all of a null subscriber's events causes a <see cref="ArgumentNullException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnsubscribeEvents_NullSubscriber_NullArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
|
||||
// Act
|
||||
void Code() => bus.UnsubscribeEvents(null!);
|
||||
|
||||
// Assert
|
||||
Assert.Throws<ArgumentNullException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribing a subscriber with no subscriptions does nothing.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnsubscribeEvents_NoSubscriptions_Nop()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
// Act
|
||||
bus.UnsubscribeEvents(subscriber);
|
||||
|
||||
// Assert: no exception
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The subscriber's handlers are not raised after they are unsubscribed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnsubscribeEvents_UnsubscribedHandler_Nop()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delCallCount = 0;
|
||||
void Handler(TestEventArgs ev) => delCallCount++;
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
bus.UnsubscribeEvents(subscriber);
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// Act
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.That(delCallCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to queue a null event causes a <see cref="ArgumentNullException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void QueueEvent_NullEvent_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
|
||||
// Act
|
||||
void Code() => bus.QueueEvent(EventSource.Local, null!);
|
||||
|
||||
// Assert
|
||||
Assert.Throws<ArgumentNullException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queuing an event does not immediately raise the event unless the queue is processed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void QueueEvent_EventQueued_DoesNotImmediatelyRaise()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delCallCount = 0;
|
||||
void Handler(TestEventArgs ev) => delCallCount++;
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
|
||||
// Act
|
||||
bus.QueueEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.That(delCallCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trying to queue an event with <see cref="EventSource.None"/> makes no sense and causes
|
||||
/// a <see cref="ArgumentOutOfRangeException"/> to be thrown.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void QueueEvent_SourceNone_ArgOutOfRange()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
|
||||
// Act
|
||||
void Code() => bus.QueueEvent(EventSource.None, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>(Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queued events are raised when the queue is processed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ProcessQueue_EventQueued_HandlerRaised()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
var subscriber = new TestEventSubscriber();
|
||||
|
||||
int delCallCount = 0;
|
||||
void Handler(TestEventArgs ev) => delCallCount++;
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
|
||||
bus.LockSubscriptions();
|
||||
bus.QueueEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
// Act
|
||||
bus.ProcessEventQueue();
|
||||
|
||||
// Assert
|
||||
Assert.That(delCallCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RaiseEvent_Ordered()
|
||||
{
|
||||
// Arrange
|
||||
var bus = BusFactory();
|
||||
|
||||
// Expected order is A -> C -> B
|
||||
var a = false;
|
||||
var b = false;
|
||||
var c = false;
|
||||
|
||||
void HandlerA(TestEventArgs ev)
|
||||
{
|
||||
Assert.That(b, Is.False, "A should run before B");
|
||||
Assert.That(c, Is.False, "A should run before C");
|
||||
|
||||
a = true;
|
||||
}
|
||||
|
||||
void HandlerB(TestEventArgs ev)
|
||||
{
|
||||
Assert.That(c, Is.True, "B should run after C");
|
||||
b = true;
|
||||
}
|
||||
|
||||
void HandlerC(TestEventArgs ev) => c = true;
|
||||
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, new SubA(), HandlerA, typeof(SubA), before: new []{typeof(SubB), typeof(SubC)});
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, new SubB(), HandlerB, typeof(SubB), after: new []{typeof(SubC)});
|
||||
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, new SubC(), HandlerC, typeof(SubC));
|
||||
bus.LockSubscriptions();
|
||||
|
||||
// Act
|
||||
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
|
||||
|
||||
// Assert
|
||||
Assert.That(a, Is.True, "A did not fire");
|
||||
Assert.That(b, Is.True, "B did not fire");
|
||||
Assert.That(c, Is.True, "C did not fire");
|
||||
}
|
||||
|
||||
public sealed class SubA : IEntityEventSubscriber
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class SubB : IEntityEventSubscriber
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class SubC : IEntityEventSubscriber
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TestEventSubscriber : IEntityEventSubscriber { }
|
||||
|
||||
internal sealed class TestEventArgs : EntityEventArgs { }
|
||||
internal sealed class TestEventTwoArgs : EntityEventArgs { }
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class EntityManagerCopyTests
|
||||
{
|
||||
[Test]
|
||||
public void CopyComponentGeneric()
|
||||
{
|
||||
var instant = RobustServerSimulation.NewSimulation();
|
||||
instant.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<AComponent>();
|
||||
});
|
||||
|
||||
var sim = instant.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var comp = entManager.AddComponent<AComponent>(original);
|
||||
|
||||
Assert.That(comp.Value, Is.EqualTo(false));
|
||||
comp.Value = true;
|
||||
|
||||
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(!entManager.HasComponent<AComponent>(target));
|
||||
|
||||
var targetComp = entManager.CopyComponent(original, target, comp);
|
||||
|
||||
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
|
||||
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
|
||||
Assert.That(!ReferenceEquals(comp, targetComp));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyComponentNonGeneric()
|
||||
{
|
||||
var instant = RobustServerSimulation.NewSimulation();
|
||||
instant.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<AComponent>();
|
||||
});
|
||||
|
||||
var sim = instant.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var comp = entManager.AddComponent<AComponent>(original);
|
||||
|
||||
Assert.That(comp.Value, Is.EqualTo(false));
|
||||
comp.Value = true;
|
||||
|
||||
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(!entManager.HasComponent<AComponent>(target));
|
||||
|
||||
var targetComp = entManager.CopyComponent(original, target, (IComponent) comp);
|
||||
|
||||
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
|
||||
Assert.That(((AComponent) targetComp).Value, Is.EqualTo(comp.Value));
|
||||
Assert.That(!ReferenceEquals(comp, targetComp));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyComponentMultiple()
|
||||
{
|
||||
var instant = RobustServerSimulation.NewSimulation();
|
||||
instant.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<AComponent>();
|
||||
fac.RegisterClass<BComponent>();
|
||||
});
|
||||
|
||||
var sim = instant.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var comp = entManager.AddComponent<AComponent>(original);
|
||||
var comp2 = entManager.AddComponent<BComponent>(original);
|
||||
|
||||
Assert.That(comp.Value, Is.EqualTo(false));
|
||||
comp.Value = true;
|
||||
|
||||
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(!entManager.HasComponent<AComponent>(target));
|
||||
|
||||
entManager.CopyComponents(original, target, null, comp, comp2);
|
||||
var targetComp = entManager.GetComponent<AComponent>(target);
|
||||
var targetComp2 = entManager.GetComponent<BComponent>(target);
|
||||
|
||||
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
|
||||
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
|
||||
|
||||
Assert.That(entManager.GetComponent<BComponent>(target), Is.EqualTo(targetComp2));
|
||||
Assert.That(targetComp2.Value, Is.EqualTo(comp2.Value));
|
||||
|
||||
Assert.That(!ReferenceEquals(comp, targetComp));
|
||||
Assert.That(!ReferenceEquals(comp2, targetComp2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyComponentMultipleViaTry()
|
||||
{
|
||||
var instant = RobustServerSimulation.NewSimulation();
|
||||
instant.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<AComponent>();
|
||||
fac.RegisterClass<BComponent>();
|
||||
});
|
||||
|
||||
var sim = instant.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var comp = entManager.AddComponent<AComponent>(original);
|
||||
var comp2 = entManager.AddComponent<BComponent>(original);
|
||||
|
||||
Assert.That(comp.Value, Is.EqualTo(false));
|
||||
comp.Value = true;
|
||||
|
||||
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(!entManager.HasComponent<AComponent>(target));
|
||||
|
||||
entManager.TryCopyComponents(original, target, null, comp.GetType(), comp2.GetType());
|
||||
var targetComp = entManager.GetComponent<AComponent>(target);
|
||||
var targetComp2 = entManager.GetComponent<BComponent>(target);
|
||||
|
||||
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
|
||||
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
|
||||
|
||||
Assert.That(entManager.GetComponent<BComponent>(target), Is.EqualTo(targetComp2));
|
||||
Assert.That(targetComp2.Value, Is.EqualTo(comp2.Value));
|
||||
|
||||
Assert.That(!ReferenceEquals(comp, targetComp));
|
||||
Assert.That(!ReferenceEquals(comp2, targetComp2));
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
private sealed partial class AComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public bool Value = false;
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
private sealed partial class BComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public bool Value = false;
|
||||
}
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture, Parallelizable ,TestOf(typeof(EntityManager))]
|
||||
public sealed partial class EntityManager_Components_Tests
|
||||
{
|
||||
private const string DummyLoadId = "DummyLoad";
|
||||
private const string DummyLoad = $@"
|
||||
- type: entity
|
||||
id: {DummyLoadId}
|
||||
name: weh
|
||||
components:
|
||||
- type: Joint
|
||||
- type: Physics
|
||||
";
|
||||
|
||||
[Test]
|
||||
public void AddRegistryComponentTest()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterPrototypes(fac => fac.LoadString(DummyLoad))
|
||||
.InitializeInstance();
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var protoManager = sim.Resolve<IPrototypeManager>();
|
||||
|
||||
var map = sim.CreateMap().Uid;
|
||||
var coords = new EntityCoordinates(map, default);
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
Assert.That(!entMan.HasComponent<PhysicsComponent>(entity));
|
||||
var proto = protoManager.Index<EntityPrototype>(DummyLoadId);
|
||||
|
||||
entMan.AddComponents(entity, proto);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(entMan.HasComponent<JointComponent>(entity));
|
||||
Assert.That(entMan.HasComponent<PhysicsComponent>(entity));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveRegistryComponentTest()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterPrototypes(fac => fac.LoadString(DummyLoad))
|
||||
.InitializeInstance();
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var protoManager = sim.Resolve<IPrototypeManager>();
|
||||
|
||||
var map = sim.CreateMap().Uid;
|
||||
var coords = new EntityCoordinates(map, default);
|
||||
var entity = entMan.SpawnEntity(DummyLoadId, coords);
|
||||
var proto = protoManager.Index<EntityPrototype>(DummyLoadId);
|
||||
|
||||
entMan.RemoveComponents(entity, proto);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(!entMan.HasComponent<JointComponent>(entity));
|
||||
Assert.That(!entMan.HasComponent<PhysicsComponent>(entity));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = new DummyComponent();
|
||||
|
||||
// Act
|
||||
entMan.AddComponent(entity, component);
|
||||
|
||||
// Assert
|
||||
var result = entMan.GetComponent<DummyComponent>(entity);
|
||||
Assert.That(result, Is.EqualTo(component));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddComponentOverwriteTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = new DummyComponent();
|
||||
|
||||
// Act
|
||||
entMan.AddComponent(entity, component, true);
|
||||
|
||||
// Assert
|
||||
var result = entMan.GetComponent<DummyComponent>(entity);
|
||||
Assert.That(result, Is.EqualTo(component));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddComponent_ExistingDeleted()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var firstComp = new DummyComponent();
|
||||
entMan.AddComponent(entity, firstComp);
|
||||
entMan.RemoveComponent<DummyComponent>(entity);
|
||||
var secondComp = new DummyComponent();
|
||||
|
||||
// Act
|
||||
entMan.AddComponent(entity, secondComp);
|
||||
|
||||
// Assert
|
||||
var result = entMan.GetComponent<DummyComponent>(entity);
|
||||
Assert.That(result, Is.EqualTo(secondComp));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.HasComponent<DummyComponent>(entity);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasComponentNoGenericTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.HasComponent(entity, typeof(DummyComponent));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasNetComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
|
||||
var factory = sim.Resolve<IComponentFactory>();
|
||||
var netId = factory.GetRegistration<DummyComponent>().NetID!;
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.HasComponent(entity, netId.Value);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetNetComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
|
||||
var factory = sim.Resolve<IComponentFactory>();
|
||||
var netId = factory.GetRegistration<DummyComponent>().NetID!;
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.GetComponent(entity, netId.Value);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(component));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.TryGetComponent<DummyComponent>(entity, out var comp);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(comp, Is.EqualTo(component));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetNetComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
|
||||
var factory = sim.Resolve<IComponentFactory>();
|
||||
var netId = factory.GetRegistration<DummyComponent>().NetID!;
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.TryGetComponent(entity, netId.Value, out var comp);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(comp, Is.EqualTo(component));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
entMan.RemoveComponent<DummyComponent>(entity);
|
||||
entMan.CullRemovedComponents();
|
||||
|
||||
// Assert
|
||||
Assert.That(entMan.HasComponent(entity, component.GetType()), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnsureQueuedComponentDeletion()
|
||||
{
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
Assert.That(component.LifeStage, Is.LessThanOrEqualTo(ComponentLifeStage.Running));
|
||||
entMan.RemoveComponentDeferred(entity, component);
|
||||
Assert.That(component.LifeStage, Is.EqualTo(ComponentLifeStage.Stopped));
|
||||
|
||||
Assert.That(entMan.EnsureComponent<DummyComponent>(entity, out var comp2), Is.False);
|
||||
Assert.That(comp2.LifeStage, Is.LessThanOrEqualTo(ComponentLifeStage.Running));
|
||||
Assert.That(component.LifeStage, Is.EqualTo(ComponentLifeStage.Deleted));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveNetComponentTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
|
||||
var factory = sim.Resolve<IComponentFactory>();
|
||||
var netId = factory.GetRegistration<DummyComponent>().NetID!;
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
entMan.RemoveComponent(entity, netId.Value);
|
||||
entMan.CullRemovedComponents();
|
||||
|
||||
// Assert
|
||||
Assert.That(entMan.HasComponent(entity, component.GetType()), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetComponentsTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.GetComponents<DummyComponent>(entity);
|
||||
|
||||
// Assert
|
||||
var list = result.ToList();
|
||||
Assert.That(list.Count, Is.EqualTo(1));
|
||||
Assert.That(list[0], Is.EqualTo(component));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllComponentsTest()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.EntityQuery<DummyComponent>(true);
|
||||
|
||||
// Assert
|
||||
var list = result.ToList();
|
||||
Assert.That(list.Count, Is.EqualTo(1));
|
||||
Assert.That(list[0], Is.EqualTo(component));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllComponentInstances()
|
||||
{
|
||||
// Arrange
|
||||
var (sim, coords) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var fac = sim.Resolve<IComponentFactory>();
|
||||
var entity = entMan.SpawnEntity(null, coords);
|
||||
var component = entMan.AddComponent<DummyComponent>(entity);
|
||||
|
||||
// Act
|
||||
var result = entMan.GetComponents(entity);
|
||||
|
||||
// Assert
|
||||
var list = result.Where(c=>fac.GetComponentName(c.GetType()) == "Dummy").ToList();
|
||||
Assert.That(list.Count, Is.EqualTo(1));
|
||||
Assert.That(list[0], Is.EqualTo(component));
|
||||
}
|
||||
|
||||
private static (ISimulation, EntityCoordinates) SimulationFactory()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterComponents(factory => factory.RegisterClass<DummyComponent>())
|
||||
.InitializeInstance();
|
||||
|
||||
var map = sim.CreateMap().Uid;
|
||||
var coords = new EntityCoordinates(map, default);
|
||||
return (sim, coords);
|
||||
}
|
||||
|
||||
[NetworkedComponent()]
|
||||
private sealed partial class DummyComponent : Component, ICompType1, ICompType2
|
||||
{
|
||||
}
|
||||
|
||||
private interface ICompType1 { }
|
||||
|
||||
private interface ICompType2 { }
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.Configuration;
|
||||
using Robust.Server.Reflection;
|
||||
using Robust.Server.Serialization;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture, Serializable]
|
||||
sealed class EntityState_Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to measure the size of <see cref="object"/>s in bytes. This is not actually a test,
|
||||
/// but a useful benchmark tool, so i'm leaving it here.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ComponentChangedSerialized()
|
||||
{
|
||||
var container = new DependencyCollection();
|
||||
container.Register<ILogManager, LogManager>();
|
||||
container.Register<IConfigurationManager, ServerNetConfigurationManager>();
|
||||
container.Register<IConfigurationManagerInternal, ServerNetConfigurationManager>();
|
||||
container.Register<INetManager, NetManager>();
|
||||
container.Register<IHWId, DummyHWId>();
|
||||
container.Register<IReflectionManager, ServerReflectionManager>();
|
||||
container.Register<IRobustSerializer, ServerRobustSerializer>();
|
||||
container.Register<IRobustMappedStringSerializer, RobustMappedStringSerializer>();
|
||||
container.Register<IAuthManager, AuthManager>();
|
||||
container.Register<IGameTiming, GameTiming>();
|
||||
container.Register<ProfManager, ProfManager>();
|
||||
container.Register<HttpClientHolder>();
|
||||
container.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
|
||||
container.BuildGraph();
|
||||
|
||||
var cfg = container.Resolve<IConfigurationManagerInternal>();
|
||||
cfg.Initialize(true);
|
||||
cfg.LoadCVarsFromAssembly(typeof(IConfigurationManager).Assembly);
|
||||
|
||||
container.Resolve<IReflectionManager>().LoadAssemblies(AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"));
|
||||
|
||||
IoCManager.InitThread(container, replaceExisting: true);
|
||||
|
||||
cfg.LoadCVarsFromAssembly(typeof(IConfigurationManager).Assembly); // Robust.Shared
|
||||
|
||||
container.Resolve<INetManager>().Initialize(true);
|
||||
|
||||
var serializer = container.Resolve<IRobustSerializer>();
|
||||
serializer.Initialize();
|
||||
IoCManager.Resolve<IRobustMappedStringSerializer>().LockStrings();
|
||||
|
||||
byte[] array;
|
||||
using(var stream = new MemoryStream())
|
||||
{
|
||||
var payload = new EntityState(
|
||||
new NetEntity(64),
|
||||
new []
|
||||
{
|
||||
new ComponentChange(0, new MapGridComponentDeltaState(16, chunkData: null, default), default)
|
||||
}, default);
|
||||
|
||||
serializer.Serialize(stream, payload);
|
||||
array = stream.ToArray();
|
||||
}
|
||||
|
||||
IoCManager.Clear();
|
||||
|
||||
Assert.Pass($"Size in Bytes: {array.Length.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.Configuration;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(EntitySystemManager))]
|
||||
public sealed class EntitySystemManagerOrderTest
|
||||
{
|
||||
private sealed class Counter
|
||||
{
|
||||
public int X;
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private abstract class TestSystemBase : IEntitySystem
|
||||
{
|
||||
public Counter? Counter;
|
||||
public int LastUpdate;
|
||||
|
||||
public virtual IEnumerable<Type> UpdatesAfter => Enumerable.Empty<Type>();
|
||||
public virtual IEnumerable<Type> UpdatesBefore => Enumerable.Empty<Type>();
|
||||
public bool UpdatesOutsidePrediction => true;
|
||||
public void Initialize() { }
|
||||
public void Shutdown() { }
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
LastUpdate = Counter!.X++;
|
||||
}
|
||||
public void FrameUpdate(float frameTime) { }
|
||||
}
|
||||
|
||||
// Expected update order is is A -> D -> C -> B
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class TestSystemA : TestSystemBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class TestSystemB : TestSystemBase
|
||||
{
|
||||
public override IEnumerable<Type> UpdatesAfter => new[] {typeof(TestSystemA)};
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class TestSystemC : TestSystemBase
|
||||
{
|
||||
public override IEnumerable<Type> UpdatesBefore => new[] {typeof(TestSystemB)};
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class TestSystemD : TestSystemBase
|
||||
{
|
||||
public override IEnumerable<Type> UpdatesAfter => new[] {typeof(TestSystemA)};
|
||||
public override IEnumerable<Type> UpdatesBefore => new[] {typeof(TestSystemC)};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
var deps = new DependencyCollection();
|
||||
deps.Register<IRuntimeLog, RuntimeLog>();
|
||||
deps.Register<ILogManager, LogManager>();
|
||||
deps.Register<IGameTiming, GameTiming>();
|
||||
deps.RegisterInstance<INetManager>(new Mock<INetManager>().Object);
|
||||
deps.Register<IConfigurationManager, ServerNetConfigurationManager>();
|
||||
deps.Register<IServerNetConfigurationManager, ServerNetConfigurationManager>();
|
||||
deps.Register<ProfManager, ProfManager>();
|
||||
deps.Register<IDynamicTypeFactory, DynamicTypeFactory>();
|
||||
deps.Register<IDynamicTypeFactoryInternal, DynamicTypeFactory>();
|
||||
deps.RegisterInstance<IModLoader>(new Mock<IModLoader>().Object);
|
||||
deps.Register<IEntitySystemManager, EntitySystemManager>();
|
||||
deps.RegisterInstance<IEntityManager>(new Mock<IEntityManager>().Object);
|
||||
// WHEN WILL THE SUFFERING END
|
||||
deps.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
|
||||
|
||||
var reflectionMock = new Mock<IReflectionManager>();
|
||||
reflectionMock.Setup(p => p.GetAllChildren<IEntitySystem>(false))
|
||||
.Returns(new[]
|
||||
{
|
||||
typeof(TestSystemA),
|
||||
typeof(TestSystemB),
|
||||
typeof(TestSystemC),
|
||||
typeof(TestSystemD),
|
||||
});
|
||||
|
||||
deps.RegisterInstance<IReflectionManager>(reflectionMock.Object);
|
||||
|
||||
deps.BuildGraph();
|
||||
|
||||
IoCManager.InitThread(deps, true);
|
||||
|
||||
var systems = deps.Resolve<IEntitySystemManager>();
|
||||
systems.Initialize();
|
||||
|
||||
var counter = new Counter();
|
||||
|
||||
systems.GetEntitySystem<TestSystemA>().Counter = counter;
|
||||
systems.GetEntitySystem<TestSystemB>().Counter = counter;
|
||||
systems.GetEntitySystem<TestSystemC>().Counter = counter;
|
||||
systems.GetEntitySystem<TestSystemD>().Counter = counter;
|
||||
|
||||
systems.TickUpdate(1, noPredictions: false);
|
||||
|
||||
Assert.That(counter.X, Is.EqualTo(4));
|
||||
|
||||
Assert.That(systems.GetEntitySystem<TestSystemA>().LastUpdate, Is.EqualTo(0));
|
||||
Assert.That(systems.GetEntitySystem<TestSystemB>().LastUpdate, Is.EqualTo(3));
|
||||
Assert.That(systems.GetEntitySystem<TestSystemC>().LastUpdate, Is.EqualTo(2));
|
||||
Assert.That(systems.GetEntitySystem<TestSystemD>().LastUpdate, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
IoCManager.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.IoC.Exceptions;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture, TestOf(typeof(EntitySystemManager))]
|
||||
public sealed class EntitySystemManager_Tests: RobustUnitTest
|
||||
{
|
||||
|
||||
public abstract class ESystemBase : IEntitySystem
|
||||
{
|
||||
public virtual IEnumerable<Type> UpdatesAfter => Enumerable.Empty<Type>();
|
||||
public virtual IEnumerable<Type> UpdatesBefore => Enumerable.Empty<Type>();
|
||||
public bool UpdatesOutsidePrediction => true;
|
||||
public void Initialize() { }
|
||||
public void Shutdown() { }
|
||||
public void Update(float frameTime) { }
|
||||
public void FrameUpdate(float frameTime) { }
|
||||
}
|
||||
[Virtual]
|
||||
public class ESystemA : ESystemBase { }
|
||||
public sealed class ESystemC : ESystemA { }
|
||||
public abstract class ESystemBase2 : ESystemBase { }
|
||||
public sealed class ESystemB : ESystemBase2 { }
|
||||
|
||||
public sealed class ESystemDepA : ESystemBase
|
||||
{
|
||||
[Dependency] public readonly ESystemDepB ESystemDepB = default!;
|
||||
}
|
||||
|
||||
public sealed class ESystemDepB : ESystemBase
|
||||
{
|
||||
[Dependency] public readonly ESystemDepA ESystemDepA = default!;
|
||||
}
|
||||
|
||||
/*
|
||||
ESystemBase (Abstract)
|
||||
- ESystemA
|
||||
- ESystemC
|
||||
- EsystemBase2 (Abstract)
|
||||
- ESystemB
|
||||
|
||||
*/
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var syssy = IoCManager.Resolve<IEntitySystemManager>();
|
||||
syssy.Clear();
|
||||
syssy.LoadExtraSystemType<ESystemA>();
|
||||
syssy.LoadExtraSystemType<ESystemB>();
|
||||
syssy.LoadExtraSystemType<ESystemC>();
|
||||
syssy.LoadExtraSystemType<ESystemDepA>();
|
||||
syssy.LoadExtraSystemType<ESystemDepB>();
|
||||
syssy.Initialize(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetsByTypeOrSupertype()
|
||||
{
|
||||
var esm = IoCManager.Resolve<IEntitySystemManager>();
|
||||
|
||||
// getting type by the exact type should work fine
|
||||
Assert.That(esm.GetEntitySystem<ESystemB>(), Is.TypeOf<ESystemB>());
|
||||
|
||||
// getting type by an abstract supertype should work fine
|
||||
// because there are no other subtypes of that supertype it would conflict with
|
||||
// it should return the only concrete subtype
|
||||
Assert.That(esm.GetEntitySystem<ESystemBase2>(), Is.TypeOf<ESystemB>());
|
||||
|
||||
// getting ESystemA type by its exact type should work fine,
|
||||
// even though EsystemC is a subtype - it should return an instance of ESystemA
|
||||
var esysA = esm.GetEntitySystem<ESystemA>();
|
||||
Assert.That(esysA, Is.TypeOf<ESystemA>());
|
||||
Assert.That(esysA, Is.Not.TypeOf<ESystemC>());
|
||||
|
||||
var esysC = esm.GetEntitySystem<ESystemC>();
|
||||
Assert.That(esysC, Is.TypeOf<ESystemC>());
|
||||
|
||||
// this should not work - it's abstract and there are multiple
|
||||
// concrete subtypes
|
||||
Assert.Throws<UnregisteredTypeException>(() =>
|
||||
{
|
||||
esm.GetEntitySystem<ESystemBase>();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DependencyTest()
|
||||
{
|
||||
var esm = IoCManager.Resolve<IEntitySystemManager>();
|
||||
|
||||
var sysA = esm.GetEntitySystem<ESystemDepA>();
|
||||
var sysB = esm.GetEntitySystem<ESystemDepB>();
|
||||
|
||||
Assert.That(sysA.ESystemDepB, Is.EqualTo(sysB));
|
||||
Assert.That(sysB.ESystemDepA, Is.EqualTo(sysA));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects;
|
||||
|
||||
public sealed class GenericEntityPrint
|
||||
{
|
||||
//[Test]
|
||||
public void Print()
|
||||
{
|
||||
// Using the test framework for things it was not meant for is my passion
|
||||
//
|
||||
// Its pretty fucked that just occasionally running this manually is so much easier than setting up a porper
|
||||
// source generator.
|
||||
var i = 8;
|
||||
|
||||
IEnumerable<string> Generics(int n, bool nullable, bool forceIncludeNumber = false)
|
||||
{
|
||||
for (var j = 1; j <= n; j++)
|
||||
{
|
||||
var jStr = n == 1 && !forceIncludeNumber ? string.Empty : j.ToString();
|
||||
yield return $"T{jStr}{(nullable ? "?" : string.Empty)}";
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<string> PartiallyNullableGenerics(int n, int notNullCount)
|
||||
{
|
||||
bool nullable;
|
||||
for (var j = 1; j <= n; j++)
|
||||
{
|
||||
nullable = j > notNullCount;
|
||||
var jStr = n == 1 ? string.Empty : j.ToString();
|
||||
yield return $"T{jStr}{(nullable ? "?" : string.Empty)}";
|
||||
}
|
||||
}
|
||||
|
||||
var structs = new StringBuilder();
|
||||
var constraints = new StringBuilder();
|
||||
var fields = new StringBuilder();
|
||||
var parameters = new StringBuilder();
|
||||
var asserts = new StringBuilder();
|
||||
var assignments = new StringBuilder();
|
||||
var tupleParameters = new StringBuilder();
|
||||
var tupleAccess = new StringBuilder();
|
||||
var entityAccess = new StringBuilder();
|
||||
var selfAccess = new StringBuilder();
|
||||
var entityNumberedAccess = new StringBuilder();
|
||||
var defaults = new StringBuilder();
|
||||
var compOperators = new StringBuilder();
|
||||
var deConstructorParameters = new StringBuilder();
|
||||
var deConstructorAccess = new StringBuilder();
|
||||
var partialTupleCasts = new StringBuilder();
|
||||
var partialEntityCasts = new StringBuilder();
|
||||
var entitySubCast = new StringBuilder();
|
||||
var castRegion = new StringBuilder();
|
||||
|
||||
for (var j = 1; j <= i; j++)
|
||||
{
|
||||
constraints.Clear();
|
||||
fields.Clear();
|
||||
parameters.Clear();
|
||||
asserts.Clear();
|
||||
assignments.Clear();
|
||||
tupleParameters.Clear();
|
||||
tupleAccess.Clear();
|
||||
entityAccess.Clear();
|
||||
selfAccess.Clear();
|
||||
entityNumberedAccess.Clear();
|
||||
defaults.Clear();
|
||||
compOperators.Clear();
|
||||
deConstructorParameters.Clear();
|
||||
deConstructorAccess.Clear();
|
||||
partialTupleCasts.Clear();
|
||||
partialEntityCasts.Clear();
|
||||
entitySubCast.Clear();
|
||||
castRegion.Clear();
|
||||
|
||||
var generics = string.Join(", ", Generics(j, false));
|
||||
var nullableGenerics = string.Join(", ", Generics(j, true));
|
||||
|
||||
for (var k = 1; k <= j; k++)
|
||||
{
|
||||
var kStr = j == 1 ? string.Empty : k.ToString();
|
||||
fields.AppendLine($" public T{kStr} Comp{kStr};");
|
||||
constraints.Append($"where T{kStr} : IComponent? ");
|
||||
parameters.Append($", T{kStr} comp{kStr}");
|
||||
asserts.AppendLine($" DebugTools.AssertOwner(owner, comp{kStr});");
|
||||
assignments.AppendLine($" Comp{kStr} = comp{kStr};");
|
||||
tupleParameters.Append($", T{kStr} Comp{kStr}");
|
||||
tupleAccess.Append($", tuple.Comp{kStr}");
|
||||
var suffix = (j >= 2 && k == 1) ? string.Empty : kStr;
|
||||
var prefix = (j >= 2 && k == 2) ? "1" : string.Empty;
|
||||
entityAccess.Append($"{prefix}, ent.Comp{suffix}");
|
||||
selfAccess.Append($"{prefix}, Comp{suffix}");
|
||||
entityNumberedAccess.Append($", ent.Comp{kStr}");
|
||||
defaults.Append(", default");
|
||||
compOperators.AppendLine($$"""
|
||||
public static implicit operator T{{kStr}}(Entity<{{generics}}> ent)
|
||||
{
|
||||
return ent.Comp{{kStr}};
|
||||
}
|
||||
|
||||
""");
|
||||
deConstructorParameters.Append($", out T{kStr} comp{kStr}");
|
||||
deConstructorAccess.AppendLine($" comp{kStr} = Comp{kStr};");
|
||||
|
||||
if (k == j)
|
||||
continue;
|
||||
|
||||
// Cast a (EntityUid, T1) tuple to an Entity<T1, T2?>
|
||||
// We could also casts for going from a (Uid, T2) tuple to a Entity<T1?, T2> but once we get to 4 or
|
||||
// more components there are just too many combinations and I CBF writing the code to generate all those.
|
||||
var partiallyNullableGenerics = string.Join(", ", PartiallyNullableGenerics(j, k));
|
||||
var defaultArgs = string.Concat(Enumerable.Repeat(", default", j-k));
|
||||
partialTupleCasts.Append($$"""
|
||||
|
||||
public static implicit operator Entity<{{partiallyNullableGenerics}}>((EntityUid Owner{{tupleParameters}}) tuple)
|
||||
{
|
||||
return new Entity<{{partiallyNullableGenerics}}>(tuple.Owner{{tupleAccess}}{{defaultArgs}});
|
||||
}
|
||||
|
||||
""");
|
||||
|
||||
// Cast an Entity<T1> to an Entity<T1, T2?>
|
||||
// As with the tuple casts, we could in principle generate more here.
|
||||
var subGenerics = string.Join(", ", Generics(k, false, true));
|
||||
partialEntityCasts.Append($$"""
|
||||
|
||||
public static implicit operator Entity<{{partiallyNullableGenerics}}>(Entity<{{subGenerics}}> ent)
|
||||
{
|
||||
return new Entity<{{partiallyNullableGenerics}}>(ent.Owner{{entityAccess}}{{defaultArgs}});
|
||||
}
|
||||
|
||||
""");
|
||||
|
||||
// Cast an Entity<T1, T2> to an Entity<T1/2>
|
||||
entitySubCast.Append($$"""
|
||||
|
||||
public static implicit operator Entity<{{subGenerics}}>(Entity<{{generics}}> ent)
|
||||
{
|
||||
return new Entity<{{subGenerics}}>(ent.Owner{{entityNumberedAccess}});
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
if (j == 2)
|
||||
{
|
||||
castRegion.Append($$"""
|
||||
{{partialTupleCasts.ToString().TrimEnd()}}
|
||||
{{partialEntityCasts.ToString().TrimEnd()}}
|
||||
{{entitySubCast.ToString().TrimEnd()}}
|
||||
""");
|
||||
}
|
||||
else if (j > 2)
|
||||
{
|
||||
castRegion.Append($$"""
|
||||
|
||||
#region Partial Tuple Casts
|
||||
{{partialTupleCasts}}
|
||||
#endregion
|
||||
|
||||
#region Partial Entity Casts
|
||||
{{partialEntityCasts}}
|
||||
#endregion
|
||||
|
||||
#region Entity Sub casts
|
||||
{{entitySubCast}}
|
||||
#endregion
|
||||
""");
|
||||
}
|
||||
|
||||
structs.Append($$"""
|
||||
[NotYamlSerializable]
|
||||
public record struct Entity<{{generics}}> : IFluentEntityUid, IAsType<EntityUid>
|
||||
{{constraints.ToString().TrimEnd()}}
|
||||
{
|
||||
public EntityUid Owner;
|
||||
{{fields.ToString().TrimEnd()}}
|
||||
readonly EntityUid IFluentEntityUid.FluentOwner => Owner;
|
||||
|
||||
public Entity(EntityUid owner{{parameters}})
|
||||
{
|
||||
{{asserts}}
|
||||
Owner = owner;
|
||||
{{assignments.ToString().TrimEnd()}}
|
||||
}
|
||||
|
||||
public static implicit operator Entity<{{generics}}>((EntityUid Owner{{tupleParameters}}) tuple)
|
||||
{
|
||||
return new Entity<{{generics}}>(tuple.Owner{{tupleAccess}});
|
||||
}
|
||||
|
||||
public static implicit operator Entity<{{nullableGenerics}}>(EntityUid owner)
|
||||
{
|
||||
return new Entity<{{nullableGenerics}}>(owner{{defaults}});
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<{{generics}}> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
{{compOperators.ToString().TrimEnd()}}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner{{deConstructorParameters}})
|
||||
{
|
||||
owner = Owner;
|
||||
{{deConstructorAccess.ToString().TrimEnd()}}
|
||||
}
|
||||
{{castRegion}}
|
||||
|
||||
public override readonly int GetHashCode() => Owner.GetHashCode();
|
||||
public readonly Entity<{{nullableGenerics}}> AsNullable() => new(Owner{{selfAccess}});
|
||||
public readonly EntityUid AsType() => Owner;
|
||||
}
|
||||
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
Console.WriteLine(structs);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture, Parallelizable]
|
||||
sealed partial class EntityManagerTests
|
||||
{
|
||||
private static ISimulation SimulationFactory()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.InitializeInstance();
|
||||
|
||||
return sim;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity prototype can define field on the TransformComponent, just like any other component.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpawnEntity_PrototypeTransform_Works()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var map = sim.CreateMap().MapId;
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, map));
|
||||
Assert.That(newEnt, Is.Not.EqualTo(EntityUid.Invalid));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComponentCount_Works()
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(0));
|
||||
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(1));
|
||||
mapSystem.DeleteMap(mapId);
|
||||
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,521 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
{
|
||||
[TestFixture, Parallelizable]
|
||||
public sealed partial class AnchoredSystemTests
|
||||
{
|
||||
private const string Prototypes = @"
|
||||
- type: entity
|
||||
name: anchoredEnt
|
||||
id: anchoredEnt
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: true";
|
||||
|
||||
private static (ISimulation, Entity<MapGridComponent> grid, MapCoordinates, SharedTransformSystem xformSys, SharedMapSystem mapSys) SimulationFactory()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterEntitySystems(f => f.LoadExtraSystemType<MoveEventTestSystem>())
|
||||
.RegisterPrototypes(f=>
|
||||
{
|
||||
f.LoadString(Prototypes);
|
||||
})
|
||||
.InitializeInstance();
|
||||
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
|
||||
var testMapId = sim.CreateMap().MapId;
|
||||
var coords = new MapCoordinates(new Vector2(7, 7), testMapId);
|
||||
// Add grid 1, as the default grid to anchor things to.
|
||||
var grid = mapManager.CreateGridEntity(testMapId);
|
||||
|
||||
return (sim, grid, coords, sim.System<SharedTransformSystem>(), sim.System<SharedMapSystem>());
|
||||
}
|
||||
|
||||
// An entity is anchored to the tile it is over on the target grid.
|
||||
// An entity is anchored by setting the flag on the transform.
|
||||
// An anchored entity is defined as an entity with the TransformComponent.Anchored flag set.
|
||||
// The Anchored field is used for serialization of anchored state.
|
||||
|
||||
// TODO: The grid SnapGrid functions are internal, expose the query functions to content.
|
||||
// PhysicsComponent.BodyType is not able to be changed by content.
|
||||
|
||||
/// <summary>
|
||||
/// When an entity is anchored to a grid tile, it's world position is centered on the tile.
|
||||
/// Otherwise you can anchor an entity to a tile without the entity actually being on top of the tile.
|
||||
/// This movement will trigger a MoveEvent.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnAnchored_WorldPosition_TileCenter()
|
||||
{
|
||||
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
// can only be anchored to a tile
|
||||
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
|
||||
|
||||
// Act
|
||||
sim.System<MoveEventTestSystem>().ResetCounters();
|
||||
xformSys.AnchorEntity(ent1);
|
||||
Assert.That(xformSys.GetWorldPosition(ent1), Is.EqualTo(new Vector2(7.5f, 7.5f))); // centered on tile
|
||||
sim.System<MoveEventTestSystem>().AssertMoved(false);
|
||||
}
|
||||
|
||||
[ComponentProtoName("AnchorOnInit")]
|
||||
[Reflect(false)]
|
||||
private sealed partial class AnchorOnInitComponent : Component;
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class AnchorOnInitTestSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<AnchorOnInitComponent, ComponentInit>((e, _, _) => Transform(e).Anchored = true);
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
internal sealed class MoveEventTestSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_transform.OnGlobalMoveEvent += OnMove;
|
||||
SubscribeLocalEvent<EntParentChangedMessage>(OnReparent);
|
||||
}
|
||||
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_transform.OnGlobalMoveEvent -= OnMove;
|
||||
}
|
||||
|
||||
public bool FailOnMove;
|
||||
public int MoveCounter;
|
||||
public int ParentCounter;
|
||||
|
||||
private void OnMove(ref MoveEvent ev)
|
||||
{
|
||||
MoveCounter++;
|
||||
if (FailOnMove)
|
||||
Assert.Fail($"Move event was raised");
|
||||
}
|
||||
private void OnReparent(ref EntParentChangedMessage ev)
|
||||
{
|
||||
ParentCounter++;
|
||||
if (FailOnMove)
|
||||
Assert.Fail($"Move event was raised");
|
||||
}
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
ParentCounter = 0;
|
||||
MoveCounter = 0;
|
||||
}
|
||||
|
||||
public void AssertMoved(bool parentChanged = true)
|
||||
{
|
||||
if (parentChanged)
|
||||
Assert.That(ParentCounter, Is.EqualTo(1));
|
||||
Assert.That(MoveCounter, Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that if an entity gets added to lookups when anchored during init by some system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See space-wizards/RobustToolbox/issues/3444
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void OnInitAnchored_AddedToLookup()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterEntitySystems(f => f.LoadExtraSystemType<AnchorOnInitTestSystem>())
|
||||
.RegisterComponents(f => f.RegisterClass<AnchorOnInitComponent>())
|
||||
.InitializeInstance();
|
||||
|
||||
var mapSys = sim.System<SharedMapSystem>();
|
||||
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var mapMan = sim.Resolve<IMapManager>();
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
var grid = mapMan.CreateGridEntity(mapId);
|
||||
var coordinates = new MapCoordinates(new Vector2(7, 7), mapId);
|
||||
var pos = mapSys.TileIndicesFor(grid, coordinates);
|
||||
mapSys.SetTile(grid, pos, new Tile(1));
|
||||
|
||||
var ent1 = entMan.SpawnEntity(null, coordinates);
|
||||
Assert.That(sim.Transform(ent1).Anchored, Is.False);
|
||||
Assert.That(!mapSys.GetAnchoredEntities(grid, pos).Any());
|
||||
entMan.DeleteEntity(ent1);
|
||||
|
||||
var ent2 = entMan.CreateEntityUninitialized(null, coordinates);
|
||||
entMan.AddComponent<AnchorOnInitComponent>(ent2);
|
||||
entMan.InitializeAndStartEntity(ent2);
|
||||
Assert.That(sim.Transform(ent2).Anchored);
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, pos).Count(), Is.EqualTo(1));
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, pos).Contains(ent2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When an entity is anchored to a grid tile, it's parent is set to the grid.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnAnchored_Parent_SetToGrid()
|
||||
{
|
||||
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
// can only be anchored to a tile
|
||||
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
|
||||
|
||||
var traversal = sim.System<SharedGridTraversalSystem>();
|
||||
traversal.Enabled = false;
|
||||
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
|
||||
|
||||
// Act
|
||||
xformSys.AnchorEntity(ent1);
|
||||
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
|
||||
traversal.Enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entities cannot be anchored to empty tiles. Attempting this is a no-op, and silently fails.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnAnchored_EmptyTile_Nop()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, Tile.Empty);
|
||||
|
||||
// Act
|
||||
xformSys.AnchorEntity(ent1);
|
||||
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entities can be anchored to any non-empty grid tile. A physics component is not required on either
|
||||
/// the grid or the entity to anchor it.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnAnchored_NonEmptyTile_Anchors()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
|
||||
// Act
|
||||
sim.Transform(ent1).Anchored = true;
|
||||
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).First(), Is.EqualTo(ent1));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.Not.EqualTo(Tile.Empty));
|
||||
Assert.That(sim.HasComp<PhysicsComponent>(ent1), Is.False);
|
||||
var tempQualifier = grid.Owner;
|
||||
Assert.That(sim.HasComp<PhysicsComponent>(tempQualifier), Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Local position of an anchored entity cannot be changed (can still change world position via parent).
|
||||
/// Writing to the property is a no-op and is silently ignored.
|
||||
/// Because the position cannot be changed, MoveEvents are not raised when setting the property.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Anchored_SetPosition_Nop()
|
||||
{
|
||||
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
// coordinates are already tile centered to prevent snapping and MoveEvent
|
||||
coordinates = coordinates.Offset(new Vector2(0.5f, 0.5f));
|
||||
|
||||
// can only be anchored to a tile
|
||||
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
|
||||
sim.Transform(ent1).Anchored = true;
|
||||
sim.System<MoveEventTestSystem>().FailOnMove = true;
|
||||
|
||||
// Act
|
||||
sim.Transform(ent1).WorldPosition = new Vector2(99, 99);
|
||||
sim.Transform(ent1).LocalPosition = new Vector2(99, 99);
|
||||
|
||||
Assert.That(xformSys.GetMapCoordinates(ent1), Is.EqualTo(coordinates));
|
||||
sim.System<MoveEventTestSystem>().FailOnMove = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing the parent of the entity un-anchors it.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Anchored_ChangeParent_Unanchors()
|
||||
{
|
||||
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coordinates);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
xformSys.AnchorEntity(ent1);
|
||||
|
||||
// Act
|
||||
xformSys.SetParent(ent1, mapSys.GetMap(coordinates.MapId));
|
||||
|
||||
Assert.That(sim.Transform(ent1).Anchored, Is.False);
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setting the parent of an anchored entity to the same parent is a no-op (it will not be un-anchored).
|
||||
/// This is an specific case to the base functionality of TransformComponent, where in general setting the same
|
||||
/// parent is a no-op.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Anchored_SetParentSame_Nop()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
|
||||
var ent1 = entMan.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
sim.Transform(ent1).Anchored = true;
|
||||
|
||||
// Act
|
||||
xformSys.SetParent(ent1, grid.Owner);
|
||||
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).First(), Is.EqualTo(ent1));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.Not.EqualTo(Tile.Empty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a tile is changed to a space tile, all entities anchored to that tile are unanchored.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Anchored_TileToSpace_Unanchors()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
mapSys.SetTile(grid, new Vector2i(100, 100), new Tile(1)); // Prevents the grid from being deleted when the Act happens
|
||||
xformSys.AnchorEntity(ent1);
|
||||
|
||||
// Act
|
||||
mapSys.SetTile(grid, tileIndices, Tile.Empty);
|
||||
|
||||
Assert.That(sim.Transform(ent1).Anchored, Is.False);
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adding an anchored entity to a container un-anchors an entity. There should be no way to have an anchored entity
|
||||
/// inside a container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The only way you can do this without changing the parent is to make the parent grid a ContainerManager, then add the anchored entity to it.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Anchored_AddToContainer_Unanchors()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
xformSys.AnchorEntity(ent1);
|
||||
|
||||
// Act
|
||||
// We purposefully use the grid as container so parent stays the same, reparent will unanchor
|
||||
var containerSys = entMan.System<SharedContainerSystem>();
|
||||
var containerMan = entMan.AddComponent<ContainerManagerComponent>(grid);
|
||||
var container = containerSys.MakeContainer<Container>(grid, "TestContainer", containerMan);
|
||||
containerSys.Insert(ent1, container);
|
||||
|
||||
Assert.That(sim.Transform(ent1).Anchored, Is.False);
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1)));
|
||||
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adding a physics component should poll TransformComponent.Anchored for the correct body type.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Anchored_AddPhysComp_IsStaticBody()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
xformSys.AnchorEntity(ent1);
|
||||
|
||||
// Act
|
||||
// assumed default body is Dynamic
|
||||
var physComp = entMan.AddComponent<PhysicsComponent>(ent1);
|
||||
|
||||
Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Static));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When an entity is anchored, it's physics body type is set to <see cref="BodyType.Static"/>.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnAnchored_HasPhysicsComp_IsStaticBody()
|
||||
{
|
||||
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var physSystem = sim.System<SharedPhysicsSystem>();
|
||||
|
||||
// can only be anchored to a tile
|
||||
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
|
||||
|
||||
var ent1 = entMan.SpawnEntity(null, coordinates);
|
||||
var physComp = entMan.AddComponent<PhysicsComponent>(ent1);
|
||||
physSystem.SetBodyType(ent1, BodyType.Dynamic, body: physComp);
|
||||
|
||||
// Act
|
||||
xformSys.AnchorEntity(ent1);
|
||||
|
||||
Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Static));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When an entity is unanchored, it's physics body type is set to <see cref="BodyType.Dynamic"/>.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnUnanchored_HasPhysicsComp_IsDynamicBody()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
var physComp = entMan.AddComponent<PhysicsComponent>(ent1);
|
||||
sim.Transform(ent1).Anchored = true;
|
||||
|
||||
// Act
|
||||
xformSys.Unanchor(ent1);
|
||||
|
||||
Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Dynamic));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If an entity with an anchored prototype is spawned in an invalid location, the entity is unanchored.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpawnAnchored_EmptyTile_Unanchors()
|
||||
{
|
||||
var (sim, grid, coords, _, mapSys) = SimulationFactory();
|
||||
|
||||
// Act
|
||||
var ent1 = sim.SpawnEntity("anchoredEnt", coords);
|
||||
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty));
|
||||
Assert.That(sim.Transform(ent1).Anchored, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If an entity is inside a container, setting Anchored silently fails.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnAnchored_InContainer_Nop()
|
||||
{
|
||||
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
|
||||
var ent1 = sim.SpawnEntity(null, coords);
|
||||
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
|
||||
mapSys.SetTile(grid, tileIndices, new Tile(1));
|
||||
|
||||
var containerSys = entMan.System<SharedContainerSystem>();
|
||||
var containerMan = entMan.AddComponent<ContainerManagerComponent>(grid);
|
||||
var container = containerSys.MakeContainer<Container>(grid, "TestContainer", containerMan);
|
||||
containerSys.Insert(ent1, container);
|
||||
|
||||
// Act
|
||||
xformSys.AnchorEntity(ent1);
|
||||
|
||||
Assert.That(sim.Transform(ent1).Anchored, Is.False);
|
||||
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
|
||||
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1)));
|
||||
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unanchoring an unanchored entity is a no-op.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Unanchored_Unanchor_Nop()
|
||||
{
|
||||
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
|
||||
|
||||
// can only be anchored to a tile
|
||||
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
|
||||
|
||||
var traversal = sim.System<SharedGridTraversalSystem>();
|
||||
traversal.Enabled = false;
|
||||
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
|
||||
|
||||
// Act
|
||||
sim.System<MoveEventTestSystem>().FailOnMove = true;
|
||||
xformSys.Unanchor(ent1);
|
||||
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
|
||||
sim.System<MoveEventTestSystem>().FailOnMove = false;
|
||||
traversal.Enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unanchoring an entity should leave it parented to the grid it was anchored to.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Anchored_Unanchored_ParentUnchanged()
|
||||
{
|
||||
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
|
||||
// can only be anchored to a tile
|
||||
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
|
||||
var ent1 = entMan.SpawnEntity("anchoredEnt", mapSys.MapToGrid(grid, coordinates));
|
||||
|
||||
xformSys.Unanchor(ent1);
|
||||
|
||||
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
{
|
||||
[TestFixture, Parallelizable]
|
||||
sealed class TransformSystemTests
|
||||
{
|
||||
private static ISimulation SimulationFactory()
|
||||
{
|
||||
var sim = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterEntitySystems(f => f.LoadExtraSystemType<AnchoredSystemTests.MoveEventTestSystem>())
|
||||
.InitializeInstance();
|
||||
|
||||
return sim;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the local position of the transform changes, a MoveEvent is raised.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnMove_LocalPosChanged_RaiseMoveEvent()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var map = sim.CreateMap().MapId;
|
||||
var ent1 = entMan.SpawnEntity(null, new MapCoordinates(Vector2.Zero, map));
|
||||
|
||||
entMan.System<AnchoredSystemTests.MoveEventTestSystem>().ResetCounters();
|
||||
entMan.System<TransformSystem>().SetLocalPosition(ent1, Vector2.One);
|
||||
entMan.System<AnchoredSystemTests.MoveEventTestSystem>().AssertMoved(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the MoverCoordinates between parent and children is correct.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void MoverCoordinatesCorrect()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var xformSystem = sim.Resolve<IEntitySystemManager>().GetEntitySystem<SharedTransformSystem>();
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
|
||||
var parent = entManager.SpawnEntity(null, new MapCoordinates(Vector2.One, mapId));
|
||||
var parentXform = entManager.GetComponent<TransformComponent>(parent);
|
||||
Assert.That(parentXform.LocalPosition, Is.EqualTo(Vector2.One));
|
||||
|
||||
var child1 = entManager.SpawnEntity(null, new MapCoordinates(Vector2.One, mapId));
|
||||
var child2 = entManager.SpawnEntity(null, new MapCoordinates(new Vector2(10f, 10f), mapId));
|
||||
|
||||
var child1Xform = entManager.GetComponent<TransformComponent>(child1);
|
||||
var child2Xform = entManager.GetComponent<TransformComponent>(child2);
|
||||
xformSystem.SetParent(child1, child1Xform, parent, parentXform: parentXform);
|
||||
xformSystem.SetParent(child2, child2Xform, parent, parentXform: parentXform);
|
||||
|
||||
var mover1 = xformSystem.GetMoverCoordinates(child1, child1Xform);
|
||||
var mover2 = xformSystem.GetMoverCoordinates(child2, child2Xform);
|
||||
|
||||
Assert.That(mover1.Position, Is.EqualTo(Vector2.One));
|
||||
Assert.That(mover2.Position, Is.EqualTo(new Vector2(10f, 10f)));
|
||||
|
||||
var child3 = entManager.SpawnEntity(null, new MapCoordinates(Vector2.One, mapId));
|
||||
var child3Xform = entManager.GetComponent<TransformComponent>(child3);
|
||||
xformSystem.SetParent(child3, child3Xform, child2, parentXform: child2Xform);
|
||||
|
||||
Assert.That(xformSystem.GetMoverCoordinates(child3, child3Xform).Position, Is.EqualTo(Vector2.One));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that when a transformcomponent is detached to null all of its children update their mapids.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DetachMapRecursive()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var xformSystem = sim.Resolve<IEntitySystemManager>().GetEntitySystem<SharedTransformSystem>();
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
|
||||
var parent = entManager.SpawnEntity(null, new MapCoordinates(Vector2.One, mapId));
|
||||
var parentXform = entManager.GetComponent<TransformComponent>(parent);
|
||||
|
||||
var child = entManager.SpawnEntity(null, new EntityCoordinates(parent, Vector2.Zero));
|
||||
var childXform = entManager.GetComponent<TransformComponent>(child);
|
||||
|
||||
Assert.That(parentXform.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(childXform.MapID, Is.EqualTo(mapId));
|
||||
|
||||
xformSystem.DetachEntity(parent, parentXform);
|
||||
Assert.That(parentXform.MapID, Is.EqualTo(MapId.Nullspace));
|
||||
Assert.That(childXform.MapID, Is.EqualTo(MapId.Nullspace));
|
||||
}
|
||||
|
||||
private sealed class Subscriber : IEntityEventSubscriber { }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user