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:
@@ -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" />
|
||||||
|
@@ -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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +53,7 @@ namespace Torch.Event
|
|||||||
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it has no event lists.");
|
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it has no event lists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all event handler methods declared by the given type and its base types.
|
/// Gets all event handler methods declared by the given type and its base types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -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)
|
||||||
|
@@ -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>
|
||||||
|
75
Torch/Patches/GameStatePatchShim.cs
Normal file
75
Torch/Patches/GameStatePatchShim.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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" />
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user