using JetBrains.Annotations;
using Moq;
using Robust.Shared.Asynchronous;
using Robust.Shared.Configuration;
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.Physics;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Server
{
[PublicAPI]
internal interface ISimulationFactory
{
ISimulationFactory RegisterComponents(CompRegistrationDelegate factory);
ISimulationFactory RegisterDependencies(DiContainerDelegate factory);
ISimulationFactory RegisterEntitySystems(EntitySystemRegistrationDelegate factory);
ISimulationFactory RegisterPrototypes(PrototypeRegistrationDelegate factory);
ISimulation InitializeInstance();
}
[PublicAPI]
internal interface ISimulation
{
IDependencyCollection Collection { get; }
///
/// Resolves a dependency directly out of IoC collection.
///
T Resolve();
///
/// Adds a new map directly to the map manager.
///
EntityUid AddMap(int mapId);
EntityUid AddMap(MapId mapId);
IEntity SpawnEntity(string? protoId, EntityCoordinates coordinates);
IEntity SpawnEntity(string? protoId, MapCoordinates coordinates);
}
internal delegate void DiContainerDelegate(IDependencyCollection diContainer);
internal delegate void CompRegistrationDelegate(IComponentFactory factory);
internal delegate void EntitySystemRegistrationDelegate(IEntitySystemManager systemMan);
internal delegate void PrototypeRegistrationDelegate(IPrototypeManager protoMan);
internal 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()
{
return Collection.Resolve();
}
public EntityUid AddMap(int mapId)
{
var mapMan = Collection.Resolve();
mapMan.CreateMap(new MapId(mapId));
return mapMan.GetMapEntityId(new MapId(mapId));
}
public EntityUid AddMap(MapId mapId)
{
var mapMan = Collection.Resolve();
mapMan.CreateMap(mapId);
return mapMan.GetMapEntityId(mapId);
}
public IEntity SpawnEntity(string? protoId, EntityCoordinates coordinates)
{
var entMan = Collection.Resolve();
return entMan.SpawnEntity(protoId, coordinates);
}
public IEntity SpawnEntity(string? protoId, MapCoordinates coordinates)
{
var entMan = Collection.Resolve();
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();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.RegisterInstance(new Mock().Object);
var reflectionManager = new Mock();
reflectionManager
.Setup(x => x.FindTypesWithAttribute())
.Returns(() => new[]
{
typeof(DataDefinitionAttribute)
});
reflectionManager
.Setup(x => x.FindTypesWithAttribute(typeof(DataDefinitionAttribute)))
.Returns(() => new[]
{
typeof(EntityPrototype),
typeof(TransformComponent),
typeof(MetaDataComponent)
});
reflectionManager
.Setup(x => x.FindTypesWithAttribute())
.Returns(() => new[] {typeof(StringSerializer)});
container.RegisterInstance(reflectionManager.Object); // tests should not be searching for types
container.RegisterInstance(new Mock().Object);
container.RegisterInstance(new Mock().Object); // no disk access for tests
container.RegisterInstance(new Mock().Object); // TODO: get timing working similar to RobustIntegrationTest
//Tier 2: Simulation
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.RegisterInstance(new Mock().Object); // TODO: get timing working similar to RobustIntegrationTest
_diFactory?.Invoke(container);
container.BuildGraph();
var logMan = container.Resolve();
logMan.RootSawmill.AddHandler(new TestLogHandler("SIM"));
var compFactory = container.Resolve();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
_regDelegate?.Invoke(compFactory);
var entityMan = container.Resolve();
entityMan.Initialize();
IoCManager.Resolve().Initialize();
_systemDelegate?.Invoke(container.Resolve());
entityMan.Startup();
var mapManager = container.Resolve();
mapManager.Initialize();
mapManager.Startup();
container.Resolve().Initialize();
var protoMan = container.Resolve();
protoMan.RegisterType(typeof(EntityPrototype));
_protoDelegate?.Invoke(protoMan);
protoMan.Resync();
return this;
}
public static ISimulationFactory NewSimulation()
{
return new RobustServerSimulation();
}
}
}