using System.Reflection;
using JetBrains.Annotations;
using Moq;
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.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.Testing;
using Robust.Shared.Threading;
using Robust.Shared.Timing;
// ReSharper disable once CheckNamespace
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; }
///
/// Resolves a dependency directly out of IoC collection.
///
T Resolve();
///
/// Adds a new map directly to the map manager.
///
(EntityUid Uid, MapId MapId) CreateMap();
EntityUid SpawnEntity(string? protoId, EntityCoordinates coordinates);
EntityUid SpawnEntity(string? protoId, MapCoordinates coordinates);
}
///
/// Helper methods for working with .
///
internal static class SimulationExtensions
{
public static T System(this ISimulation simulation) where T : IEntitySystem
{
return simulation.Resolve().GetEntitySystem();
}
public static bool HasComp(this ISimulation simulation, EntityUid entity) where T : IComponent
{
return simulation.Resolve().HasComponent(entity);
}
public static T Comp(this ISimulation simulation, EntityUid entity) where T : IComponent
{
return simulation.Resolve().GetComponent(entity);
}
public static TransformComponent Transform(this ISimulation simulation, EntityUid entity)
{
return simulation.Comp(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()
{
return Collection.Resolve();
}
public (EntityUid Uid, MapId MapId) CreateMap()
{
var uid = Collection.Resolve().System().CreateMap(out var mapId);
return (uid, mapId);
}
public EntityUid SpawnEntity(string? protoId, EntityCoordinates coordinates)
{
var entMan = Collection.Resolve();
return entMan.SpawnEntity(protoId, coordinates);
}
public EntityUid 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.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.RegisterInstance(new Mock().Object);
container.Register();
container.Register();
container.Register();
var realReflection = new ServerReflectionManager();
realReflection.LoadAssemblies(new List(2)
{
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"),
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Server"),
});
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(() => realReflection.FindTypesWithAttribute());
reflectionManager
.Setup(x => x.FindAllTypes())
.Returns(() => realReflection.FindAllTypes());
container.RegisterInstance(new Mock().Object);
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.RegisterInstance(new Mock().Object); //Console is technically a frontend, we want to run headless
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
container.Register();
// Needed for grid fixture debugging.
container.Register();
container.Register();
// I just wanted to load pvs system
container.Register();
container.Register();
// god help you if you actually need to test pvs functions
container.RegisterInstance(new Mock().Object);
container.RegisterInstance(new Mock().Object);
container.RegisterInstance(new Mock().Object);
container.RegisterInstance(new Mock().Object);
container.RegisterInstance(new Mock().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();
configMan.Initialize(true);
configMan.LoadCVarsFromAssembly(typeof(Program).Assembly); // Server
configMan.LoadCVarsFromAssembly(typeof(ProgramShared).Assembly); // Shared
configMan.LoadCVarsFromAssembly(typeof(RobustServerSimulation).Assembly); // Tests
configMan.LoadCVarsFromAssembly(typeof(RTCVars).Assembly); // Tests
var logMan = container.Resolve();
logMan.RootSawmill.AddHandler(new TestLogHandler(configMan, "SIM"));
var compFactory = container.Resolve();
// if only we had some sort of attribute for automatically registering components.
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
compFactory.RegisterClass();
_regDelegate?.Invoke(compFactory);
compFactory.GenerateNetIds();
var entityMan = container.Resolve();
entityMan.Initialize();
var entitySystemMan = container.Resolve();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
entitySystemMan.LoadExtraSystemType();
_systemDelegate?.Invoke(entitySystemMan);
var mapManager = container.Resolve();
mapManager.Initialize();
entityMan.Startup();
mapManager.Startup();
container.Resolve().Initialize(true);
container.Resolve().Initialize();
var protoMan = container.Resolve();
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();
}
}
}