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(); } } }