Patch manager supports communicating method cancel state

Game state patch split out into a patch shim
Special NS for events
Core and auxillary assembly concepts
Reflected manager stops working for mods/scripts
This commit is contained in:
Westin Miller
2017-10-10 02:47:45 -07:00
parent 62d73cbf96
commit 3307d2d23d
9 changed files with 187 additions and 144 deletions

View File

@@ -163,14 +163,14 @@
<Compile Include="ITorchConfig.cs" /> <Compile Include="ITorchConfig.cs" />
<Compile Include="Managers\DependencyManagerExtensions.cs" /> <Compile Include="Managers\DependencyManagerExtensions.cs" />
<Compile Include="Managers\DependencyProviderExtensions.cs" /> <Compile Include="Managers\DependencyProviderExtensions.cs" />
<Compile Include="Managers\Event\EventHandlerAttribute.cs" /> <Compile Include="Event\EventHandlerAttribute.cs" />
<Compile Include="Managers\Event\IEvent.cs" /> <Compile Include="Event\IEvent.cs" />
<Compile Include="Managers\Event\IEventHandler.cs" /> <Compile Include="Event\IEventHandler.cs" />
<Compile Include="Managers\IChatManagerClient.cs" /> <Compile Include="Managers\IChatManagerClient.cs" />
<Compile Include="Managers\IChatManagerServer.cs" /> <Compile Include="Managers\IChatManagerServer.cs" />
<Compile Include="Managers\IDependencyManager.cs" /> <Compile Include="Managers\IDependencyManager.cs" />
<Compile Include="Managers\IDependencyProvider.cs" /> <Compile Include="Managers\IDependencyProvider.cs" />
<Compile Include="Managers\Event\IEventManager.cs" /> <Compile Include="Event\IEventManager.cs" />
<Compile Include="Managers\IManager.cs" /> <Compile Include="Managers\IManager.cs" />
<Compile Include="Managers\IMultiplayerManagerClient.cs" /> <Compile Include="Managers\IMultiplayerManagerClient.cs" />
<Compile Include="Managers\IMultiplayerManagerBase.cs" /> <Compile Include="Managers\IMultiplayerManagerBase.cs" />

View File

@@ -26,8 +26,12 @@ namespace Torch.Event
AddDispatchShim(type); AddDispatchShim(type);
} }
private static readonly HashSet<Type> _dispatchShims = new HashSet<Type>();
private static void AddDispatchShim(Type type) private static void AddDispatchShim(Type type)
{ {
lock (_dispatchShims)
if (!_dispatchShims.Add(type))
return;
if (!type.IsSealed || !type.IsAbstract) if (!type.IsSealed || !type.IsAbstract)
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it isn't declared singleton"); _log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it isn't declared singleton");
var listsFound = 0; var listsFound = 0;
@@ -40,7 +44,7 @@ namespace Torch.Event
_log.Error($"Ignore event dispatch list {type.FullName}#{field.Name}; we already have one."); _log.Error($"Ignore event dispatch list {type.FullName}#{field.Name}; we already have one.");
else else
{ {
_eventLists.Add(eventType, (IEventList) field.GetValue(null)); _eventLists.Add(eventType, (IEventList)field.GetValue(null));
listsFound++; listsFound++;
} }

View File

@@ -79,8 +79,9 @@ namespace Torch.Managers.PatchManager
} }
private const string INSTANCE_PARAMETER = "__instance"; public const string INSTANCE_PARAMETER = "__instance";
private const string RESULT_PARAMETER = "__result"; public const string RESULT_PARAMETER = "__result";
public const string PREFIX_SKIPPED_PARAMETER = "__prefixSkipped";
#pragma warning disable 649 #pragma warning disable 649
[ReflectedStaticMethod(Type = typeof(RuntimeHelpers), Name = "_CompileMethod", OverrideTypeNames = new[] { "System.IRuntimeMethodInfo" })] [ReflectedStaticMethod(Type = typeof(RuntimeHelpers), Name = "_CompileMethod", OverrideTypeNames = new[] { "System.IRuntimeMethodInfo" })]
@@ -118,7 +119,7 @@ namespace Torch.Managers.PatchManager
var specialVariables = new Dictionary<string, LocalBuilder>(); var specialVariables = new Dictionary<string, LocalBuilder>();
Label labelAfterOriginalContent = target.DefineLabel(); Label labelAfterOriginalContent = target.DefineLabel();
Label labelAfterOriginalReturn = target.DefineLabel(); Label labelSkipMethodContent = target.DefineLabel();
Type returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void); Type returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void);
@@ -131,6 +132,13 @@ namespace Torch.Managers.PatchManager
resultVariable = target.DeclareLocal(returnType); resultVariable = target.DeclareLocal(returnType);
} }
resultVariable?.SetToDefault(target); resultVariable?.SetToDefault(target);
LocalBuilder prefixSkippedVariable = null;
if (Prefixes.Count > 0 && Suffixes.Any(x => x.GetParameters()
.Any(y => y.Name.Equals(PREFIX_SKIPPED_PARAMETER))))
{
prefixSkippedVariable = target.DeclareLocal(typeof(bool));
specialVariables.Add(PREFIX_SKIPPED_PARAMETER, prefixSkippedVariable);
}
if (resultVariable != null) if (resultVariable != null)
specialVariables.Add(RESULT_PARAMETER, resultVariable); specialVariables.Add(RESULT_PARAMETER, resultVariable);
@@ -140,7 +148,7 @@ namespace Torch.Managers.PatchManager
{ {
EmitMonkeyCall(target, prefix, specialVariables); EmitMonkeyCall(target, prefix, specialVariables);
if (prefix.ReturnType == typeof(bool)) if (prefix.ReturnType == typeof(bool))
target.Emit(OpCodes.Brfalse, labelAfterOriginalReturn); target.Emit(OpCodes.Brfalse, labelSkipMethodContent);
else if (prefix.ReturnType != typeof(void)) else if (prefix.ReturnType != typeof(void))
throw new Exception( throw new Exception(
$"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}"); $"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}");
@@ -154,7 +162,15 @@ namespace Torch.Managers.PatchManager
target.MarkLabel(labelAfterOriginalContent); target.MarkLabel(labelAfterOriginalContent);
if (resultVariable != null) if (resultVariable != null)
target.Emit(OpCodes.Stloc, resultVariable); target.Emit(OpCodes.Stloc, resultVariable);
target.MarkLabel(labelAfterOriginalReturn); Label notSkip = target.DefineLabel();
target.Emit(OpCodes.Br, notSkip);
target.MarkLabel(labelSkipMethodContent);
if (prefixSkippedVariable != null)
{
target.Emit(OpCodes.Ldc_I4_1);
target.Emit(OpCodes.Stloc, prefixSkippedVariable);
}
target.MarkLabel(notSkip);
target.EmitComment("Suffixes Begin"); target.EmitComment("Suffixes Begin");
foreach (MethodInfo suffix in Suffixes) foreach (MethodInfo suffix in Suffixes)
@@ -182,8 +198,18 @@ namespace Torch.Managers.PatchManager
throw new Exception("Can't use an instance parameter for a static method"); throw new Exception("Can't use an instance parameter for a static method");
target.Emit(OpCodes.Ldarg_0); target.Emit(OpCodes.Ldarg_0);
break; break;
case PREFIX_SKIPPED_PARAMETER:
if (param.ParameterType != typeof(bool))
throw new Exception($"Prefix skipped parameter {param.ParameterType} must be of type bool");
if (param.ParameterType.IsByRef || param.IsOut)
throw new Exception($"Prefix skipped parameter {param.ParameterType} can't be a reference type");
if (specialVariables.TryGetValue(PREFIX_SKIPPED_PARAMETER, out LocalBuilder prefixSkip))
target.Emit(OpCodes.Ldloc, prefixSkip);
else
target.Emit(OpCodes.Ldc_I4_0);
break;
case RESULT_PARAMETER: case RESULT_PARAMETER:
var retType = param.ParameterType.IsByRef Type retType = param.ParameterType.IsByRef
? param.ParameterType.GetElementType() ? param.ParameterType.GetElementType()
: param.ParameterType; : param.ParameterType;
if (retType == null || !retType.IsAssignableFrom(specialVariables[RESULT_PARAMETER].LocalType)) if (retType == null || !retType.IsAssignableFrom(specialVariables[RESULT_PARAMETER].LocalType))
@@ -191,13 +217,13 @@ namespace Torch.Managers.PatchManager
target.Emit(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc, specialVariables[RESULT_PARAMETER]); target.Emit(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc, specialVariables[RESULT_PARAMETER]);
break; break;
default: default:
var declParam = _method.GetParameters().FirstOrDefault(x => x.Name == param.Name); ParameterInfo declParam = _method.GetParameters().FirstOrDefault(x => x.Name == param.Name);
if (declParam == null) if (declParam == null)
throw new Exception($"Parameter name {param.Name} not found"); throw new Exception($"Parameter name {param.Name} not found");
var paramIdx = (_method.IsStatic ? 0 : 1) + declParam.Position; int paramIdx = (_method.IsStatic ? 0 : 1) + declParam.Position;
var patchByRef = param.IsOut || param.ParameterType.IsByRef; bool patchByRef = param.IsOut || param.ParameterType.IsByRef;
var declByRef = declParam.IsOut || declParam.ParameterType.IsByRef; bool declByRef = declParam.IsOut || declParam.ParameterType.IsByRef;
if (patchByRef == declByRef) if (patchByRef == declByRef)
target.Emit(OpCodes.Ldarg, paramIdx); target.Emit(OpCodes.Ldarg, paramIdx);
else if (patchByRef) else if (patchByRef)

View File

@@ -22,8 +22,14 @@ namespace Torch.Managers.PatchManager
AddPatchShim(t); AddPatchShim(t);
} }
private static void AddPatchShim(Type type) private static readonly HashSet<Type> _patchShims = new HashSet<Type>();
// Internal, not static, so the static cctor of TorchBase can hookup the GameStatePatchShim which tells us when
// its safe to patch the rest of the game.
internal static void AddPatchShim(Type type)
{ {
lock (_patchShims)
if (!_patchShims.Add(type))
return;
if (!type.IsSealed || !type.IsAbstract) if (!type.IsSealed || !type.IsAbstract)
_log.Warn($"Registering type {type.FullName} as a patch shim type, even though it isn't declared singleton"); _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); MethodInfo method = type.GetMethod("Patch", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
@@ -34,7 +40,7 @@ namespace Torch.Managers.PatchManager
} }
ParameterInfo[] ps = method.GetParameters(); ParameterInfo[] ps = method.GetParameters();
if (ps.Length != 1 || ps[0].IsOut || ps[0].IsOptional || ps[0].ParameterType.IsByRef || if (ps.Length != 1 || ps[0].IsOut || ps[0].IsOptional || ps[0].ParameterType.IsByRef ||
ps[0].ParameterType == typeof(PatchContext) || method.ReturnType == typeof(void)) 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)`"); _log.Error($"Patch shim type {type.FullName} doesn't have a method with signature `void Patch(PatchContext)`");
return; return;
@@ -55,13 +61,10 @@ namespace Torch.Managers.PatchManager
private static readonly Dictionary<MethodBase, DecoratedMethod> _rewritePatterns = new Dictionary<MethodBase, DecoratedMethod>(); private static readonly Dictionary<MethodBase, DecoratedMethod> _rewritePatterns = new Dictionary<MethodBase, DecoratedMethod>();
private static 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>(); // ReSharper disable once CollectionNeverQueried.Local because we may want this in the future.
private static readonly List<PatchContext> _coreContexts = new List<PatchContext>();
/// <summary> /// <inheritdoc cref="GetPattern"/>
/// 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>
internal static MethodRewritePattern GetPatternInternal(MethodBase method) internal static MethodRewritePattern GetPatternInternal(MethodBase method)
{ {
lock (_rewritePatterns) lock (_rewritePatterns)
@@ -145,14 +148,20 @@ namespace Torch.Managers.PatchManager
return count; return count;
} }
/// <inheritdoc cref="Commit"/>
internal static void CommitInternal()
{
lock (_rewritePatterns)
foreach (DecoratedMethod m in _rewritePatterns.Values)
m.Commit();
}
/// <summary> /// <summary>
/// Commits all method decorations into IL. /// Commits all method decorations into IL.
/// </summary> /// </summary>
public void Commit() public void Commit()
{ {
lock (_rewritePatterns) CommitInternal();
foreach (DecoratedMethod m in _rewritePatterns.Values)
m.Commit();
} }
/// <summary> /// <summary>

View File

@@ -0,0 +1,75 @@
using System;
using System.Reflection;
using Sandbox;
using Torch.API;
using Torch.Managers.PatchManager;
using Torch.Utils;
namespace Torch.Patches
{
[PatchShim]
internal static class GameStatePatchShim
{
#pragma warning disable 649
[ReflectedMethodInfo(typeof(MySandboxGame), nameof(MySandboxGame.Dispose))]
private static MethodInfo _sandboxGameDispose;
[ReflectedMethodInfo(typeof(MySandboxGame), "Initialize")]
private static MethodInfo _sandboxGameInit;
#pragma warning restore 649
internal static void Patch(PatchContext target)
{
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(PrefixConstructor));
target.GetPattern(ctor).Suffixes.Add(MethodRef(SuffixConstructor));
target.GetPattern(_sandboxGameInit).Prefixes.Add(MethodRef(PrefixInit));
target.GetPattern(_sandboxGameInit).Suffixes.Add(MethodRef(SuffixInit));
target.GetPattern(_sandboxGameDispose).Prefixes.Add(MethodRef(PrefixDispose));
target.GetPattern(_sandboxGameDispose).Suffixes.Add(MethodRef(SuffixDispose));
}
private static MethodInfo MethodRef(Action a )
{
return a.Method;
}
private static void PrefixConstructor()
{
if (TorchBase.Instance is TorchBase tb)
tb.GameState = TorchGameState.Creating;
}
private static void SuffixConstructor()
{
PatchManager.CommitInternal();
if (TorchBase.Instance is TorchBase tb)
tb.GameState = TorchGameState.Created;
}
private static void PrefixInit()
{
if (TorchBase.Instance is TorchBase tb)
tb.GameState = TorchGameState.Loading;
}
private static void SuffixInit()
{
if (TorchBase.Instance is TorchBase tb)
tb.GameState = TorchGameState.Loaded;
}
private static void PrefixDispose()
{
if (TorchBase.Instance is TorchBase tb)
tb.GameState = TorchGameState.Unloading;
}
private static void SuffixDispose()
{
if (TorchBase.Instance is TorchBase tb)
tb.GameState = TorchGameState.Unloaded;
}
}
}

View File

@@ -268,7 +268,8 @@ namespace Torch.Managers
{ {
var data = new byte[entry.Length]; var data = new byte[entry.Length];
stream.Read(data, 0, data.Length); stream.Read(data, 0, data.Length);
assemblies.Add(Assembly.Load(data)); Assembly asm = Assembly.Load(data);
TorchBase.RegisterAuxAssembly(asm);
} }
} }
} }

View File

@@ -155,15 +155,16 @@
<Link>Properties\AssemblyVersion.cs</Link> <Link>Properties\AssemblyVersion.cs</Link>
</Compile> </Compile>
<Compile Include="Collections\ObservableList.cs" /> <Compile Include="Collections\ObservableList.cs" />
<Compile Include="Event\EventShimAttribute.cs" />
<Compile Include="Managers\ChatManager\ChatManagerClient.cs" /> <Compile Include="Managers\ChatManager\ChatManagerClient.cs" />
<Compile Include="Managers\ChatManager\ChatManagerServer.cs" /> <Compile Include="Managers\ChatManager\ChatManagerServer.cs" />
<Compile Include="Extensions\DispatcherExtensions.cs" /> <Compile Include="Extensions\DispatcherExtensions.cs" />
<Compile Include="Extensions\ICollectionExtensions.cs" /> <Compile Include="Extensions\ICollectionExtensions.cs" />
<Compile Include="Managers\DependencyManager.cs" /> <Compile Include="Managers\DependencyManager.cs" />
<Compile Include="Managers\Event\EventList.cs" /> <Compile Include="Event\EventList.cs" />
<Compile Include="Managers\Event\EventManager.cs" /> <Compile Include="Event\EventManager.cs" />
<Compile Include="Managers\Event\EventShimProgrammableBlock.cs" /> <Compile Include="Event\EventShimProgrammableBlock.cs" />
<Compile Include="Managers\Event\IEventList.cs" /> <Compile Include="Event\IEventList.cs" />
<Compile Include="Managers\KeenLogManager.cs" /> <Compile Include="Managers\KeenLogManager.cs" />
<Compile Include="Managers\PatchManager\AssemblyMemory.cs" /> <Compile Include="Managers\PatchManager\AssemblyMemory.cs" />
<Compile Include="Managers\PatchManager\DecoratedMethod.cs" /> <Compile Include="Managers\PatchManager\DecoratedMethod.cs" />
@@ -179,12 +180,14 @@
<Compile Include="Managers\PatchManager\MSIL\MsilOperandInline.cs" /> <Compile Include="Managers\PatchManager\MSIL\MsilOperandInline.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilOperandSwitch.cs" /> <Compile Include="Managers\PatchManager\MSIL\MsilOperandSwitch.cs" />
<Compile Include="Managers\PatchManager\MethodRewritePattern.cs" /> <Compile Include="Managers\PatchManager\MethodRewritePattern.cs" />
<Compile Include="Managers\PatchManager\PatchShimAttribute.cs" />
<Compile Include="Managers\PatchManager\PatchContext.cs" /> <Compile Include="Managers\PatchManager\PatchContext.cs" />
<Compile Include="Managers\PatchManager\PatchManager.cs" /> <Compile Include="Managers\PatchManager\PatchManager.cs" />
<Compile Include="Managers\PatchManager\PatchPriorityAttribute.cs" /> <Compile Include="Managers\PatchManager\PatchPriorityAttribute.cs" />
<Compile Include="Managers\PatchManager\Transpile\LoggingILGenerator.cs" /> <Compile Include="Managers\PatchManager\Transpile\LoggingILGenerator.cs" />
<Compile Include="Managers\PatchManager\Transpile\MethodContext.cs" /> <Compile Include="Managers\PatchManager\Transpile\MethodContext.cs" />
<Compile Include="Managers\PatchManager\Transpile\MethodTranspiler.cs" /> <Compile Include="Managers\PatchManager\Transpile\MethodTranspiler.cs" />
<Compile Include="Patches\GameStatePatchShim.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SaveGameStatus.cs" /> <Compile Include="SaveGameStatus.cs" />
<Compile Include="Collections\KeyTree.cs" /> <Compile Include="Collections\KeyTree.cs" />

View File

@@ -26,6 +26,7 @@ using Torch.Event;
using Torch.Managers; using Torch.Managers;
using Torch.Managers.ChatManager; using Torch.Managers.ChatManager;
using Torch.Managers.PatchManager; using Torch.Managers.PatchManager;
using Torch.Patches;
using Torch.Utils; using Torch.Utils;
using Torch.Session; using Torch.Session;
using VRage.Collections; using VRage.Collections;
@@ -46,10 +47,12 @@ namespace Torch
{ {
static TorchBase() static TorchBase()
{ {
RuntimeHelpers.RunClassConstructor(typeof(ReflectedManager).TypeHandle);
PatchManager.AddPatchShim(typeof(GameStatePatchShim));
PatchManager.CommitInternal();
RegisterCoreAssembly(typeof(ITorchBase).Assembly); RegisterCoreAssembly(typeof(ITorchBase).Assembly);
RegisterCoreAssembly(typeof(TorchBase).Assembly); RegisterCoreAssembly(typeof(TorchBase).Assembly);
// We can safely never detach this since we don't reload assemblies. RegisterCoreAssembly(Assembly.GetEntryAssembly());
new ReflectedManager().Attach();
} }
/// <summary> /// <summary>
@@ -124,21 +127,11 @@ namespace Torch
sessionManager.AddFactory((x) => new EntityManager(this)); sessionManager.AddFactory((x) => new EntityManager(this));
Managers.AddManager(sessionManager); Managers.AddManager(sessionManager);
var patcher = new PatchManager(this); Managers.AddManager(new PatchManager(this));
GameStateInjector.Inject(patcher.AcquireContext());
Managers.AddManager(patcher);
// Managers.AddManager(new KeenLogManager(this));
Managers.AddManager(new FilesystemManager(this)); Managers.AddManager(new FilesystemManager(this));
Managers.AddManager(new UpdateManager(this)); Managers.AddManager(new UpdateManager(this));
Managers.AddManager(new EventManager(this)); Managers.AddManager(new EventManager(this));
Managers.AddManager(Plugins); Managers.AddManager(Plugins);
GameStateChanged += (game, state) =>
{
if (state != TorchGameState.Created)
return;
// At this point flush the patches; it's safe.
patcher.Commit();
};
TorchAPI.Instance = this; TorchAPI.Instance = this;
} }
@@ -395,7 +388,7 @@ namespace Torch
/// <inheritdoc /> /// <inheritdoc />
public virtual void Update() public virtual void Update()
{ {
GetManager<IPluginManager>().UpdatePlugins(); Managers.GetManager<IPluginManager>().UpdatePlugins();
} }
@@ -405,7 +398,7 @@ namespace Torch
public TorchGameState GameState public TorchGameState GameState
{ {
get => _gameState; get => _gameState;
private set internal set
{ {
_gameState = value; _gameState = value;
GameStateChanged?.Invoke(MySandboxGame.Static, _gameState); GameStateChanged?.Invoke(MySandboxGame.Static, _gameState);
@@ -415,81 +408,36 @@ namespace Torch
/// <inheritdoc/> /// <inheritdoc/>
public event TorchGameStateChangedDel GameStateChanged; public event TorchGameStateChangedDel GameStateChanged;
#region GameStateInjecting private static readonly HashSet<Assembly> _registeredCoreAssemblies = new HashSet<Assembly>();
private static class GameStateInjector /// <summary>
/// Registers a core (Torch) assembly with the system, including its
/// <see cref="EventManager"/> shims, <see cref="PatchManager"/> shims, and <see cref="ReflectedManager"/> components.
/// </summary>
/// <param name="asm">Assembly to register</param>
internal static void RegisterCoreAssembly(Assembly asm)
{ {
#pragma warning disable 649 lock (_registeredCoreAssemblies)
[ReflectedMethodInfo(typeof(MySandboxGame), nameof(MySandboxGame.Dispose))] if (_registeredCoreAssemblies.Add(asm))
private static MethodInfo _sandboxGameDispose;
[ReflectedMethodInfo(typeof(MySandboxGame), "Initialize")]
private static MethodInfo _sandboxGameInit;
#pragma warning restore 649
internal static void Inject(PatchContext target)
{
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)));
target.GetPattern(ctor).Suffixes.Add(MethodRef(nameof(SuffixConstructor)));
target.GetPattern(_sandboxGameInit).Prefixes.Add(MethodRef(nameof(PrefixInit)));
target.GetPattern(_sandboxGameInit).Suffixes.Add(MethodRef(nameof(SuffixInit)));
target.GetPattern(_sandboxGameDispose).Prefixes.Add(MethodRef(nameof(PrefixDispose)));
target.GetPattern(_sandboxGameDispose).Suffixes.Add(MethodRef(nameof(SuffixDispose)));
}
private static MethodInfo MethodRef(string name)
{
return typeof(GameStateInjector).GetMethod(name,
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
}
private static void PrefixConstructor()
{
if (Instance is TorchBase tb)
tb.GameState = TorchGameState.Creating;
}
private static void SuffixConstructor()
{
if (Instance is TorchBase tb)
tb.GameState = TorchGameState.Created;
}
private static void PrefixInit()
{
if (Instance is TorchBase tb)
tb.GameState = TorchGameState.Loading;
}
private static void SuffixInit()
{
if (Instance is TorchBase tb)
tb.GameState = TorchGameState.Loaded;
}
private static void PrefixDispose()
{
if (Instance is TorchBase tb)
tb.GameState = TorchGameState.Unloading;
}
private static void SuffixDispose()
{
if (Instance is TorchBase tb)
tb.GameState = TorchGameState.Unloaded;
}
}
#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); EventManager.AddDispatchShims(asm);
PatchManager.AddPatchShims(asm); PatchManager.AddPatchShims(asm);
ReflectedManager.Process(asm);
}
}
private static readonly HashSet<Assembly> _registeredAuxAssemblies = new HashSet<Assembly>();
/// <summary>
/// Registers an auxillary (plugin) assembly with the system, including its
/// <see cref="ReflectedManager"/> related components.
/// </summary>
/// <param name="asm">Assembly to register</param>
internal static void RegisterAuxAssembly(Assembly asm)
{
lock (_registeredAuxAssemblies)
if (_registeredAuxAssemblies.Add(asm))
{
ReflectedManager.Process(asm);
} }
} }
} }

View File

@@ -398,43 +398,20 @@ namespace Torch.Utils
/// <summary> /// <summary>
/// Automatically calls <see cref="ReflectedManager.Process(Assembly)"/> for every assembly already loaded, and every assembly that is loaded in the future. /// Automatically calls <see cref="ReflectedManager.Process(Assembly)"/> for every assembly already loaded, and every assembly that is loaded in the future.
/// </summary> /// </summary>
public class ReflectedManager public static class ReflectedManager
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger(); private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private static readonly string[] _namespaceBlacklist = new[] { private static readonly string[] _namespaceBlacklist = new[] {
"System", "VRage", "Sandbox", "SpaceEngineers", "Microsoft" "System", "VRage", "Sandbox", "SpaceEngineers", "Microsoft"
}; };
/// <summary>
/// Registers the assembly load event and loads every already existing assembly.
/// </summary>
public void Attach()
{
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
Process(asm);
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
}
/// <summary>
/// Deregisters the assembly load event
/// </summary>
public void Detach()
{
AppDomain.CurrentDomain.AssemblyLoad -= CurrentDomain_AssemblyLoad;
}
private void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Process(args.LoadedAssembly);
}
private static readonly HashSet<Type> _processedTypes = new HashSet<Type>(); private static readonly HashSet<Type> _processedTypes = new HashSet<Type>();
/// <summary> /// <summary>
/// Ensures all reflected fields and methods contained in the given type are initialized /// Ensures all reflected fields and methods contained in the given type are initialized
/// </summary> /// </summary>
/// <param name="t">Type to process</param> /// <param name="t">Type to process</param>
public static void Process(Type t) internal static void Process(Type t)
{ {
if (_processedTypes.Add(t)) if (_processedTypes.Add(t))
{ {
@@ -467,7 +444,7 @@ namespace Torch.Utils
/// Ensures all types in the given assembly are initialized using <see cref="Process(Type)"/> /// Ensures all types in the given assembly are initialized using <see cref="Process(Type)"/>
/// </summary> /// </summary>
/// <param name="asm">Assembly to process</param> /// <param name="asm">Assembly to process</param>
public static void Process(Assembly asm) internal static void Process(Assembly asm)
{ {
foreach (Type type in asm.GetTypes()) foreach (Type type in asm.GetTypes())
Process(type); Process(type);