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.Network.Transfer; using Robust.Server.Physics; using Robust.Server.Player; using Robust.Server.Physics.Components; using Robust.Server.Prototypes; using Robust.Server.Reflection; using Robust.Server.Replays; using Robust.Server.ServerStatus; 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.GameStates; 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.Network.Transfer; 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); ISimulationFactory AddRoot(AddRootDelegate factory); ISimulationFactory ChangeCVar(ChangeCVarDelegate 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(); } /// /// 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, IEntityManager entMan) where T : IComponent { return entMan.HasComponent(entity); } public static T Comp(this ISimulation simulation, EntityUid entity, IEntityManager entMan) where T : IComponent { return entMan.GetComponent(entity); } public static TransformComponent Transform(this ISimulation simulation, EntityUid entity, IEntityManager entMan) { return simulation.Comp(entity, entMan); } } 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 delegate void AddRootDelegate(IResourceManager resourceMan); public delegate void ChangeCVarDelegate(IConfigurationManager configMan); public sealed class RobustServerSimulation : ISimulation, ISimulationFactory { private DiContainerDelegate? _diFactory; private CompRegistrationDelegate? _regDelegate; private EntitySystemRegistrationDelegate? _systemDelegate; private PrototypeRegistrationDelegate? _protoDelegate; private AddRootDelegate? _addRootDelegate; private ChangeCVarDelegate? _changeCVarDelegate; 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); } 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 ISimulationFactory AddRoot(AddRootDelegate factory) { _addRootDelegate += factory; return this; } public ISimulationFactory ChangeCVar(ChangeCVarDelegate factory) { _changeCVarDelegate += 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(); 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"), }); realReflection.EnsureGetAllTypesCache(); var reflectionManager = new Mock(); reflectionManager .Setup(x => x.FindTypesWithAttribute()) .Returns(realReflection.FindTypesWithAttribute); reflectionManager .Setup(x => x.FindTypesWithAttribute()) .Returns(realReflection.FindTypesWithAttribute); reflectionManager .Setup(x => x.FindTypesWithAttribute()) .Returns(realReflection.FindTypesWithAttribute); reflectionManager .Setup(x => x.FindTypesWithAttribute()) .Returns(realReflection.FindTypesWithAttribute); reflectionManager .Setup(x => x.FindTypesWithAttribute()) .Returns(realReflection.FindTypesWithAttribute); reflectionManager .Setup(x => x.FindTypesWithAttribute()) .Returns(realReflection.FindTypesWithAttribute); reflectionManager .Setup(x => x.FindTypesWithAttribute()) .Returns(realReflection.FindTypesWithAttribute); reflectionManager .Setup(x => x.FindTypesWithAttributeSet()) .Returns(realReflection.FindTypesWithAttributeSet); reflectionManager .Setup(x => x.FindAllTypes()) .Returns(realReflection.FindAllTypes); reflectionManager .Setup(x => x.IsAttributeDefined(It.IsAny(), It.IsAny())) .Returns((Type type1, Type type2) => realReflection.IsAttributeDefined(type1, type2)); 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); // 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(); // Needed for grid fixture debugging. container.Register(); container.Register(); // I just wanted to load pvs system 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 _changeCVarDelegate?.Invoke(configMan); 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(); 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(); entitySystemMan.LoadExtraSystemType(); _systemDelegate?.Invoke(entitySystemMan); entityMan.Startup(); container.Resolve().Initialize(true); container.Resolve().Initialize(); var protoMan = container.Resolve(); protoMan.Initialize(); protoMan.RegisterKind(typeof(EntityPrototype), typeof(EntityCategoryPrototype)); var resourceMan = container.Resolve(); _addRootDelegate?.Invoke(resourceMan); _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(); } } }