Files
RobustToolbox/Robust.UnitTesting/RobustUnitTest.cs
Pieter-Jan Briers 2a9de462d5 Preserve tile maps when saving maps & related changes (#5003)
* Un-hardcode behavior to make a component not saved to map file.

MapSaveId is a special component that can't be saved to map files due to a hardcoded type check. This behavior can now be applied to any component with [UnsavedComponent].

Moved "component registration" attributes into a single file because they don't deserve their own (poorly organized) .cs files.

* Add ITileDefinitionManager.TryGetDefinition

Try-pattern version of the existing indexers.

* Preserve tile maps when saving maps

This changes the map saver and loader code so that the "tilemap" can be preserved between map modifications as much as possible.

The tile map from the loaded map gets stored onto MapSaveTileMapComponent components on all loaded grids. This tile map is then used when saving, meaning that changes to the engine's internal tile IDs do not cause diffs.

Fixes #5000

* Changelog

* Fix tests
2024-03-27 14:14:19 +11:00

216 lines
8.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using NUnit.Framework;
using Robust.Client.ComponentTrees;
using Robust.Client.GameObjects;
using Robust.Server.Debugging;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Server.Physics;
using Robust.Shared.Configuration;
using Robust.Shared.Containers;
using Robust.Shared.ContentPack;
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.Threading;
using Robust.Shared.Utility;
using InputSystem = Robust.Server.GameObjects.InputSystem;
using MapSystem = Robust.Server.GameObjects.MapSystem;
namespace Robust.UnitTesting
{
public enum UnitTestProject : byte
{
Server,
Client
}
[Parallelizable]
public abstract partial class RobustUnitTest
{
protected virtual Type[]? ExtraComponents => null;
private static Type[] _components = new []
{
typeof(EyeComponent),
typeof(MapComponent),
typeof(MapGridComponent),
typeof(ContainerManagerComponent),
typeof(MetaDataComponent),
typeof(TransformComponent),
typeof(PhysicsComponent),
typeof(PhysicsMapComponent),
typeof(BroadphaseComponent),
typeof(FixturesComponent),
typeof(JointComponent),
typeof(GridTreeComponent),
typeof(MovedGridsComponent),
typeof(JointRelayTargetComponent),
typeof(OccluderComponent),
typeof(OccluderTreeComponent),
typeof(SpriteTreeComponent),
typeof(LightTreeComponent),
typeof(CollisionWakeComponent),
typeof(CollideOnAnchorComponent),
typeof(Gravity2DComponent),
typeof(ActorComponent)
};
public virtual UnitTestProject Project => UnitTestProject.Server;
[OneTimeSetUp]
public void BaseSetup()
{
// Clear state across tests.
var deps = IoCManager.InitThread();
deps.Clear();
RegisterIoC();
var assemblies = new List<Assembly>(4);
switch (Project)
{
case UnitTestProject.Client:
assemblies.Add(AppDomain.CurrentDomain.GetAssemblyByName("Robust.Client"));
break;
case UnitTestProject.Server:
assemblies.Add(AppDomain.CurrentDomain.GetAssemblyByName("Robust.Server"));
break;
default:
throw new NotSupportedException($"Unknown testing project: {Project}");
}
assemblies.Add(AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"));
assemblies.Add(Assembly.GetExecutingAssembly());
var configurationManager = deps.Resolve<IConfigurationManagerInternal>();
configurationManager.Initialize(Project == UnitTestProject.Server);
deps.Resolve<IReflectionManager>().Initialize();
foreach (var assembly in assemblies)
{
configurationManager.LoadCVarsFromAssembly(assembly);
}
var contentAssemblies = GetContentAssemblies();
foreach (var assembly in contentAssemblies)
{
configurationManager.LoadCVarsFromAssembly(assembly);
}
configurationManager.LoadCVarsFromAssembly(typeof(RobustUnitTest).Assembly);
var systems = deps.Resolve<IEntitySystemManager>();
// Required systems
systems.LoadExtraSystemType<EntityLookupSystem>();
// uhhh so maybe these are the wrong system for the client, but I CBF adding sprite system and all the rest,
// and it was like this when I found it.
systems.LoadExtraSystemType<SharedGridTraversalSystem>();
systems.LoadExtraSystemType<FixtureSystem>();
systems.LoadExtraSystemType<Gravity2DController>();
systems.LoadExtraSystemType<CollisionWakeSystem>();
if (Project == UnitTestProject.Client)
{
systems.LoadExtraSystemType<ClientMetaDataSystem>();
systems.LoadExtraSystemType<Robust.Server.Containers.ContainerSystem>();
systems.LoadExtraSystemType<Robust.Server.GameObjects.TransformSystem>();
systems.LoadExtraSystemType<Robust.Client.Physics.BroadPhaseSystem>();
systems.LoadExtraSystemType<Robust.Client.Physics.JointSystem>();
systems.LoadExtraSystemType<Robust.Client.Physics.PhysicsSystem>();
systems.LoadExtraSystemType<Robust.Client.Debugging.DebugRayDrawingSystem>();
systems.LoadExtraSystemType<PrototypeReloadSystem>();
systems.LoadExtraSystemType<Robust.Client.Debugging.DebugPhysicsSystem>();
systems.LoadExtraSystemType<Robust.Client.GameObjects.MapSystem>();
}
else
{
systems.LoadExtraSystemType<ServerMetaDataSystem>();
systems.LoadExtraSystemType<PvsSystem>();
systems.LoadExtraSystemType<Robust.Server.Containers.ContainerSystem>();
systems.LoadExtraSystemType<Robust.Server.GameObjects.TransformSystem>();
systems.LoadExtraSystemType<BroadPhaseSystem>();
systems.LoadExtraSystemType<JointSystem>();
systems.LoadExtraSystemType<PhysicsSystem>();
systems.LoadExtraSystemType<DebugRayDrawingSystem>();
systems.LoadExtraSystemType<PrototypeReloadSystem>();
systems.LoadExtraSystemType<DebugPhysicsSystem>();
systems.LoadExtraSystemType<MapLoaderSystem>();
systems.LoadExtraSystemType<InputSystem>();
systems.LoadExtraSystemType<PvsOverrideSystem>();
systems.LoadExtraSystemType<MapSystem>();
}
var entMan = deps.Resolve<IEntityManager>();
var mapMan = deps.Resolve<IMapManager>();
// Required components for the engine to work
// Why are we still here? Just to suffer? Why can't we just use [RegisterComponent] magic?
// TODO End Suffering.
// suffering has been alleviated, but still present
var compFactory = deps.Resolve<IComponentFactory>();
compFactory.RegisterTypes(_components);
if (ExtraComponents != null)
compFactory.RegisterTypes(ExtraComponents);
if (Project == UnitTestProject.Server)
{
compFactory.RegisterClass<MapSaveTileMapComponent>();
compFactory.RegisterClass<MapSaveIdComponent>();
}
deps.Resolve<IParallelManagerInternal>().Initialize();
// So by default EntityManager does its own EntitySystemManager initialize during Startup.
// We want to bypass this and load our own systems hence we will manually initialize it here.
entMan.Initialize();
// RobustUnitTest is complete hot garbage.
// This makes EventTables ignore *all* the screwed up component abuse it causes.
entMan.EventBus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); // The nightmare never ends
mapMan.Initialize();
systems.Initialize();
deps.Resolve<IReflectionManager>().LoadAssemblies(assemblies);
var modLoader = deps.Resolve<TestingModLoader>();
modLoader.Assemblies = contentAssemblies;
modLoader.TryLoadModulesFrom(ResPath.Root, "");
entMan.Startup();
mapMan.Startup();
}
[OneTimeTearDown]
public void BaseTearDown()
{
IoCManager.Clear();
}
/// <summary>
/// Called after all IoC registration has been done, but before the graph has been built.
/// This allows one to add new IoC types or overwrite existing ones if needed.
/// </summary>
protected virtual void OverrideIoC()
{
}
protected virtual Assembly[] GetContentAssemblies()
{
return Array.Empty<Assembly>();
}
}
}