first commit
This commit is contained in:
12
NitroxModel/Core/IAutoFacRegistrar.cs
Normal file
12
NitroxModel/Core/IAutoFacRegistrar.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Autofac;
|
||||
|
||||
namespace NitroxModel.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Nitrox projects should inherit from this interface and register their services into the DI container using the builder method.
|
||||
/// </summary>
|
||||
public interface IAutoFacRegistrar
|
||||
{
|
||||
void RegisterDependencies(ContainerBuilder containerBuilder);
|
||||
}
|
||||
}
|
60
NitroxModel/Core/NitroxEnvironment.cs
Normal file
60
NitroxModel/Core/NitroxEnvironment.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NitroxModel.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// Environment helper for getting meta data about where and how Nitrox is running.
|
||||
/// </summary>
|
||||
public static class NitroxEnvironment
|
||||
{
|
||||
public static string ReleasePhase => IsReleaseMode ? "Alpha" : "InDev";
|
||||
public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
|
||||
public static DateTime BuildDate => File.GetCreationTimeUtc(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
public static Types Type { get; private set; } = Types.NORMAL;
|
||||
public static bool IsTesting => Type == Types.TESTING;
|
||||
public static bool IsNormal => Type == Types.NORMAL;
|
||||
|
||||
public static int CurrentProcessId
|
||||
{
|
||||
get
|
||||
{
|
||||
using Process process = Process.GetCurrentProcess();
|
||||
return process.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsReleaseMode
|
||||
{
|
||||
get
|
||||
{
|
||||
#if RELEASE
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private static bool hasSet;
|
||||
public static void Set(Types value)
|
||||
{
|
||||
if (hasSet)
|
||||
{
|
||||
throw new Exception("Environment type can only be set once");
|
||||
}
|
||||
|
||||
Type = value;
|
||||
hasSet = true;
|
||||
}
|
||||
|
||||
public enum Types
|
||||
{
|
||||
NORMAL,
|
||||
TESTING
|
||||
}
|
||||
}
|
||||
}
|
159
NitroxModel/Core/NitroxServiceLocator.cs
Normal file
159
NitroxModel/Core/NitroxServiceLocator.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using Autofac;
|
||||
using Autofac.Builder;
|
||||
using NitroxModel.DataStructures.Util;
|
||||
|
||||
namespace NitroxModel.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Dependency Injection (DI) class for resolving types as defined in the DI registrar, implementing <see cref="IAutoFacRegistrar" />.
|
||||
/// </summary>
|
||||
public static class NitroxServiceLocator
|
||||
{
|
||||
private static IContainer DependencyContainer { get; set; }
|
||||
private static ILifetimeScope CurrentLifetimeScope { get; set; }
|
||||
public static event EventHandler LifetimeScopeEnded;
|
||||
|
||||
public static void InitializeDependencyContainer(params IAutoFacRegistrar[] registrars)
|
||||
{
|
||||
ContainerBuilder builder = new();
|
||||
foreach (IAutoFacRegistrar registrar in registrars)
|
||||
{
|
||||
registrar.RegisterDependencies(builder);
|
||||
}
|
||||
|
||||
// IgnoreStartableComponents - Prevents "phantom" executions of the Start() method
|
||||
// on a MonoBehaviour because someone accidentally did something funky with a DI registration.
|
||||
DependencyContainer = builder.Build(ContainerBuildOptions.IgnoreStartableComponents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a new life time scope. A single instance per registered service will be returned while this scope is active.
|
||||
/// Services can scoped to this life time using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.InstancePerLifetimeScope" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A life time scope should be created when the game leaves the main menu and loads a level with multiplayer.
|
||||
/// It should end when the game process unloads the level (e.g. player returns to the main menu).
|
||||
/// </remarks>
|
||||
public static void BeginNewLifetimeScope()
|
||||
{
|
||||
if (DependencyContainer == null)
|
||||
{
|
||||
throw new InvalidOperationException("You must install an Autofac container before initializing a new lifetime scope.");
|
||||
}
|
||||
|
||||
CurrentLifetimeScope?.Dispose();
|
||||
CurrentLifetimeScope = DependencyContainer.BeginLifetimeScope();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the life time scoped services that were registered using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.InstancePerLifetimeScope" />.
|
||||
/// </summary>
|
||||
public static void EndCurrentLifetimeScope()
|
||||
{
|
||||
CurrentLifetimeScope?.Dispose();
|
||||
OnLifetimeScopeEnded();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Only locates the service in the container, pre-lifetime scope.
|
||||
/// </summary>
|
||||
public static T LocateServicePreLifetime<T>()
|
||||
{
|
||||
return DependencyContainer.Resolve<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a service which was registered into the DI container. Creates a new instance if required.<br />
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method should not be used if the constructor is available for defining a parameter where its type is the service to inject.
|
||||
/// For Unity monobehaviours the constructor is used by Unity and cannot be used to inject services. In this case, use this method.
|
||||
/// </remarks>
|
||||
public static T LocateService<T>()
|
||||
where T : class
|
||||
{
|
||||
CheckServiceResolutionViability();
|
||||
return CurrentLifetimeScope.Resolve<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-generic alternative to <see cref="LocateService{T}" />.
|
||||
/// </summary>
|
||||
public static object LocateService(Type serviceType)
|
||||
{
|
||||
CheckServiceResolutionViability();
|
||||
return CurrentLifetimeScope.Resolve(serviceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to locate the service if it exists. Can return an <see cref="Optional{T}" /> without a value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of service to try to locate.</typeparam>
|
||||
/// <returns>Optional that might or might not hold the service instance.</returns>
|
||||
public static Optional<T> LocateOptionalService<T>() where T : class
|
||||
{
|
||||
CheckServiceResolutionViability();
|
||||
return Optional.OfNullable(CurrentLifetimeScope.ResolveOptional<T>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to locate the service if it exists. Can return an <see cref="Optional{T}" /> without a value.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">Type of service to try to locate.</param>
|
||||
/// <returns>Optional that might or might not hold the service instance.</returns>
|
||||
public static Optional<object> LocateOptionalService(Type serviceType)
|
||||
{
|
||||
CheckServiceResolutionViability();
|
||||
return Optional.OfNullable(CurrentLifetimeScope.ResolveOptional(serviceType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws if a service is asked for but without a proper life time scope.
|
||||
/// </summary>
|
||||
private static void CheckServiceResolutionViability()
|
||||
{
|
||||
if (DependencyContainer == null)
|
||||
{
|
||||
throw new InvalidOperationException("You must install an Autofac container before resolving dependencies.");
|
||||
}
|
||||
if (CurrentLifetimeScope == null)
|
||||
{
|
||||
throw new InvalidOperationException("You must begin a new lifetime scope before resolving dependencies.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnLifetimeScopeEnded() => LifetimeScopeEnded?.Invoke(null, EventArgs.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Generic static class to cache type with very fast lookups. Only use for singleton types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type in the cache, should be singleton.</typeparam>
|
||||
public static class Cache<T> where T : class
|
||||
{
|
||||
private static T value;
|
||||
public static T Value => value ??= LocateServiceAndRegister();
|
||||
public static T ValuePreLifetime => value ??= LocateServicePreLifetimeAndRegister();
|
||||
|
||||
private static T LocateServiceAndRegister()
|
||||
{
|
||||
LifetimeScopeEnded += Invalidate;
|
||||
return LocateService<T>();
|
||||
}
|
||||
|
||||
private static T LocateServicePreLifetimeAndRegister()
|
||||
{
|
||||
LifetimeScopeEnded += Invalidate;
|
||||
return LocateServicePreLifetime<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the cache for type <see cref="T" />. The next <see cref="Value" /> access will request from <see cref="NitroxServiceLocator" /> again.
|
||||
/// </summary>
|
||||
private static void Invalidate(object _, EventArgs __)
|
||||
{
|
||||
value = null;
|
||||
LifetimeScopeEnded -= Invalidate;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user