Moved event stuff to a non-manager NS.
Core assembly concept: Assemblies that will never need to get unloaded (Torch.API, Torch, Torch.Server) Event shims and patch shims on the core assemblies.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Torch.API.Managers.Event
|
||||
namespace Torch.API.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute indicating that a method should be invoked when the event occurs.
|
@@ -1,4 +1,4 @@
|
||||
namespace Torch.API.Managers.Event
|
||||
namespace Torch.API.Event
|
||||
{
|
||||
public interface IEvent
|
||||
{
|
@@ -1,4 +1,4 @@
|
||||
namespace Torch.API.Managers.Event
|
||||
namespace Torch.API.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface used to tag an event handler. This does <b>not</b> register it with the event manager.
|
@@ -1,6 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Torch.API.Managers.Event
|
||||
namespace Torch.API.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Manager class responsible for registration of event handlers.
|
@@ -3,10 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Torch.API.Managers.Event;
|
||||
using Torch.Managers.EventManager;
|
||||
using Torch.API.Event;
|
||||
|
||||
namespace Torch.Managers.Event
|
||||
namespace Torch.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an ordered list of callbacks.
|
@@ -5,10 +5,10 @@ using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NLog;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers.Event;
|
||||
using Torch.Managers.EventManager;
|
||||
using Torch.API.Event;
|
||||
using Torch.Managers;
|
||||
|
||||
namespace Torch.Managers.Event
|
||||
namespace Torch.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Manager class responsible for managing registration and dispatching of events.
|
||||
@@ -19,13 +19,18 @@ namespace Torch.Managers.Event
|
||||
|
||||
private static readonly Dictionary<Type, IEventList> _eventLists = new Dictionary<Type, IEventList>();
|
||||
|
||||
static EventManager()
|
||||
internal static void AddDispatchShims(Assembly asm)
|
||||
{
|
||||
AddDispatchShim(typeof(EventShimProgrammableBlock));
|
||||
foreach (Type type in asm.GetTypes())
|
||||
if (type.HasAttribute<EventShimAttribute>())
|
||||
AddDispatchShim(type);
|
||||
}
|
||||
|
||||
private static void AddDispatchShim(Type type)
|
||||
{
|
||||
if (!type.IsSealed || !type.IsAbstract)
|
||||
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it isn't declared singleton");
|
||||
var listsFound = 0;
|
||||
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
|
||||
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(EventList<>))
|
||||
@@ -34,9 +39,14 @@ namespace Torch.Managers.Event
|
||||
if (_eventLists.ContainsKey(eventType))
|
||||
_log.Error($"Ignore event dispatch list {type.FullName}#{field.Name}; we already have one.");
|
||||
else
|
||||
_eventLists.Add(eventType, (IEventList)field.GetValue(null));
|
||||
{
|
||||
_eventLists.Add(eventType, (IEventList) field.GetValue(null));
|
||||
listsFound++;
|
||||
}
|
||||
|
||||
}
|
||||
if (listsFound == 0)
|
||||
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it has no event lists.");
|
||||
}
|
||||
|
||||
|
20
Torch/Event/EventShimAttribute.cs
Normal file
20
Torch/Event/EventShimAttribute.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Tagging class used to indicate that the class should be treated as an event shim.
|
||||
/// Only works for core assemblies loaded by Torch (non-plugins).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Event shims should be singleton, and have one (or more) fields that are of type <see cref="EventList{T}"/>.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal class EventShimAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
@@ -1,17 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.Entities.Blocks;
|
||||
using Torch.API.Managers.Event;
|
||||
using Torch.Managers.Event;
|
||||
using Torch.API.Event;
|
||||
using Torch.Managers.PatchManager;
|
||||
using Torch.Utils;
|
||||
|
||||
namespace Torch.Managers.EventManager
|
||||
namespace Torch.Event
|
||||
{
|
||||
internal static class EventShimProgrammableBlock
|
||||
{
|
@@ -1,7 +1,7 @@
|
||||
using System.Reflection;
|
||||
using Torch.API.Managers.Event;
|
||||
using Torch.API.Event;
|
||||
|
||||
namespace Torch.Managers.Event
|
||||
namespace Torch.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the interface for adding and removing from an ordered list of callbacks.
|
@@ -7,14 +7,12 @@ namespace Torch.Managers.PatchManager
|
||||
/// <summary>
|
||||
/// Represents a set of common patches that can all be reversed in a single step.
|
||||
/// </summary>
|
||||
public class PatchContext
|
||||
public sealed class PatchContext
|
||||
{
|
||||
private readonly PatchManager _replacer;
|
||||
private readonly Dictionary<MethodBase, MethodRewritePattern> _rewritePatterns = new Dictionary<MethodBase, MethodRewritePattern>();
|
||||
|
||||
internal PatchContext(PatchManager replacer)
|
||||
internal PatchContext()
|
||||
{
|
||||
_replacer = replacer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -26,7 +24,7 @@ namespace Torch.Managers.PatchManager
|
||||
{
|
||||
if (_rewritePatterns.TryGetValue(method, out MethodRewritePattern pattern))
|
||||
return pattern;
|
||||
MethodRewritePattern parent = _replacer.GetPattern(method);
|
||||
MethodRewritePattern parent = PatchManager.GetPatternInternal(method);
|
||||
var res = new MethodRewritePattern(parent);
|
||||
_rewritePatterns.Add(method, res);
|
||||
return res;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NLog;
|
||||
using Torch.API;
|
||||
using Torch.Managers.PatchManager.Transpile;
|
||||
|
||||
@@ -12,8 +13,40 @@ namespace Torch.Managers.PatchManager
|
||||
/// </summary>
|
||||
public class PatchManager : Manager
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
internal static void AddPatchShims(Assembly asm)
|
||||
{
|
||||
foreach (Type t in asm.GetTypes())
|
||||
if (t.HasAttribute<PatchShimAttribute>())
|
||||
AddPatchShim(t);
|
||||
}
|
||||
|
||||
private static void AddPatchShim(Type type)
|
||||
{
|
||||
if (!type.IsSealed || !type.IsAbstract)
|
||||
_log.Warn($"Registering type {type.FullName} as a patch shim type, even though it isn't declared singleton");
|
||||
MethodInfo method = type.GetMethod("Patch", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
|
||||
if (method == null)
|
||||
{
|
||||
_log.Error($"Patch shim type {type.FullName} doesn't have a static Patch method.");
|
||||
return;
|
||||
}
|
||||
ParameterInfo[] ps = method.GetParameters();
|
||||
if (ps.Length != 1 || ps[0].IsOut || ps[0].IsOptional || ps[0].ParameterType.IsByRef ||
|
||||
ps[0].ParameterType == typeof(PatchContext) || method.ReturnType == typeof(void))
|
||||
{
|
||||
_log.Error($"Patch shim type {type.FullName} doesn't have a method with signature `void Patch(PatchContext)`");
|
||||
return;
|
||||
}
|
||||
var context = new PatchContext();
|
||||
lock (_coreContexts)
|
||||
_coreContexts.Add(context);
|
||||
method.Invoke(null, new object[] { context });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new patch manager. Only have one active at a time.
|
||||
/// Creates a new patch manager.
|
||||
/// </summary>
|
||||
/// <param name="torchInstance"></param>
|
||||
public PatchManager(ITorchBase torchInstance) : base(torchInstance)
|
||||
@@ -21,14 +54,15 @@ namespace Torch.Managers.PatchManager
|
||||
}
|
||||
|
||||
private static readonly Dictionary<MethodBase, DecoratedMethod> _rewritePatterns = new Dictionary<MethodBase, DecoratedMethod>();
|
||||
private readonly Dictionary<Assembly, List<PatchContext>> _contexts = new Dictionary<Assembly, List<PatchContext>>();
|
||||
private static readonly Dictionary<Assembly, List<PatchContext>> _contexts = new Dictionary<Assembly, List<PatchContext>>();
|
||||
private static List<PatchContext> _coreContexts = new List<PatchContext>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rewrite pattern for the given method, creating one if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <param name="method">Method to get the pattern for</param>
|
||||
/// <returns></returns>
|
||||
public MethodRewritePattern GetPattern(MethodBase method)
|
||||
internal static MethodRewritePattern GetPatternInternal(MethodBase method)
|
||||
{
|
||||
lock (_rewritePatterns)
|
||||
{
|
||||
@@ -40,6 +74,16 @@ namespace Torch.Managers.PatchManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rewrite pattern for the given method, creating one if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <param name="method">Method to get the pattern for</param>
|
||||
/// <returns></returns>
|
||||
public MethodRewritePattern GetPattern(MethodBase method)
|
||||
{
|
||||
return GetPatternInternal(method);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="PatchContext"/> used for tracking changes. A call to <see cref="Commit"/> will apply the patches.
|
||||
@@ -48,7 +92,7 @@ namespace Torch.Managers.PatchManager
|
||||
public PatchContext AcquireContext()
|
||||
{
|
||||
Assembly assembly = Assembly.GetCallingAssembly();
|
||||
var context = new PatchContext(this);
|
||||
var context = new PatchContext();
|
||||
lock (_contexts)
|
||||
{
|
||||
if (!_contexts.TryGetValue(assembly, out List<PatchContext> localContexts))
|
||||
@@ -106,6 +150,7 @@ namespace Torch.Managers.PatchManager
|
||||
/// </summary>
|
||||
public void Commit()
|
||||
{
|
||||
lock (_rewritePatterns)
|
||||
foreach (DecoratedMethod m in _rewritePatterns.Values)
|
||||
m.Commit();
|
||||
}
|
||||
@@ -123,11 +168,15 @@ namespace Torch.Managers.PatchManager
|
||||
/// </summary>
|
||||
public override void Detach()
|
||||
{
|
||||
lock (_contexts)
|
||||
{
|
||||
foreach (List<PatchContext> set in _contexts.Values)
|
||||
foreach (PatchContext ctx in set)
|
||||
ctx.RemoveAll();
|
||||
_contexts.Clear();
|
||||
foreach (DecoratedMethod m in _rewritePatterns.Values)
|
||||
m.Revert();
|
||||
_rewritePatterns.Clear();
|
||||
lock (_contexts)
|
||||
_contexts.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
Torch/Managers/PatchManager/PatchShimAttribute.cs
Normal file
20
Torch/Managers/PatchManager/PatchShimAttribute.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch.Managers.PatchManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Tagging class used to indicate that the class should be treated as supplying patch rules.
|
||||
/// Only works for core assemblies loaded by Torch (non-plugins).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Event shims should be singleton, and have one method of signature <i>void Patch(PatchContext)</i>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal class PatchShimAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
@@ -22,6 +22,7 @@ using Torch.API.Managers;
|
||||
using Torch.API.ModAPI;
|
||||
using Torch.API.Session;
|
||||
using Torch.Commands;
|
||||
using Torch.Event;
|
||||
using Torch.Managers;
|
||||
using Torch.Managers.ChatManager;
|
||||
using Torch.Managers.PatchManager;
|
||||
@@ -45,6 +46,8 @@ namespace Torch
|
||||
{
|
||||
static TorchBase()
|
||||
{
|
||||
RegisterCoreAssembly(typeof(ITorchBase).Assembly);
|
||||
RegisterCoreAssembly(typeof(TorchBase).Assembly);
|
||||
// We can safely never detach this since we don't reload assemblies.
|
||||
new ReflectedManager().Attach();
|
||||
}
|
||||
@@ -100,6 +103,7 @@ namespace Torch
|
||||
/// <exception cref="InvalidOperationException">Thrown if a TorchBase instance already exists.</exception>
|
||||
protected TorchBase()
|
||||
{
|
||||
RegisterCoreAssembly(GetType().Assembly);
|
||||
if (Instance != null)
|
||||
throw new InvalidOperationException("A TorchBase instance already exists.");
|
||||
|
||||
@@ -126,6 +130,7 @@ namespace Torch
|
||||
// Managers.AddManager(new KeenLogManager(this));
|
||||
Managers.AddManager(new FilesystemManager(this));
|
||||
Managers.AddManager(new UpdateManager(this));
|
||||
Managers.AddManager(new EventManager(this));
|
||||
Managers.AddManager(Plugins);
|
||||
GameStateChanged += (game, state) =>
|
||||
{
|
||||
@@ -422,7 +427,7 @@ namespace Torch
|
||||
|
||||
internal static void Inject(PatchContext target)
|
||||
{
|
||||
ConstructorInfo ctor = typeof(MySandboxGame).GetConstructor(new[] {typeof(string[])});
|
||||
ConstructorInfo ctor = typeof(MySandboxGame).GetConstructor(new[] { typeof(string[]) });
|
||||
if (ctor == null)
|
||||
throw new ArgumentException("Can't find constructor MySandboxGame(string[])");
|
||||
target.GetPattern(ctor).Prefixes.Add(MethodRef(nameof(PrefixConstructor)));
|
||||
@@ -476,5 +481,16 @@ namespace Torch
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static readonly HashSet<Assembly> _registeredAssemblies = new HashSet<Assembly>();
|
||||
private static void RegisterCoreAssembly(Assembly asm)
|
||||
{
|
||||
lock (_registeredAssemblies)
|
||||
if (_registeredAssemblies.Add(asm))
|
||||
{
|
||||
EventManager.AddDispatchShims(asm);
|
||||
PatchManager.AddPatchShims(asm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user