Added load order restrictions for DependencyAttribute
Abstracted dependency manager away as an interface
This commit is contained in:
@@ -44,6 +44,9 @@ namespace Torch.API
|
||||
/// <inheritdoc cref="IPluginManager"/>
|
||||
IPluginManager Plugins { get; }
|
||||
|
||||
/// <inheritdoc cref="IDependencyManager"/>
|
||||
IDependencyManager Managers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The binary version of the current instance.
|
||||
/// </summary>
|
||||
@@ -90,12 +93,6 @@ namespace Torch.API
|
||||
/// Initialize the Torch instance.
|
||||
/// </summary>
|
||||
void Init();
|
||||
|
||||
/// <summary>
|
||||
/// Get an <see cref="IManager"/> that is part of the Torch instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Manager type</typeparam>
|
||||
T GetManager<T>() where T : class, IManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
88
Torch.API/Managers/IDependencyManager.cs
Normal file
88
Torch.API/Managers/IDependencyManager.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch.API.Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages a set of <see cref="IManager"/> and the dependencies between them.
|
||||
/// </summary>
|
||||
public interface IDependencyManager : IDependencyProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers the given manager into the dependency system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method only returns false when there is already a manager registered with a type derived from this given manager,
|
||||
/// or when the given manager is derived from an already existing manager.
|
||||
/// </remarks>
|
||||
/// <param name="manager">Manager to register</param>
|
||||
/// <exception cref="InvalidOperationException">When adding a new manager to an initialized dependency manager</exception>
|
||||
/// <returns>true if added, false if not</returns>
|
||||
bool AddManager(IManager manager);
|
||||
|
||||
/// <summary>
|
||||
/// Clears all managers registered with this dependency manager
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
void ClearManagers();
|
||||
|
||||
/// <summary>
|
||||
/// Removes a single manager from this dependency manager.
|
||||
/// </summary>
|
||||
/// <param name="manager">The manager to remove</param>
|
||||
/// <returns>true if successful, false if the manager wasn't found</returns>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
bool RemoveManager(IManager manager);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the dependency manager, and all its registered managers.
|
||||
/// </summary>
|
||||
void Init();
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the dependency manager, and all its registered managers.
|
||||
/// </summary>
|
||||
void Dispose();
|
||||
|
||||
/// <summary>
|
||||
/// The order that managers should be loaded in. (Dependencies, then dependents)
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">When trying to determine load order before this dependency manager is initialized</exception>
|
||||
IEnumerable<IManager> LoadOrder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The order that managers should be unloaded in. (Dependents, then dependencies)
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">When trying to determine unload order before this dependency manager is initialized</exception>
|
||||
IEnumerable<IManager> UnloadOrder { get; }
|
||||
}
|
||||
|
||||
public static class DependencyManagerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes a single manager from this dependency manager.
|
||||
/// </summary>
|
||||
/// <param name="managerType">The dependency type to remove</param>
|
||||
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
public static IManager RemoveManager(this IDependencyManager depManager, Type managerType)
|
||||
{
|
||||
IManager mgr = depManager.GetManager(managerType);
|
||||
return depManager.RemoveManager(mgr) ? mgr : null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a single manager from this dependency manager.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The dependency type to remove</typeparam>
|
||||
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
public static IManager RemoveManager<T>(this IDependencyManager depManager)
|
||||
{
|
||||
return depManager.RemoveManager(typeof(T));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
32
Torch.API/Managers/IDependencyProvider.cs
Normal file
32
Torch.API/Managers/IDependencyProvider.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch.API.Managers
|
||||
{
|
||||
public interface IDependencyProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the manager that provides the given type. If there is no such manager, returns null.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of manager</param>
|
||||
/// <returns>manager, or null if none exists</returns>
|
||||
IManager GetManager(Type type);
|
||||
}
|
||||
|
||||
public static class DependencyProviderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the manager that provides the given type. If there is no such manager, returns null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of manager</typeparam>
|
||||
/// <returns>manager, or null if none exists</returns>
|
||||
public static T GetManager<T>(this IDependencyProvider depProvider) where T : class, IManager
|
||||
{
|
||||
return (T)depProvider.GetManager(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -26,11 +26,6 @@ namespace Torch.API.Managers
|
||||
/// </summary>
|
||||
void UpdatePlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all loaded plugins.
|
||||
/// </summary>
|
||||
void DisposePlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Load plugins.
|
||||
/// </summary>
|
||||
|
@@ -157,6 +157,8 @@
|
||||
<Compile Include="ConnectionState.cs" />
|
||||
<Compile Include="IChatMessage.cs" />
|
||||
<Compile Include="ITorchConfig.cs" />
|
||||
<Compile Include="Managers\IDependencyManager.cs" />
|
||||
<Compile Include="Managers\IDependencyProvider.cs" />
|
||||
<Compile Include="Managers\IManager.cs" />
|
||||
<Compile Include="Managers\IMultiplayerManager.cs" />
|
||||
<Compile Include="IPlayer.cs" />
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Server.Managers;
|
||||
using Torch.Server.ViewModels;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Torch.Server.Views
|
||||
public ConfigControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
_instanceManager = TorchBase.Instance.GetManager<InstanceManager>();
|
||||
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
|
||||
DataContext = _instanceManager.DedicatedConfig;
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Sandbox.ModAPI;
|
||||
using Torch;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Commands.Permissions;
|
||||
using Torch.Managers;
|
||||
using VRage.Game.ModAPI;
|
||||
@@ -50,7 +51,7 @@ namespace Torch.Commands
|
||||
[Command("longhelp", "Get verbose help. Will send a long message, check the Comms tab.")]
|
||||
public void LongHelp()
|
||||
{
|
||||
var commandManager = Context.Torch.GetManager<CommandManager>();
|
||||
var commandManager = Context.Torch.Managers.GetManager<CommandManager>();
|
||||
commandManager.Commands.GetNode(Context.Args, out CommandTree.CommandNode node);
|
||||
|
||||
if (node != null)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -7,20 +8,22 @@ using Torch.API.Managers;
|
||||
|
||||
namespace Torch.Managers
|
||||
{
|
||||
public sealed class DependencyManager
|
||||
public sealed class DependencyManager : IDependencyManager
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private class DependencyInfo
|
||||
{
|
||||
private readonly Manager.DependencyAttribute _attribute;
|
||||
internal Type DependencyType => Field.FieldType;
|
||||
internal FieldInfo Field { get; private set; }
|
||||
internal bool Optional { get; private set; }
|
||||
internal bool Optional => _attribute.Optional;
|
||||
internal bool Ordered => _attribute.Ordered;
|
||||
|
||||
public DependencyInfo(FieldInfo field)
|
||||
{
|
||||
Field = field;
|
||||
Optional = field.GetCustomAttribute<Manager.DependencyAttribute>().Optional;
|
||||
_attribute = field.GetCustomAttribute<Manager.DependencyAttribute>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,18 +72,14 @@ namespace Torch.Managers
|
||||
private readonly Dictionary<Type, ManagerInstance> _dependencySatisfaction;
|
||||
private readonly List<ManagerInstance> _registeredManagers;
|
||||
private readonly List<ManagerInstance> _orderedManagers;
|
||||
private readonly DependencyManager _parentManager;
|
||||
private readonly IDependencyProvider[] _parentProviders;
|
||||
|
||||
public DependencyManager(DependencyManager parent = null)
|
||||
public DependencyManager(params IDependencyProvider[] parents)
|
||||
{
|
||||
_dependencySatisfaction = new Dictionary<Type, ManagerInstance>();
|
||||
_registeredManagers = new List<ManagerInstance>();
|
||||
_orderedManagers = new List<ManagerInstance>();
|
||||
_parentManager = parent;
|
||||
if (parent == null)
|
||||
return;
|
||||
foreach (KeyValuePair<Type, ManagerInstance> kv in parent._dependencySatisfaction)
|
||||
_dependencySatisfaction.Add(kv.Key, kv.Value);
|
||||
_parentProviders = parents.Distinct().ToArray();
|
||||
}
|
||||
|
||||
private void AddDependencySatisfaction(ManagerInstance instance)
|
||||
@@ -100,16 +99,7 @@ namespace Torch.Managers
|
||||
AddDependencySatisfaction(manager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the given manager into the dependency system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method only returns false when there is already a manager registered with a type derived from this given manager,
|
||||
/// or when the given manager is derived from an already existing manager.
|
||||
/// </remarks>
|
||||
/// <param name="manager">Manager to register</param>
|
||||
/// <exception cref="InvalidOperationException">When adding a new manager to an initialized dependency manager</exception>
|
||||
/// <returns>true if added, false if not</returns>
|
||||
/// <inheritdoc/>
|
||||
public bool AddManager(IManager manager)
|
||||
{
|
||||
if (_initialized)
|
||||
@@ -122,16 +112,13 @@ namespace Torch.Managers
|
||||
if (_registeredManagers.Any(x => manager.GetType().IsInstanceOfType(x.Instance)))
|
||||
return false;
|
||||
|
||||
var instance = new ManagerInstance(manager);
|
||||
ManagerInstance instance = new ManagerInstance(manager);
|
||||
_registeredManagers.Add(instance);
|
||||
AddDependencySatisfaction(instance);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all managers registered with this dependency manager
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
/// <inheritdoc/>
|
||||
public void ClearManagers()
|
||||
{
|
||||
if (_initialized)
|
||||
@@ -141,19 +128,14 @@ namespace Torch.Managers
|
||||
_dependencySatisfaction.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a single manager from this dependency manager.
|
||||
/// </summary>
|
||||
/// <param name="manager">The manager to remove</param>
|
||||
/// <returns>true if successful, false if the manager wasn't found</returns>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
/// <inheritdoc/>
|
||||
public bool RemoveManager(IManager manager)
|
||||
{
|
||||
if (_initialized)
|
||||
throw new InvalidOperationException("Can't remove managers from an initialized dependency manager");
|
||||
if (manager == null)
|
||||
return false;
|
||||
for (var i = 0; i < _registeredManagers.Count; i++)
|
||||
for (int i = 0; i < _registeredManagers.Count; i++)
|
||||
if (_registeredManagers[i].Instance == manager)
|
||||
{
|
||||
_registeredManagers.RemoveAtFast(i);
|
||||
@@ -163,29 +145,6 @@ namespace Torch.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a single manager from this dependency manager.
|
||||
/// </summary>
|
||||
/// <param name="type">The dependency type to remove</param>
|
||||
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
public IManager RemoveManager(Type type)
|
||||
{
|
||||
IManager mgr = GetManager(type);
|
||||
return RemoveManager(mgr) ? mgr : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a single manager from this dependency manager.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The dependency type to remove</typeparam>
|
||||
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
|
||||
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||
public IManager RemoveManager<T>()
|
||||
{
|
||||
return RemoveManager(typeof(T));
|
||||
}
|
||||
|
||||
private void Sort()
|
||||
{
|
||||
// Resets the previous sort results
|
||||
@@ -204,11 +163,14 @@ namespace Torch.Managers
|
||||
foreach (DependencyInfo dependency in manager.Dependencies)
|
||||
{
|
||||
if (_dependencySatisfaction.TryGetValue(dependency.DependencyType, out var dependencyInstance))
|
||||
{
|
||||
if (dependency.Ordered)
|
||||
{
|
||||
inFactor++;
|
||||
dependencyInstance.Dependents.Add(manager);
|
||||
}
|
||||
else if (!dependency.Optional && _parentManager?.GetManager(dependency.DependencyType) == null)
|
||||
}
|
||||
else if (!dependency.Optional && _parentProviders.All(x => x.GetManager(dependency.DependencyType) == null))
|
||||
_log.Warn("Unable to satisfy dependency {0} ({1}) of {2}.", dependency.DependencyType.Name,
|
||||
dependency.Field.Name, manager.Instance.GetType().Name);
|
||||
}
|
||||
@@ -305,28 +267,43 @@ namespace Torch.Managers
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IManager> LoadOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_initialized)
|
||||
throw new InvalidOperationException("Can't determine dependency load order when uninitialized");
|
||||
foreach (ManagerInstance k in _orderedManagers)
|
||||
yield return k.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the manager that provides the given type. If there is no such manager, returns null.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of manager</param>
|
||||
/// <returns>manager, or null if none exists</returns>
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IManager> UnloadOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_initialized)
|
||||
throw new InvalidOperationException("Can't determine dependency load order when uninitialized");
|
||||
for (int i = _orderedManagers.Count - 1; i >= 0; i--)
|
||||
yield return _orderedManagers[i].Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IManager GetManager(Type type)
|
||||
{
|
||||
// ReSharper disable once ConvertIfStatementToReturnStatement
|
||||
if (_dependencySatisfaction.TryGetValue(type, out ManagerInstance mgr))
|
||||
return mgr.Instance;
|
||||
return _parentManager?.GetManager(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the manager that provides the given type. If there is no such manager, returns null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of manager</typeparam>
|
||||
/// <returns>manager, or null if none exists</returns>
|
||||
public T GetManager<T>() where T : class, IManager
|
||||
foreach (IDependencyProvider provider in _parentProviders)
|
||||
{
|
||||
return (T)GetManager(typeof(T));
|
||||
IManager entry = provider.GetManager(type);
|
||||
if (entry != null)
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -31,7 +31,29 @@ namespace Torch.Managers
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class DependencyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// If this dependency isn't required.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The tagged field can be null if, and only if, this is true.
|
||||
/// </remarks>
|
||||
public bool Optional { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Dependency must be loaded before and unloaded after the containing manager.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public class NetworkManager : Manager { }
|
||||
/// public class ChatManager : Manager {
|
||||
/// [Dependency(Ordered = true)]
|
||||
/// private NetworkManager _network;
|
||||
/// }
|
||||
/// </code>
|
||||
/// Load order will be NetworkManager, then ChatManager.
|
||||
/// Unload order will be ChatManager, then NetworkManager
|
||||
/// </example>
|
||||
public bool Ordered { get; set; } = true;
|
||||
}
|
||||
|
||||
protected ITorchBase Torch { get; }
|
||||
|
@@ -48,7 +48,7 @@ namespace Torch.Managers
|
||||
/// <summary>
|
||||
/// Unloads all plugins.
|
||||
/// </summary>
|
||||
public void DisposePlugins()
|
||||
public override void Dispose()
|
||||
{
|
||||
foreach (var plugin in Plugins)
|
||||
plugin.Dispose();
|
||||
|
@@ -146,7 +146,7 @@ namespace Torch.Managers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
public override void Dispose()
|
||||
{
|
||||
_updatePollTimer?.Dispose();
|
||||
}
|
||||
|
@@ -181,6 +181,7 @@
|
||||
<Compile Include="TorchBase.cs" />
|
||||
<Compile Include="SteamService.cs" />
|
||||
<Compile Include="TorchPluginBase.cs" />
|
||||
<Compile Include="TorchSession.cs" />
|
||||
<Compile Include="ViewModels\ModViewModel.cs" />
|
||||
<Compile Include="Collections\MTObservableCollection.cs" />
|
||||
<Compile Include="Extensions\MyPlayerCollectionExtensions.cs" />
|
||||
|
@@ -73,7 +73,8 @@ namespace Torch
|
||||
/// </summary>
|
||||
protected static Logger Log { get; } = LogManager.GetLogger("Torch");
|
||||
|
||||
private readonly DependencyManager _managers;
|
||||
/// <inheritdoc/>
|
||||
public IDependencyManager Managers { get; }
|
||||
|
||||
private bool _init;
|
||||
|
||||
@@ -91,7 +92,7 @@ namespace Torch
|
||||
TorchVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
RunArgs = new string[0];
|
||||
|
||||
_managers = new DependencyManager();
|
||||
Managers = new DependencyManager();
|
||||
|
||||
Plugins = new PluginManager(this);
|
||||
Multiplayer = new MultiplayerManager(this);
|
||||
@@ -99,14 +100,14 @@ namespace Torch
|
||||
Network = new NetworkManager(this);
|
||||
Commands = new CommandManager(this);
|
||||
|
||||
_managers.AddManager(new FilesystemManager(this));
|
||||
_managers.AddManager(new UpdateManager(this));
|
||||
_managers.AddManager(Network);
|
||||
_managers.AddManager(Commands);
|
||||
_managers.AddManager(Plugins);
|
||||
_managers.AddManager(Multiplayer);
|
||||
_managers.AddManager(Entities);
|
||||
_managers.AddManager(new ChatManager(this));
|
||||
Managers.AddManager(new FilesystemManager(this));
|
||||
Managers.AddManager(new UpdateManager(this));
|
||||
Managers.AddManager(Network);
|
||||
Managers.AddManager(Commands);
|
||||
Managers.AddManager(Plugins);
|
||||
Managers.AddManager(Multiplayer);
|
||||
Managers.AddManager(Entities);
|
||||
Managers.AddManager(new ChatManager(this));
|
||||
|
||||
|
||||
TorchAPI.Instance = this;
|
||||
@@ -115,13 +116,13 @@ namespace Torch
|
||||
/// <inheritdoc />
|
||||
public T GetManager<T>() where T : class, IManager
|
||||
{
|
||||
return _managers.GetManager<T>();
|
||||
return Managers.GetManager<T>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AddManager<T>(T manager) where T : class, IManager
|
||||
{
|
||||
return _managers.AddManager(manager);
|
||||
return Managers.AddManager(manager);
|
||||
}
|
||||
|
||||
public bool IsOnGameThread()
|
||||
@@ -249,7 +250,7 @@ namespace Torch
|
||||
MySession.OnUnloading += OnSessionUnloading;
|
||||
MySession.OnUnloaded += OnSessionUnloaded;
|
||||
RegisterVRagePlugin();
|
||||
_managers.Init();
|
||||
Managers.Init();
|
||||
_init = true;
|
||||
}
|
||||
|
||||
@@ -317,7 +318,7 @@ namespace Torch
|
||||
/// <inheritdoc />
|
||||
public virtual void Dispose()
|
||||
{
|
||||
Plugins.DisposePlugins();
|
||||
Managers.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
Reference in New Issue
Block a user