Added load order restrictions for DependencyAttribute

Abstracted dependency manager away as an interface
This commit is contained in:
Westin Miller
2017-08-18 02:03:53 -07:00
parent 42f58a8649
commit 80d4f62694
13 changed files with 220 additions and 103 deletions

View File

@@ -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>

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

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

View File

@@ -26,11 +26,6 @@ namespace Torch.API.Managers
/// </summary>
void UpdatePlugins();
/// <summary>
/// Disposes all loaded plugins.
/// </summary>
void DisposePlugins();
/// <summary>
/// Load plugins.
/// </summary>

View File

@@ -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" />

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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; }

View File

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

View File

@@ -146,7 +146,7 @@ namespace Torch.Managers
}
/// <inheritdoc />
public void Dispose()
public override void Dispose()
{
_updatePollTimer?.Dispose();
}

View File

@@ -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" />

View File

@@ -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 />