From 6b9e48f96ac015d5919e2625d7755024ca2bb52c Mon Sep 17 00:00:00 2001 From: MapleWheels Date: Tue, 10 Feb 2026 14:28:22 -0500 Subject: [PATCH] - Added ISystem (automatically run services) service type. --- .../SharedSource/LuaCs/LuaCsSetup.cs | 2 +- .../LuaCs/_Services/ServicesProvider.cs | 61 ++++++++++++++++--- .../LuaCs/_Services/_Interfaces/IService.cs | 6 ++ .../_Interfaces/IServicesProvider.cs | 36 +++++------ 4 files changed, 74 insertions(+), 31 deletions(-) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index 5ffb73d69..7aefff43f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -215,7 +215,7 @@ namespace Barotrauma servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); // gen IL - servicesProvider.Compile(); + servicesProvider.CompileAndRun(); return servicesProvider; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ServicesProvider.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ServicesProvider.cs index e9e06fe44..9452de187 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ServicesProvider.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ServicesProvider.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -12,7 +15,14 @@ public class ServicesProvider : IServicesProvider { private ServiceContainer _serviceContainerInst; private ServiceContainer ServiceContainer => _serviceContainerInst; - + /// + /// Definition: [Key: InterfaceType, Value: ConcreteTypes] + /// + private readonly ConcurrentDictionary> _systemTypeDefs = new(); + /// + /// Definition: [Key: ConcreteType, Value: TypeInstance] + /// + private readonly ConcurrentDictionary _systemInstances = new(); private readonly ReaderWriterLockSlim _serviceLock = new(); public ServicesProvider() @@ -27,6 +37,13 @@ public class ServicesProvider : IServicesProvider public void RegisterServiceType(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface { + // ISystem services must run as a lifetime singleton + if (typeof(TSvcInterface).IsAssignableTo(typeof(ISystem))) + { + lifetimeInstance = new PerContainerLifetime(); + _systemTypeDefs.GetOrAdd(typeof(TSvcInterface), (type) => new ConcurrentBag()); + } + if (lifetimeInstance is null) { switch (lifetime) @@ -54,7 +71,6 @@ public class ServicesProvider : IServicesProvider ServiceContainer.Register(lifetimeInstance); else ServiceContainer.Register(); - OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService)); } finally { @@ -70,6 +86,13 @@ public class ServicesProvider : IServicesProvider throw new ArgumentNullException($"Tried to register a service of type {typeof(TService).Name} but the name provided is null or empty." ); } + // ISystem services must run as a lifetime singleton + if (typeof(TSvcInterface).IsAssignableTo(typeof(ISystem))) + { + lifetimeInstance = new PerContainerLifetime(); + _systemTypeDefs.GetOrAdd(typeof(TSvcInterface), (type) => new ConcurrentBag()); + } + if (lifetimeInstance is null) { switch (lifetime) @@ -94,7 +117,6 @@ public class ServicesProvider : IServicesProvider { _serviceLock.EnterReadLock(); ServiceContainer.Register(name, lifetimeInstance); - OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService)); } finally { @@ -115,20 +137,26 @@ public class ServicesProvider : IServicesProvider } } - public void Compile() + public void CompileAndRun() { try { - _serviceLock.EnterReadLock(); + _serviceLock.EnterWriteLock(); ServiceContainer?.Compile(); + foreach (var typeDef in _systemTypeDefs.Values.SelectMany(type => type)) + { + if (_systemInstances.ContainsKey(typeDef)) + { + continue; + } + _systemInstances[typeDef] = (ISystem)ServiceContainer?.TryGetInstance(typeDef); + } } finally { - _serviceLock.ExitReadLock(); + _serviceLock.ExitWriteLock(); } } - - public event Action OnServiceRegistered; public void InjectServices(T inst) where T : class { @@ -209,19 +237,32 @@ public class ServicesProvider : IServicesProvider } } - [MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.Synchronized)] public void DisposeAndReset() { // Plugins should never be allowed to execute this. if (Assembly.GetCallingAssembly() != Assembly.GetExecutingAssembly()) { throw new MethodAccessException( - $"Assembly {Assembly.GetCallingAssembly().FullName} attempted to call DisposeAllServices()."); + $"Assembly {Assembly.GetCallingAssembly().FullName} attempted to call {nameof(DisposeAndReset)}()."); } try { _serviceLock.EnterWriteLock(); + foreach (var system in _systemInstances.Values) + { + try + { + system.Dispose(); + } + catch (Exception e) + { + // ignored, no logging services available. + } + } + _systemInstances.Clear(); + _systemTypeDefs.Clear(); _serviceContainerInst?.Dispose(); _serviceContainerInst = new ServiceContainer(); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IService.cs index 27abce8e2..78fd7c8ba 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IService.cs @@ -3,6 +3,12 @@ using Microsoft.Toolkit.Diagnostics; namespace Barotrauma.LuaCs; +/// +/// Represents a that is automatically instantiated at startup for the lifetime of the +/// instance. +/// +public interface ISystem : IReusableService { } + /// /// Defines a service that can be reset to it's post-constructor state and reused without needing to be disposed. /// Intended for persistent services. diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IServicesProvider.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IServicesProvider.cs index 847540737..29ad12045 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IServicesProvider.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IServicesProvider.cs @@ -6,7 +6,8 @@ using LightInject; namespace Barotrauma.LuaCs; /// -/// Provides instancing and management of IServices. +/// Provides instancing and management of , , and +/// instances. /// public interface IServicesProvider { @@ -15,28 +16,23 @@ public interface IServicesProvider /// /// Registers a type as a service for a given interface. /// - /// - /// - /// - /// + /// NOTE: services are forced to + /// The of the service when requested. + /// Custom lifetime instance. + /// Service interface. + /// Implementing service type. void RegisterServiceType(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface; /// /// Registers a type as a service for a given interface that can be requested by name. /// - /// - /// - /// - /// - /// + /// NOTE: services are forced to + /// Name of the service for lookup. + /// The of the service when requested. + /// Custom lifetime instance. + /// Service interface. + /// Implementing service type. void RegisterServiceType(string name, ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface; - - /// - /// Called whenever a new service type for a given interface is implemented. - /// Args[0]: Interface type - /// Args[1]: Implementing type - /// - event System.Action OnServiceRegistered; /// /// Registers a factory for resolving the service type. @@ -46,14 +42,14 @@ public interface IServicesProvider void RegisterServiceResolver(Func factory) where TSvcInterface : class, IService; /// - /// Runs compilation of registered services. + /// Compiles/Generates IL for registered services and instantiates all registered types. /// - public void Compile(); + public void CompileAndRun(); #endregion #region Services_Instancing_Injection - + /// /// Injects services into the properties of already instanced objects. ///