Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HolyDLL v1.1.1
holy.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using IL.RoR2; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using MonoMod.Cil; using MonoMod.RuntimeDetour; using MonoMod.RuntimeDetour.HookGen; using MonoMod.Utils; using R2API; using RoR2; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("holy")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3203c2dc68c5c9326450bc9f458d2bc2712a64fe")] [assembly: AssemblyProduct("holy")] [assembly: AssemblyTitle("holy")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace HolyHolyHoly { [BepInPlugin("xyz.yekoc.Holy", "HolyHolyHOLY", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class HolyPlugin : BaseUnityPlugin { private sealed class ReferenceComparer : IEqualityComparer<CharacterBody> { public bool Equals(CharacterBody a, CharacterBody b) { return a == b; } public int GetHashCode(CharacterBody b) { return RuntimeHelpers.GetHashCode(b); } } [CompilerGenerated] private static class <>O { public static Manipulator <0>__Batchify; public static Manipulator <1>__Manipulate; } public const string GUID = "xyz.yekoc.Holy"; private static HolyPlugin instance; private static MethodInfo recalculateStats; private static readonly Dictionary<CharacterBody, HolyData> bodies = new Dictionary<CharacterBody, HolyData>(new ReferenceComparer()); private static readonly List<CharacterBody> dirty = new List<CharacterBody>(); private ConfigEntry<bool> enabledConfig; private ConfigEntry<int> minimumBatch; private ConfigEntry<int> maxThreads; private ILHook recalcHook; private ILHook sprintHook; private MethodBase workerTarget; private Action<CharacterBody> worker; private bool ready; private bool serialFallback; private int windowBatches; private int windowBodies; private int windowBails; private readonly Dictionary<int, int> windowBailSites = new Dictionary<int, int>(); private readonly HashSet<string> loggedErrors = new HashSet<string>(); private void Awake() { //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected O, but got Unknown instance = this; enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Recalculate character stats on worker threads. Turning this off makes the mod do nothing."); minimumBatch = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Minimum Batch Size", 4, "Batches smaller than this are recalculated on the main thread, where thread overhead would outweigh the gain."); maxThreads = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Max Threads", 0, "Upper limit on threads used per batch, 0 uses every core."); ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "Defer Foreign Calls", true, "Let other mods' RecalculateStats IL hooks/Harmony patches that touch main-thread-only code run right after the parallel part. When off, bodies reaching such code are recalculated on the main thread instead (exact, but slower)."); ConfigEntry<bool> val2 = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log Analysis", false, "Log the verdict for every call inside RecalculateStats."); if (!enabledConfig.Value) { return; } if (!HookIntrospection.Available) { ((BaseUnityPlugin)this).Logger.LogError((object)"Unrecognized MonoMod version, can't verify other mods' hooks. Staying disabled."); return; } recalculateStats = typeof(CharacterBody).GetMethod("RecalculateStats", BindingFlags.Instance | BindingFlags.Public); RsapiCompat.Init(((BaseUnityPlugin)this).Logger); RecalcPatcher.Init(((BaseUnityPlugin)this).Logger); RecalcPatcher.DeferForeignCalls = val.Value; RecalcPatcher.LogEverything = val2.Value; object obj = <>O.<0>__Batchify; if (obj == null) { Manipulator val3 = Batchify; <>O.<0>__Batchify = val3; obj = (object)val3; } CharacterBody.FixedUpdate += (Manipulator)obj; object obj2 = <>O.<0>__Batchify; if (obj2 == null) { Manipulator val4 = Batchify; <>O.<0>__Batchify = val4; obj2 = (object)val4; } CharacterMaster.OnBodyStart += (Manipulator)obj2; MethodInfo? setMethod = typeof(CharacterBody).GetProperty("isSprinting").GetSetMethod(); object obj3 = <>O.<0>__Batchify; if (obj3 == null) { Manipulator val5 = Batchify; <>O.<0>__Batchify = val5; obj3 = (object)val5; } sprintHook = new ILHook((MethodBase)setMethod, (Manipulator)obj3); RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(InstallRecalculateHook)); Run.onRunStartGlobal += delegate { Refresh(); }; Stage.onStageStartGlobal += delegate { bodies.Clear(); dirty.Clear(); }; ((MonoBehaviour)this).StartCoroutine(BatchLoop()); } private void OnDestroy() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown object obj = <>O.<0>__Batchify; if (obj == null) { Manipulator val = Batchify; <>O.<0>__Batchify = val; obj = (object)val; } CharacterBody.FixedUpdate -= (Manipulator)obj; object obj2 = <>O.<0>__Batchify; if (obj2 == null) { Manipulator val2 = Batchify; <>O.<0>__Batchify = val2; obj2 = (object)val2; } CharacterMaster.OnBodyStart -= (Manipulator)obj2; ILHook obj3 = sprintHook; if (obj3 != null) { obj3.Dispose(); } ILHook obj4 = recalcHook; if (obj4 != null) { obj4.Dispose(); } RsapiCompat.Unload(); ready = false; } private void InstallRecalculateHook() { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown ILHookConfig val = new ILHookConfig { ID = "xyz.yekoc.Holy", After = new string[1] { "*" } }; MethodInfo methodInfo = recalculateStats; object obj = <>O.<1>__Manipulate; if (obj == null) { Manipulator val2 = RecalcPatcher.Manipulate; <>O.<1>__Manipulate = val2; obj = (object)val2; } recalcHook = new ILHook((MethodBase)methodInfo, (Manipulator)obj, ref val); UpdateWorker(); } private void Refresh() { if (recalcHook != null) { serialFallback = false; windowBatches = (windowBodies = (windowBails = 0)); windowBailSites.Clear(); RecalcPatcher.BeginSession(); recalcHook.Undo(); recalcHook.Apply(); UpdateWorker(); ((BaseUnityPlugin)this).Logger.LogInfo((object)(ready ? $"RecalculateStats ready for parallel execution ({RecalcPatcher.DeferredSites} deferred calls, {RecalcPatcher.BailSites} main-thread fallbacks)." : "RecalculateStats will stay on the main thread this run, see earlier messages.")); } } private void UpdateWorker() { MethodBase iLTarget = HookIntrospection.GetILTarget(recalcHook); if (iLTarget != workerTarget) { workerTarget = iLTarget; worker = null; try { if (iLTarget is DynamicMethod dynamicMethod) { worker = (Action<CharacterBody>)dynamicMethod.CreateDelegate(typeof(Action<CharacterBody>)); } else if (iLTarget is MethodInfo method) { worker = (Action<CharacterBody>)Delegate.CreateDelegate(typeof(Action<CharacterBody>), method); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Couldn't bind the patched RecalculateStats: " + ex)); } } ready = worker != null && RecalcPatcher.Healthy; } private static void Batchify(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); MethodInfo method = typeof(HolyPlugin).GetMethod("Enqueue"); while (val.TryGotoNext(new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt(x, (MethodBase)recalculateStats) })) { val.Next.OpCode = OpCodes.Call; val.Next.Operand = il.Import((MethodBase)method); int index = val.Index; val.Index = index + 1; } } public static void Enqueue(CharacterBody body) { if ((Object)(object)instance == (Object)null || !instance.ready || ParallelRuntime.InParallel || !Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)Run.instance)) { body.RecalculateStats(); return; } if (!bodies.TryGetValue(body, out var value)) { value = (bodies[body] = new HolyData()); } if (!value.queued) { value.queued = true; dirty.Add(body); } body.statsDirty = false; } private IEnumerator BatchLoop() { WaitForFixedUpdate wait = new WaitForFixedUpdate(); while (true) { yield return wait; try { ProcessBatch(); } catch (Exception ex) { Exception e = ex; ((BaseUnityPlugin)this).Logger.LogError((object)e); } } } private void ProcessBatch() { //IL_0175: Unknown result type (might be due to invalid IL or missing references) if (dirty.Count == 0) { return; } List<CharacterBody> batch = new List<CharacterBody>(dirty.Count); foreach (CharacterBody item in dirty) { if (bodies.TryGetValue(item, out var value)) { value.queued = false; } if (Object.op_Implicit((Object)(object)item)) { batch.Add(item); } else { bodies.Remove(item); } } dirty.Clear(); if (batch.Count == 0) { return; } UpdateWorker(); RecalcPatcher.RunStaticConstructors(); if (!ready || serialFallback || !Object.op_Implicit((Object)(object)Run.instance) || batch.Count < minimumBatch.Value) { foreach (CharacterBody item2 in batch) { RecalculateOnMainThread(item2, null); } return; } HolyData[] datas = new HolyData[batch.Count]; ParallelRuntime.gravity = Physics.gravity.y; for (int i = 0; i < batch.Count; i++) { CharacterBody val = batch[i]; HolyData holyData = (datas[i] = bodies[val]); holyData.needsSync = false; holyData.bailSite = 0; holyData.error = null; holyData.deferred.Clear(); holyData.prevLevel = val.level; holyData.prevMaxHealth = val.maxHealth; holyData.prevMaxShield = val.maxShield; try { _ = val.master; RsapiCompat.Prepare(val, holyData); } catch (Exception error) { holyData.needsSync = true; holyData.error = error; } } Action<CharacterBody> run = worker; ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = ((maxThreads.Value > 0) ? maxThreads.Value : (-1)) }; Parallel.For(0, batch.Count, parallelOptions, delegate(int num3) { HolyData holyData3 = datas[num3]; if (holyData3.needsSync) { return; } ParallelRuntime.Enter(holyData3); try { run(batch[num3]); } catch (BailException) { holyData3.needsSync = true; } catch (Exception error2) { holyData3.needsSync = true; holyData3.error = error2; } finally { ParallelRuntime.Exit(); } }); bool flag = HookIntrospection.DetourCount(recalculateStats) > 1; int num = 0; for (int num2 = 0; num2 < batch.Count; num2++) { CharacterBody val2 = batch[num2]; HolyData holyData2 = datas[num2]; if (holyData2.needsSync) { num++; windowBailSites.TryGetValue(holyData2.bailSite, out var value2); windowBailSites[holyData2.bailSite] = value2 + 1; RecalculateOnMainThread(val2, holyData2); continue; } try { ParallelRuntime.RunDeferred(val2, holyData2); if (flag) { ParallelRuntime.skipTarget = val2; try { val2.RecalculateStats(); } finally { ParallelRuntime.skipTarget = null; } } } catch (Exception e) { LogOnce("Error while finishing a parallel stat recalculation", e); } } TrackFallbacks(batch.Count, num); } private void RecalculateOnMainThread(CharacterBody body, HolyData abandoned) { if (abandoned != null) { abandoned.deferred.Clear(); body.level = abandoned.prevLevel; body.maxHealth = abandoned.prevMaxHealth; body.maxShield = abandoned.prevMaxShield; } try { body.RecalculateStats(); if (abandoned?.error != null) { LogOnce("RecalculateStats failed on a worker thread but worked on the main thread", abandoned.error); } } catch (Exception e) { LogOnce("RecalculateStats threw", e); } } private void TrackFallbacks(int count, int bails) { windowBatches++; windowBodies += count; windowBails += bails; if (windowBatches < 32) { return; } if (windowBails * 2 > windowBodies) { serialFallback = true; IEnumerable<string> values = from p in windowBailSites.OrderByDescending((KeyValuePair<int, int> p) => p.Value).Take(3) select $"{RecalcPatcher.BailSiteName(p.Key)} ({p.Value})"; ((BaseUnityPlugin)this).Logger.LogWarning((object)string.Format("{0} of {1} recent stat recalculations needed the main thread, running them serially until the next run. Most common causes: {2}", windowBails, windowBodies, string.Join("; ", values))); } windowBatches = (windowBodies = (windowBails = 0)); windowBailSites.Clear(); } private void LogOnce(string context, Exception e) { string item = context + e.GetType().FullName + e.Message; if (loggedErrors.Count < 64 && loggedErrors.Add(item)) { ((BaseUnityPlugin)this).Logger.LogError((object)(context + ":\n" + e)); } } } internal sealed class MethodComparer : IEqualityComparer<MethodBase> { public static readonly MethodComparer Instance = new MethodComparer(); public bool Equals(MethodBase a, MethodBase b) { if ((object)a == b) { return true; } if (a == null || b == null) { return false; } try { return a.MethodHandle == b.MethodHandle; } catch (Exception) { return false; } } public int GetHashCode(MethodBase m) { try { return m.MethodHandle.GetHashCode(); } catch (Exception) { return m.GetHashCode(); } } } internal static class HookIntrospection { private sealed class HandleIndex { public int count = -1; public readonly Dictionary<RuntimeMethodHandle, object> keys = new Dictionary<RuntimeMethodHandle, object>(); } private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly FieldInfo detourMap = typeof(Detour).GetField("_DetourMap", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ilHookMap = typeof(ILHook).GetField("_Map", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo endpointMap = typeof(HookEndpointManager).GetField("HookEndpointMap", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); private static readonly PropertyInfo ilHookCtx = typeof(ILHook).GetProperty("_Ctx", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); internal static bool InspectHarmony = true; private static readonly Dictionary<FieldInfo, HandleIndex> indices = new Dictionary<FieldInfo, HandleIndex>(); public static bool Available => detourMap != null && ilHookMap != null && endpointMap != null && ilHookCtx != null; public static MethodBase GetILTarget(ILHook hook) { object value = ilHookCtx.GetValue(hook); if (value == null) { return null; } object? obj = value.GetType().GetField("Detour", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(value); return ((Detour)(((obj is Detour) ? obj : null)?)).Target; } public static int DetourCount(MethodBase m) { return (Lookup(detourMap, m) is ICollection collection) ? collection.Count : 0; } public static string CheckHooks(MethodBase m, List<MethodBase> extras) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown int num = DetourCount(m); int num2 = 0; object obj = Lookup(ilHookMap, m); if (obj != null && obj.GetType().GetField("Chain", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) is IEnumerable enumerable) { bool flag = false; foreach (ILHook item in enumerable) { ILHook val = item; if (val.IsApplied) { flag = true; Type type = ((Delegate)(object)val.Manipulator)?.Method?.DeclaringType; if (!(type?.FullName == "HarmonyLib.Public.Patching.ManagedMethodPatcher") && !(type?.Assembly == typeof(HookIntrospection).Assembly)) { return "IL-hooked by " + Owner((Delegate)(object)val.Manipulator); } } } if (flag) { num2++; } } object obj2 = Lookup(endpointMap, m); if (obj2 != null && obj2.GetType().GetField("HookMap", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2) is IDictionary dictionary) { foreach (DictionaryEntry item2 in dictionary) { if (!(item2.Key is Delegate obj3) || !(item2.Value is IEnumerable enumerable2)) { continue; } foreach (object item3 in enumerable2) { if (item3 is Hook) { num2++; Delegate[] invocationList = obj3.GetInvocationList(); foreach (Delegate obj4 in invocationList) { extras.Add(obj4.Method); } } } } } if (num > num2) { return "detoured by a hook that can't be inspected"; } Patches val2 = (InspectHarmony ? Harmony.GetPatchInfo(m) : null); if (val2 != null) { if (val2.Transpilers.Count > 0) { return "transpiled by " + string.Join(", ", val2.Transpilers.Select((Patch p) => p.owner)); } extras.AddRange(((IEnumerable<Patch>)val2.Prefixes).Select((Func<Patch, MethodBase>)((Patch p) => p.PatchMethod))); extras.AddRange(((IEnumerable<Patch>)val2.Postfixes).Select((Func<Patch, MethodBase>)((Patch p) => p.PatchMethod))); extras.AddRange(((IEnumerable<Patch>)val2.Finalizers).Select((Func<Patch, MethodBase>)((Patch p) => p.PatchMethod))); } return null; } public static string Owner(Delegate d) { Type type = d?.Method?.DeclaringType; return (type == null) ? "unknown" : (type.Assembly.GetName().Name + " (" + type.FullName + ")"); } public static void BeginSession() { indices.Clear(); } private static object Lookup(FieldInfo mapField, MethodBase m) { if (!(mapField.GetValue(null) is IDictionary dictionary)) { return null; } lock (dictionary) { if (dictionary.Contains(m)) { return dictionary[m]; } if (!indices.TryGetValue(mapField, out var value)) { value = (indices[mapField] = new HandleIndex()); } if (value.count != dictionary.Count) { value.keys.Clear(); foreach (DictionaryEntry item in dictionary) { try { if (item.Key is MethodBase methodBase) { value.keys[methodBase.MethodHandle] = methodBase; } } catch (Exception) { } } value.count = dictionary.Count; } try { if (value.keys.TryGetValue(m.MethodHandle, out var value2)) { return dictionary[value2]; } } catch (Exception) { } } return null; } } internal sealed class NativeAnalyzer { public sealed class Verdict { public static readonly Verdict Safe = new Verdict(isUnsafe: false, null); public readonly bool Unsafe; public readonly string Reason; public Verdict(bool isUnsafe, string reason) { Unsafe = isUnsafe; Reason = reason; } public static Verdict No(string reason) { return new Verdict(isUnsafe: true, reason); } } private sealed class StaticWriteException : Exception { public StaticWriteException(FieldInfo f) : base("writes shared static " + f.DeclaringType?.Name + "." + f.Name) { } } public Func<MethodBase, Verdict> Override; public Func<MethodBase, IEnumerable<MethodBase>> ExtraTargets; public Action<Type> OnTypeTouched; public int MaxDepth = 48; private readonly Dictionary<string, Verdict> memo = new Dictionary<string, Verdict>(); private readonly Dictionary<string, int> onStack = new Dictionary<string, int>(); private static OpCode[] oneByte; private static OpCode[] twoByte; private static OpCode[] OneByte { get { if (oneByte == null) { BuildTables(); } return oneByte; } } private static OpCode[] TwoByte { get { if (twoByte == null) { BuildTables(); } return twoByte; } } public void Clear() { memo.Clear(); onStack.Clear(); } public Verdict Analyze(MethodBase method) { onStack.Clear(); int low; return Visit(method, 0, out low); } private Verdict Visit(MethodBase m, int depth, out int low) { low = int.MaxValue; string key = Key(m); if (memo.TryGetValue(key, out var value)) { return value; } if (onStack.TryGetValue(key, out var value2)) { low = value2; return Verdict.Safe; } if (depth > MaxDepth) { Verdict verdict = Verdict.No("call chain too deep to verify at " + Describe(m)); memo[key] = verdict; return verdict; } Verdict verdict2 = Classify(m); if (verdict2 != null) { memo[key] = verdict2; return verdict2; } onStack[key] = depth; Verdict verdict3 = Verdict.Safe; try { foreach (MethodBase item in Callees(m)) { int low2; Verdict verdict4 = Visit(item, depth + 1, out low2); if (low2 < low) { low = low2; } if (verdict4.Unsafe) { verdict3 = Verdict.No(Describe(m) + " -> " + verdict4.Reason); break; } } if (!verdict3.Unsafe && ExtraTargets != null) { foreach (MethodBase item2 in ExtraTargets(m)) { if (!(item2 == null)) { int low3; Verdict verdict5 = Visit(item2, depth + 1, out low3); if (low3 < low) { low = low3; } if (verdict5.Unsafe) { verdict3 = Verdict.No(Describe(m) + " => " + verdict5.Reason); break; } } } } } catch (StaticWriteException ex) { verdict3 = Verdict.No(Describe(m) + " -> " + ex.Message); } catch (Exception ex2) { verdict3 = Verdict.No("could not analyze " + Describe(m) + " (" + ex2.GetType().Name + ": " + ex2.Message + ")"); } onStack.Remove(key); if (low >= depth) { low = int.MaxValue; } if (verdict3.Unsafe || low == int.MaxValue) { memo[key] = verdict3; } return verdict3; } private Verdict Classify(MethodBase m) { Verdict verdict = Override?.Invoke(m); if (verdict != null) { return verdict; } Type declaringType = m.DeclaringType; string asm = declaringType?.Assembly.GetName().Name ?? ""; string type = declaringType?.FullName ?? ""; if (IsWhitelisted(type, m.Name)) { return Verdict.Safe; } bool flag = IsBcl(asm); MethodImplAttributes methodImplementationFlags = m.MethodImplementationFlags; if ((methodImplementationFlags & MethodImplAttributes.InternalCall) != MethodImplAttributes.IL || (m.Attributes & MethodAttributes.PinvokeImpl) != MethodAttributes.PrivateScope) { return flag ? Verdict.Safe : Verdict.No("native " + Describe(m)); } if ((methodImplementationFlags & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.CodeTypeMask) { if (!m.IsConstructor && m.Name != "Invoke") { return Verdict.Safe; } return m.IsConstructor ? Verdict.Safe : Verdict.No("opaque delegate call " + Describe(m)); } if (flag) { return Verdict.Safe; } if (m.IsAbstract) { return Verdict.Safe; } return null; } public static bool IsBcl(string asm) { int result; switch (asm) { default: if (!asm.StartsWith("System.", StringComparison.Ordinal)) { result = ((asm == "Microsoft.CSharp") ? 1 : 0); break; } goto case "mscorlib"; case "mscorlib": case "netstandard": case "System": result = 1; break; } return (byte)result != 0; } public static bool IsUnity(string asm) { return asm.StartsWith("UnityEngine", StringComparison.Ordinal) || asm.StartsWith("Unity.", StringComparison.Ordinal); } private static bool IsWhitelisted(string type, string name) { switch (type) { case "UnityEngine.Mathf": case "UnityEngine.Vector2": case "UnityEngine.Vector3": case "UnityEngine.Vector4": case "UnityEngine.Vector2Int": case "UnityEngine.Vector3Int": case "UnityEngine.Quaternion": case "UnityEngine.Matrix4x4": case "UnityEngine.Color": case "UnityEngine.Color32": case "UnityEngine.Bounds": case "UnityEngine.Rect": return true; case "UnityEngine.Debug": return name.StartsWith("Log", StringComparison.Ordinal) || name.StartsWith("Assert", StringComparison.Ordinal); case "UnityEngine.Object": { int result; switch (name) { default: result = ((name == "GetHashCode") ? 1 : 0); break; case "op_Implicit": case "op_Equality": case "op_Inequality": case "CompareBaseObjects": case "IsNativeObjectAlive": case "GetCachedPtr": case "Equals": result = 1; break; } return (byte)result != 0; } case "RoR2.ItemDef": return name == "get_itemIndex"; case "RoR2.EquipmentDef": return name == "get_equipmentIndex"; case "RoR2.CharacterBody": return name == "get_master"; default: return false; } } private IEnumerable<MethodBase> Callees(MethodBase m) { MethodBody body = m.GetMethodBody(); if (body == null) { yield break; } byte[] il = body.GetILAsByteArray(); if (il == null) { yield break; } Module module = m.Module; Type[] typeArgs = ((m.DeclaringType != null && m.DeclaringType.IsGenericType) ? m.DeclaringType.GetGenericArguments() : null); Type[] methodArgs = ((m.IsGenericMethod && !m.IsConstructor) ? m.GetGenericArguments() : null); int operandSize; for (int i = 0; i < il.Length; i += operandSize) { byte b = il[i++]; OpCode op = ((b != 254) ? OneByte[b] : TwoByte[il[i++]]); operandSize = OperandSize(op.OperandType, il, i); if (op.OperandType == OperandType.InlineMethod) { int token = BitConverter.ToInt32(il, i); MethodBase target = null; try { target = module.ResolveMethod(token, typeArgs, methodArgs); } catch (Exception) { } if (target == null) { throw new InvalidOperationException("unresolvable method token 0x" + token.ToString("X8")); } if (target.DeclaringType != null) { OnTypeTouched?.Invoke(target.DeclaringType); } yield return target; } else { if (op.OperandType != OperandType.InlineField) { continue; } int token2 = BitConverter.ToInt32(il, i); FieldInfo field = null; try { field = module.ResolveField(token2, typeArgs, methodArgs); } catch (Exception) { } if (!(field != null) || !field.IsStatic) { continue; } if (field.DeclaringType != null) { OnTypeTouched?.Invoke(field.DeclaringType); } if ((op.Value == OpCodes.Stsfld.Value || (op.Value == OpCodes.Ldsflda.Value && !field.IsInitOnly)) && field.GetCustomAttributes(typeof(ThreadStaticAttribute), inherit: false).Length == 0) { string asm = field.DeclaringType?.Assembly.GetName().Name ?? ""; if (!IsBcl(asm)) { throw new StaticWriteException(field); } } } } } private static int OperandSize(OperandType t, byte[] il, int at) { switch (t) { case OperandType.InlineNone: return 0; case OperandType.ShortInlineBrTarget: case OperandType.ShortInlineI: case OperandType.ShortInlineVar: return 1; case OperandType.InlineVar: return 2; case OperandType.InlineI8: case OperandType.InlineR: return 8; case OperandType.InlineSwitch: return 4 + 4 * BitConverter.ToInt32(il, at); default: return 4; } } private static void BuildTables() { OpCode[] array = new OpCode[256]; OpCode[] array2 = new OpCode[256]; FieldInfo[] fields = typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.GetValue(null) is OpCode opCode) { ushort num = (ushort)opCode.Value; if (opCode.Size == 1) { array[num] = opCode; } else if (num >> 8 == 254) { array2[num & 0xFF] = opCode; } } } twoByte = array2; oneByte = array; } public static string Describe(MethodBase m) { return (m.DeclaringType?.Name ?? "?") + "." + m.Name; } private static string Key(MethodBase m) { string text = m.Module.ModuleVersionId.ToString("N") + ":" + m.MetadataToken.ToString("X"); if (m.DeclaringType != null && m.DeclaringType.IsGenericType) { text = text + "<" + string.Join(",", Array.ConvertAll(m.DeclaringType.GetGenericArguments(), (Type t) => t.FullName ?? t.Name)) + ">"; } if (m.IsGenericMethod && !m.IsConstructor) { text = text + "[" + string.Join(",", Array.ConvertAll(m.GetGenericArguments(), (Type t) => t.FullName ?? t.Name)) + "]"; } return text; } } internal sealed class HolyData { public enum Kind { Health, Level, Scale, Visuals, Event, Method, Delegate } public struct Deferred { public Kind kind; public float a; public float b; public object o; public object[] args; } public bool queued; public float prevLevel; public float prevMaxHealth; public float prevMaxShield; public object statMods; public object customStats; public readonly List<Deferred> deferred = new List<Deferred>(); public bool needsSync; public int bailSite; public Exception error; public void Add(Kind kind, float a = 0f, float b = 0f, object o = null, object[] args = null) { deferred.Add(new Deferred { kind = kind, a = a, b = b, o = o, args = args }); } } internal sealed class BailException : Exception { public static readonly BailException Instance = new BailException(); private BailException() : base("HolyHolyHOLY: recalculation moved to the main thread") { } } public static class ParallelRuntime { [ThreadStatic] private static HolyData current; internal static CharacterBody skipTarget; internal static float gravity = -30f; internal static readonly List<MethodBase> deferSites = new List<MethodBase>(); public static bool InParallel => current != null; internal static void Enter(HolyData data) { current = data; } internal static void Exit() { current = null; } public static bool ShouldSkip(CharacterBody body) { if ((Object)(object)skipTarget == (Object)null || skipTarget != body) { return false; } skipTarget = null; return true; } public static void BailIfParallel(int site) { HolyData holyData = current; if (holyData == null) { return; } holyData.needsSync = true; holyData.bailSite = site; throw BailException.Instance; } public static void UpdateVisuals(CharacterBody body) { HolyData holyData = current; if (holyData != null) { holyData.Add(HolyData.Kind.Visuals); } else { body.UpdateAllTemporaryVisualEffects(); } } public static void LevelChanged(CharacterBody body, float oldLevel, float newLevel) { HolyData holyData = current; if (holyData != null) { holyData.Add(HolyData.Kind.Level, oldLevel, newLevel); } else { body.OnCalculatedLevelChanged(oldLevel, newLevel); } } public static void SetDesiredScale(CharacterBody body, float value) { HolyData holyData = current; if (holyData == null) { body.desiredScale = value; } else if (value != body.desiredScale) { holyData.Add(HolyData.Kind.Scale, value); } } public static void DisableSkills(CharacterBody body) { if (current == null) { body.HandleDisableAllSkillsDebuff(); } else if (body.HasBuff(Buffs.DisableAllSkills) != body.allSkillsDisabled) { BailIfParallel(-1); } } public static float CalculateApex(float initialSpeed) { return (current != null) ? Trajectory.CalculateApex(initialSpeed, gravity) : Trajectory.CalculateApex(initialSpeed); } public static void InvokeRecalculateEvent(Action<CharacterBody> handlers, CharacterBody body) { HolyData holyData = current; if (holyData != null) { holyData.Add(HolyData.Kind.Event, 0f, 0f, handlers); } else { handlers(body); } } public static bool HealthGate(CharacterBody body) { HolyData holyData = current; if (holyData == null) { return NetworkServer.active; } holyData.Add(HolyData.Kind.Health); return false; } public static void GetStatMods(CharacterBody body) { if (current == null) { RsapiCompat.GetStatMods(body); } } public static object GetThreadStatMods() { return current.statMods; } public static object GetThreadCustomStats() { return current.customStats; } public static void SetThreadCustomStats(object value) { current.customStats = value; } public static void DeferMethod(object[] stack, int site) { current.Add(HolyData.Kind.Method, 0f, 0f, deferSites[site], stack); } public static void DeferDelegate(object[] stack, bool delegateLast) { current.Add(HolyData.Kind.Delegate, delegateLast ? 1f : 0f, 0f, null, stack); } internal static void RunDeferred(CharacterBody body, HolyData data) { foreach (HolyData.Deferred item in data.deferred) { switch (item.kind) { case HolyData.Kind.Health: ApplyHealthDelta(body, data); break; case HolyData.Kind.Level: body.OnCalculatedLevelChanged(item.a, item.b); break; case HolyData.Kind.Scale: body.desiredScale = item.a; break; case HolyData.Kind.Visuals: body.UpdateAllTemporaryVisualEffects(); break; case HolyData.Kind.Event: ((Action<CharacterBody>)item.o)(body); break; case HolyData.Kind.Method: InvokeDeferredMethod((MethodBase)item.o, item.args); break; case HolyData.Kind.Delegate: InvokeDeferredDelegate(item.args, item.a != 0f); break; } } data.deferred.Clear(); } private static void InvokeDeferredMethod(MethodBase m, object[] stack) { try { if (m.IsStatic || m.IsConstructor) { m.Invoke(null, stack); return; } object[] array = new object[stack.Length - 1]; Array.Copy(stack, 1, array, 0, array.Length); m.Invoke(stack[0], array); } catch (TargetInvocationException ex) when (ex.InnerException != null) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } } private static void InvokeDeferredDelegate(object[] stack, bool delegateLast) { object[] array = new object[stack.Length - 1]; Delegate obj; if (delegateLast) { obj = (Delegate)stack[^1]; Array.Copy(stack, 0, array, 0, array.Length); } else { obj = (Delegate)stack[0]; Array.Copy(stack, 1, array, 0, array.Length); } try { obj.DynamicInvoke(array); } catch (TargetInvocationException ex) when (ex.InnerException != null) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } } private static void ApplyHealthDelta(CharacterBody body, HolyData data) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (!NetworkServer.active) { return; } HealthComponent healthComponent = body.healthComponent; if (Object.op_Implicit((Object)(object)healthComponent)) { float num = body.maxHealth - data.prevMaxHealth; float num2 = body.maxShield - data.prevMaxShield; if (num > 0f) { healthComponent.Heal(num, default(ProcChainMask), false); } else if (healthComponent.health > body.maxHealth) { healthComponent.Networkhealth = Mathf.Max(healthComponent.health + num, body.maxHealth); } if (num2 > 0f) { healthComponent.RechargeShield(num2); } else if (healthComponent.shield > body.maxShield) { healthComponent.Networkshield = Mathf.Max(healthComponent.shield + num2, body.maxShield); } if (healthComponent.regainableHealth > healthComponent.missingHealth) { healthComponent.NetworkregainableHealth = healthComponent.missingHealth; } } } } internal static class RecalcPatcher { private struct ChildType { public Assembly Assembly; public int Token; public string Name; } public static bool DeferForeignCalls = true; public static bool LogEverything; private static readonly List<string> BailSiteNames = new List<string>(); private static ManualLogSource log; private static readonly NativeAnalyzer analyzer = new NativeAnalyzer(); private static readonly Dictionary<MethodBase, List<MethodBase>> hookExtras = new Dictionary<MethodBase, List<MethodBase>>(MethodComparer.Instance); private static readonly HashSet<Type> touchedTypes = new HashSet<Type>(); private static readonly HashSet<string> reported = new HashSet<string>(); private static readonly Dictionary<MethodBase, int> deferSiteIds = new Dictionary<MethodBase, int>(MethodComparer.Instance); private static readonly Dictionary<Type, List<Type>> derivedTypes = new Dictionary<Type, List<Type>>(); private static Dictionary<string, List<ChildType>> childIndex; private static int indexedAssemblyCount; private static Dictionary<MethodBase, MethodInfo> replacements; public static bool Healthy { get; private set; } public static int DeferredSites { get; private set; } public static int BailSites { get; private set; } private static MethodInfo RT(string name) { return typeof(ParallelRuntime).GetMethod(name, BindingFlags.Static | BindingFlags.Public); } public static void Init(ManualLogSource logger) { log = logger; analyzer.Override = Override; analyzer.ExtraTargets = Extras; analyzer.OnTypeTouched = delegate(Type t) { touchedTypes.Add(t); }; Type typeFromHandle = typeof(CharacterBody); Dictionary<MethodBase, MethodInfo> dictionary = new Dictionary<MethodBase, MethodInfo>(MethodComparer.Instance); dictionary.Add(typeFromHandle.GetMethod("UpdateAllTemporaryVisualEffects", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), RT("UpdateVisuals")); dictionary.Add(typeFromHandle.GetMethod("OnCalculatedLevelChanged", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), RT("LevelChanged")); dictionary.Add(typeFromHandle.GetProperty("desiredScale", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetSetMethod(nonPublic: true), RT("SetDesiredScale")); dictionary.Add(typeFromHandle.GetMethod("HandleDisableAllSkillsDebuff", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), RT("DisableSkills")); dictionary.Add(typeof(Trajectory).GetMethod("CalculateApex", new Type[1] { typeof(float) }), RT("CalculateApex")); replacements = dictionary; if (RsapiCompat.GetStatModsMethod != null) { replacements[RsapiCompat.GetStatModsMethod] = RT("GetStatMods"); } } public static void BeginSession() { analyzer.Clear(); hookExtras.Clear(); int num = NonDynamicAssemblyCount(); if (num != indexedAssemblyCount) { indexedAssemblyCount = num; childIndex = null; derivedTypes.Clear(); } HookIntrospection.BeginSession(); } public static void Manipulate(ILContext il) { Healthy = false; try { Rewrite(il); Healthy = true; } catch (Exception ex) { log.LogError((object)("Couldn't prepare RecalculateStats for parallel execution, everything will stay on the main thread.\n" + ex)); } } private static void Rewrite(ILContext il) { //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0715: Unknown result type (might be due to invalid IL or missing references) //IL_05b2: Unknown result type (might be due to invalid IL or missing references) //IL_064c: Unknown result type (might be due to invalid IL or missing references) //IL_05c5: Unknown result type (might be due to invalid IL or missing references) List<Action> list = new List<Action>(); List<KeyValuePair<LogLevel, string>> list2 = new List<KeyValuePair<LogLevel, string>>(); int num = 0; int num2 = 0; Instruction[] array = il.Instrs.ToArray(); list.Add(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) ILCursor val2 = new ILCursor(il); ILLabel val3 = val2.DefineLabel(); val2.Emit(OpCodes.Ldarg_0); val2.Emit(OpCodes.Call, (MethodBase)RT("ShouldSkip")); val2.Emit(OpCodes.Brfalse, (object)val3); val2.Emit(OpCodes.Ret); val2.MarkLabel(val3); }); Instruction val = FindHealthGate(array); if (val == null) { throw new InvalidOperationException("couldn't find the health adjustment block (NetworkServer.active before HealthComponent.Heal)"); } HashSet<Instruction> hashSet = FindRecalculateEventInvokes(array); if (hashSet.Count == 0) { throw new InvalidOperationException("couldn't find the onRecalculateStats invocation"); } Instruction[] array2 = array; foreach (Instruction instr in array2) { if (instr == val) { list.Add(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) instr.OpCode = OpCodes.Ldarg_0; instr.Operand = null; new ILCursor(il).Goto(instr, (MoveType)2, false).Emit(OpCodes.Call, (MethodBase)RT("HealthGate")); }); } else if (hashSet.Contains(instr)) { MethodReference wrapper = il.Import((MethodBase)RT("InvokeRecalculateEvent")); list.Add(delegate { //IL_0011: Unknown result type (might be due to invalid IL or missing references) instr.OpCode = OpCodes.Call; instr.Operand = wrapper; }); } else if (instr.OpCode == OpCodes.Calli) { num2++; list2.Add(Note((LogLevel)4, "an indirect calli can't be verified, bodies reaching it will be recalculated on the main thread")); list.Add(delegate { EmitBail(il, instr, "an indirect calli"); }); } else { if (instr.OpCode != OpCodes.Call && instr.OpCode != OpCodes.Callvirt && instr.OpCode != OpCodes.Newobj) { continue; } object operand = instr.Operand; MethodReference mref = (MethodReference)((operand is MethodReference) ? operand : null); if (mref == null) { continue; } MethodBase methodBase; try { methodBase = ReflectionHelper.ResolveReflection(mref); } catch (Exception) { methodBase = null; } if (methodBase == null) { num2++; list2.Add(Note((LogLevel)4, "couldn't resolve " + ((MemberReference)mref).FullName + ", bodies reaching it will be recalculated on the main thread")); list.Add(delegate { EmitBail(il, instr, ((MemberReference)mref).FullName); }); } else { if (methodBase.DeclaringType?.Assembly == typeof(RecalcPatcher).Assembly) { continue; } if (replacements.TryGetValue(methodBase, out var value)) { if (MethodComparer.Instance.Equals(methodBase, RsapiCompat.GetStatModsMethod) && !RsapiCompat.Active) { throw new InvalidOperationException("R2API.RecalculateStats is hooked in but couldn't be made thread safe"); } MethodReference wrapper2 = il.Import((MethodBase)value); list.Add(delegate { //IL_0011: Unknown result type (might be due to invalid IL or missing references) instr.OpCode = OpCodes.Call; instr.Operand = wrapper2; }); } else { if (IsReferenceBagGetter(methodBase)) { continue; } Delegate bagged = ResolveBaggedDelegate(instr); MethodBase[] array3 = (((object)bagged != null) ? ((IEnumerable<Delegate>)bagged.GetInvocationList()).Select((Func<Delegate, MethodBase>)((Delegate d) => d.Method)).ToArray() : new MethodBase[1] { methodBase }); NativeAnalyzer.Verdict verdict = NativeAnalyzer.Verdict.Safe; MethodBase[] array4 = array3; foreach (MethodBase method in array4) { verdict = analyzer.Analyze(method); if (verdict.Unsafe) { break; } } if (LogEverything) { list2.Add(Note((LogLevel)32, Describe(array3[0]) + ": " + (verdict.Unsafe ? verdict.Reason : "safe"))); } if (!verdict.Unsafe) { continue; } Assembly assembly = array3[0].DeclaringType?.Assembly; bool flag = IsPluginAssembly(assembly); Type[] stack = StackTypes(methodBase); bool flag2 = (object)bagged != null || IsDelegateInvoke(methodBase); bool flag3 = DeferForeignCalls && (flag || (flag2 && (object)bagged == null)) && stack != null && ReturnsVoid(methodBase) && !AtExceptionBoundary(il, instr); string what = (((object)bagged != null) ? ("IL hook delegate " + Describe(array3[0]) + " from " + assembly?.GetName().Name) : (Describe(methodBase) + (flag ? (" from " + assembly?.GetName().Name) : ""))); LogLevel level = (LogLevel)(flag ? 16 : 32); if (flag3) { num++; list2.Add(Note(level, what + " touches main-thread-only code and will run after the parallel part (" + verdict.Reason + ")")); if (flag2) { list.Add(delegate { EmitDeferral(il, instr, stack, delegate(ILCursor c) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) c.Emit(((object)bagged != null) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); c.Emit(OpCodes.Call, (MethodBase)RT("DeferDelegate")); }); }); continue; } int site = DeferSiteId(methodBase); list.Add(delegate { EmitDeferral(il, instr, stack, delegate(ILCursor c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) c.Emit(OpCodes.Ldc_I4, site); c.Emit(OpCodes.Call, (MethodBase)RT("DeferMethod")); }); }); } else { num2++; list2.Add(Note(level, what + " touches main-thread-only code, bodies reaching it will be recalculated on the main thread (" + verdict.Reason + ")")); list.Add(delegate { EmitBail(il, instr, what); }); } } } } } foreach (Action item in list) { item(); } DeferredSites = num; BailSites = num2; foreach (KeyValuePair<LogLevel, string> item2 in list2) { if (reported.Add(item2.Value)) { log.Log(item2.Key, (object)item2.Value); } } } private static KeyValuePair<LogLevel, string> Note(LogLevel level, string text) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return new KeyValuePair<LogLevel, string>(level, text); } private static Instruction FindHealthGate(Instruction[] instrs) { MethodInfo heal = typeof(HealthComponent).GetMethod("Heal"); MethodInfo getMethod = typeof(NetworkServer).GetProperty("active").GetGetMethod(); int num = Array.FindIndex(instrs, (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt(x, (MethodBase)heal)); for (int num2 = num - 1; num2 >= 0; num2--) { if (ILPatternMatchingExt.MatchCall(instrs[num2], (MethodBase)getMethod)) { return instrs[num2]; } } return null; } private static HashSet<Instruction> FindRecalculateEventInvokes(Instruction[] instrs) { HashSet<Instruction> hashSet = new HashSet<Instruction>(); MethodInfo method = typeof(Action<CharacterBody>).GetMethod("Invoke"); for (int i = 0; i < instrs.Length; i++) { if (!ILPatternMatchingExt.MatchLdfld<CharacterBody>(instrs[i], "onRecalculateStats")) { continue; } for (int j = i + 1; j < instrs.Length && j <= i + 12; j++) { if (ILPatternMatchingExt.MatchCallOrCallvirt(instrs[j], (MethodBase)method)) { hashSet.Add(instrs[j]); break; } } } return hashSet; } private static bool IsReferenceBagGetter(MethodBase m) { return m.Name == "Get" && m.DeclaringType != null && m.DeclaringType.IsGenericType && m.DeclaringType.Name.StartsWith("InnerBag", StringComparison.Ordinal); } private static Delegate ResolveBaggedDelegate(Instruction invoke) { Instruction previous = invoke.Previous; Instruction val = ((previous != null) ? previous.Previous : null); if (previous != null && val != null) { object operand = previous.Operand; MethodReference val2 = (MethodReference)((operand is MethodReference) ? operand : null); int num = default(int); if (val2 != null && ILPatternMatchingExt.MatchLdcI4(val, ref num)) { try { MethodBase methodBase = ReflectionHelper.ResolveReflection(val2); if (methodBase == null || !IsReferenceBagGetter(methodBase)) { return null; } return ((MethodInfo)methodBase).Invoke(null, new object[1] { num }) as Delegate; } catch (Exception) { return null; } } } return null; } private static bool IsDelegateInvoke(MethodBase m) { return m.Name == "Invoke" && m.DeclaringType != null && typeof(Delegate).IsAssignableFrom(m.DeclaringType); } private static bool ReturnsVoid(MethodBase m) { return m is MethodInfo methodInfo && methodInfo.ReturnType == typeof(void); } private static Type[] StackTypes(MethodBase m) { if (m.IsConstructor) { return null; } List<Type> list = new List<Type>(); if (!m.IsStatic) { if (m.DeclaringType == null || m.DeclaringType.IsValueType) { return null; } list.Add(m.DeclaringType); } ParameterInfo[] parameters = m.GetParameters(); foreach (ParameterInfo parameterInfo in parameters) { if (parameterInfo.ParameterType.IsByRef || parameterInfo.ParameterType.IsPointer) { return null; } list.Add(parameterInfo.ParameterType); } if (list.Any((Type t) => t.ContainsGenericParameters)) { return null; } return list.ToArray(); } private static bool AtExceptionBoundary(ILContext il, Instruction instr) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) Enumerator<ExceptionHandler> enumerator = il.Body.ExceptionHandlers.GetEnumerator(); try { while (enumerator.MoveNext()) { ExceptionHandler current = enumerator.Current; Instruction[] array = (Instruction[])(object)new Instruction[5] { current.TryStart, current.TryEnd, current.HandlerStart, current.HandlerEnd, current.FilterStart }; foreach (Instruction val in array) { if (val != null && (val == instr || val == instr.Next)) { return true; } } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } return false; } private static void EmitBail(ILContext il, Instruction instr, string site) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) int num = BailSiteNames.IndexOf(site); if (num < 0) { num = BailSiteNames.Count; BailSiteNames.Add(site); } ILCursor val = new ILCursor(il).Goto(instr, (MoveType)0, false); val.MoveAfterLabels(); val.Emit(OpCodes.Ldc_I4, num); val.Emit(OpCodes.Call, (MethodBase)RT("BailIfParallel")); } public static string BailSiteName(int id) { return (id == -1) ? "the Disable All Skills debuff toggling" : ((id >= 0 && id < BailSiteNames.Count) ? BailSiteNames[id] : "unknown"); } private static void EmitDeferral(ILContext il, Instruction call, Type[] stack, Action<ILCursor> defer) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) VariableDefinition[] array = stack.Select(delegate(Type t) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown VariableDefinition val5 = new VariableDefinition(il.Import(t)); il.Body.Variables.Add(val5); return val5; }).ToArray(); ILCursor val = new ILCursor(il).Goto(call, (MoveType)0, false); val.MoveAfterLabels(); for (int num = array.Length - 1; num >= 0; num--) { val.Emit(OpCodes.Stloc, array[num]); } ILLabel val2 = val.DefineLabel(); ILLabel val3 = val.DefineLabel(); val.Emit(OpCodes.Call, (MethodBase)typeof(ParallelRuntime).GetProperty("InParallel").GetGetMethod()); val.Emit(OpCodes.Brfalse, (object)val2); val.Emit(OpCodes.Ldc_I4, array.Length); val.Emit(OpCodes.Newarr, il.Import(typeof(object))); for (int num2 = 0; num2 < array.Length; num2++) { val.Emit(OpCodes.Dup); val.Emit(OpCodes.Ldc_I4, num2); val.Emit(OpCodes.Ldloc, array[num2]); if (stack[num2].IsValueType) { val.Emit(OpCodes.Box, il.Import(stack[num2])); } val.Emit(OpCodes.Stelem_Ref); } defer(val); val.Emit(OpCodes.Br, (object)val3); val.MarkLabel(val2); VariableDefinition[] array2 = array; foreach (VariableDefinition val4 in array2) { val.Emit(OpCodes.Ldloc, val4); } val.Goto(call, (MoveType)2, false); val.MarkLabel(val3); } private static int DeferSiteId(MethodBase m) { if (!deferSiteIds.TryGetValue(m, out var value)) { value = ParallelRuntime.deferSites.Count; ParallelRuntime.deferSites.Add(m); deferSiteIds[m] = value; } return value; } private static bool IsPluginAssembly(Assembly a) { if (a == null || a.IsDynamic || NativeAnalyzer.IsBcl(a.GetName().Name)) { return false; } try { string location = a.Location; if (string.IsNullOrEmpty(location)) { return false; } string directoryName = Path.GetDirectoryName(typeof(CharacterBody).Assembly.Location); return !string.Equals(Path.GetDirectoryName(location), directoryName, StringComparison.OrdinalIgnoreCase); } catch (Exception) { return false; } } private static NativeAnalyzer.Verdict Override(MethodBase m) { if (RsapiCompat.Patched.Contains(m)) { return NativeAnalyzer.Verdict.Safe; } if (m.Name == "Invoke" && m.DeclaringType != null && m.DeclaringType.Name.StartsWith("orig_", StringComparison.Ordinal)) { string? text = m.DeclaringType.Namespace; if (text != null && text.StartsWith("On.", StringComparison.Ordinal)) { return NativeAnalyzer.Verdict.Safe; } } List<MethodBase> list = new List<MethodBase>(); string text2 = HookIntrospection.CheckHooks(m, list); if (text2 != null) { return NativeAnalyzer.Verdict.No(NativeAnalyzer.Describe(m) + " is " + text2); } if (list.Count > 0) { hookExtras[m] = list; } return null; } private static IEnumerable<MethodBase> Extras(MethodBase m) { if (hookExtras.TryGetValue(m, out var hooked)) { foreach (MethodBase item in hooked) { yield return item; } } MethodInfo mi = m as MethodInfo; if ((object)mi == null || !mi.IsVirtual || mi.IsFinal) { yield break; } Type declaring = mi.DeclaringType; if (declaring == null || declaring.IsSealed || declaring.IsInterface) { yield break; } string asm = declaring.Assembly.GetName().Name; if (NativeAnalyzer.IsBcl(asm) || NativeAnalyzer.IsUnity(asm)) { yield break; } MethodInfo baseDef = mi.GetBaseDefinition(); foreach (Type t in Derived(declaring.IsGenericType ? declaring.GetGenericTypeDefinition() : declaring)) { if (t.ContainsGenericParameters) { continue; } MethodInfo[] methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo cand in methods) { if (!(cand.Name != mi.Name) && cand.IsVirtual) { MethodInfo candBase = cand.GetBaseDefinition(); if (candBase.MetadataToken == baseDef.MetadataToken && candBase.Module == baseDef.Module) { yield return cand; } } } } } private static List<Type> Derived(Type declaring) { if (derivedTypes.TryGetValue(declaring, out var value)) { return value; } if (childIndex == null) { IndexHierarchy(); } value = new List<Type>(); Queue<string> queue = new Queue<string>(); HashSet<string> hashSet = new HashSet<string> { CecilName(declaring) }; queue.Enqueue(CecilName(declaring)); while (queue.Count > 0) { if (!childIndex.TryGetValue(queue.Dequeue(), out var value2)) { continue; } foreach (ChildType item in value2) { if (hashSet.Add(item.Name)) { queue.Enqueue(item.Name); } try { Type type = item.Assembly.ManifestModule.ResolveType(item.Token); if (DerivesFrom(type, declaring)) { value.Add(type); } } catch (Exception) { } } } derivedTypes[declaring] = value; return value; } private static void IndexHierarchy() { childIndex = new Dictionary<string, List<ChildType>>(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { string name = assembly.GetName().Name; if (assembly.IsDynamic || NativeAnalyzer.IsBcl(name) || NativeAnalyzer.IsUnity(name) || IndexFromMetadata(assembly)) { continue; } Type[] array; try { array = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { array = ex.Types.Where((Type t) => t != null).ToArray(); } catch (Exception) { continue; } Type[] array2 = array; foreach (Type type in array2) { try { Type baseType = type.BaseType; if (!(baseType == null)) { AddChild(CecilName(baseType.IsGenericType ? baseType.GetGenericTypeDefinition() : baseType), assembly, type.MetadataToken, CecilName(type)); } } catch (Exception) { } } } } private static bool IndexFromMetadata(Assembly a) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) try { string location = a.Location; if (string.IsNullOrEmpty(location) || !File.Exists(location)) { return false; } AssemblyDefinition val = AssemblyDefinition.ReadAssembly(location); try { foreach (TypeDefinition type in val.MainModule.GetTypes()) { TypeReference val2 = type.BaseType; if (val2 != null) { GenericInstanceType val3 = (GenericInstanceType)(object)((val2 is GenericInstanceType) ? val2 : null); if (val3 != null) { val2 = ((TypeSpecification)val3).ElementType; } string fullName = ((MemberReference)val2).FullName; MetadataToken metadataToken = ((MemberReference)type).MetadataToken; AddChild(fullName, a, ((MetadataToken)(ref metadataToken)).ToInt32(), ((MemberReference)type).FullName); } } } finally { ((IDisposable)val)?.Dispose(); } return true; } catch (Exception) { return false; } } private static void AddChild(string baseName, Assembly a, int token, string name) { if (!childIndex.TryGetValue(baseName, out var value)) { value = (childIndex[baseName] = new List<ChildType>()); } value.Add(new ChildType { Assembly = a, Token = token, Name = name }); } private static string CecilName(Type t) { if (t.IsNested) { return CecilName(t.DeclaringType) + "/" + t.Name; } return string.IsNullOrEmpty(t.Namespace) ? t.Name : (t.Namespace + "." + t.Name); } private static bool DerivesFrom(Type t, Type declaring) { Type baseType = t.BaseType; while (baseType != null && baseType != typeof(object)) { if ((baseType.IsGenericType ? baseType.GetGenericTypeDefinition() : baseType) == declaring) { return true; } baseType = baseType.BaseType; } return false; } private static int NonDynamicAssemblyCount() { return AppDomain.CurrentDomain.GetAssemblies().Count((Assembly a) => !a.IsDynamic); } public static void RunStaticConstructors() { if (touchedTypes.Count == 0) { return; } Type[] array = touchedTypes.ToArray(); foreach (Type type in array) { if (type.ContainsGenericParameters) { continue; } string name = type.Assembly.GetName().Name; if (!NativeAnalyzer.IsBcl(name)) { try { RuntimeHelpers.RunClassConstructor(type.TypeHandle); } catch (Exception) { } } } touchedTypes.Clear(); } private static string Describe(MethodBase m) { return NativeAnalyzer.Describe(m); } } internal static class RsapiCompat { [CompilerGenerated] private static class <>O { public static Manipulator <0>__RedirectScratch; } private const BindingFlags Any = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public static readonly HashSet<MethodBase> Patched = new HashSet<MethodBase>(MethodComparer.Instance); private static FieldInfo statModsField; private static FieldInfo customStatsField; private static Action<CharacterBody> getStatMods; private static Func<CharacterBody, object> getCustomStats; private static readonly List<ILHook> hooks = new List<ILHook>(); public static bool Active { get; private set; } public static MethodInfo GetStatModsMethod { get; private set; } public static void Init(ManualLogSource log, bool installHooks = true) { //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Expected O, but got Unknown //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Expected O, but got Unknown Type typeFromHandle = typeof(RecalculateStatsAPI); statModsField = typeFromHandle.GetField("StatMods", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); customStatsField = typeFromHandle.GetField("BodyCustomStats", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); GetStatModsMethod = typeFromHandle.GetMethod("GetStatMods", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(CharacterBody) }, null); MethodInfo method = typeFromHandle.GetMethod("GetCustomStatsFromBody", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(CharacterBody) }, null); if (statModsField == null || customStatsField == null || GetStatModsMethod == null || method == null) { log.LogWarning((object)"R2API.RecalculateStats internals changed, can't make it thread safe."); return; } getStatMods = (Action<CharacterBody>)Delegate.CreateDelegate(typeof(Action<CharacterBody>), GetStatModsMethod); getCustomStats = (Func<CharacterBody, object>)Delegate.CreateDelegate(typeof(Func<CharacterBody, object>), method); Type[] array; try { array = typeFromHandle.Assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { array = ex.Types.Where((Type t) => t != null).ToArray(); } Type[] array2 = array; foreach (Type type in array2) { MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.IsAbstract || methodInfo.ContainsGenericParameters || !TouchesScratch(methodInfo) || methodInfo == GetStatModsMethod) { continue; } if (installHooks) { List<ILHook> list = hooks; object obj = <>O.<0>__RedirectScratch; if (obj == null) { Manipulator val = RedirectScratch; <>O.<0>__RedirectScratch = val; obj = (object)val; } list.Add(new ILHook((MethodBase)methodInfo, (Manipulator)obj)); } Patched.Add(methodInfo); } } Active = true; } public static void Unload() { foreach (ILHook hook in hooks) { hook.Dispose(); } hooks.Clear(); Patched.Clear(); Active = false; } public static void GetStatMods(CharacterBody body) { getStatMods(body); } public static void Prepare(CharacterBody body, HolyData data) { if (Active) { getStatMods(body); data.statMods = statModsField.GetValue(null); data.customStats = getCustomStats(body); } } private static bool TouchesScratch(MethodBase m) { byte[] array; try { array = m.GetMethodBody()?.GetILAsByteArray(); } catch (Exception) { return false; } if (array == null) { return false; } int metadataToken = statModsField.MetadataToken; int metadataToken2 = customStatsField.MetadataToken; for (int i = 0; i + 4 < array.Length; i++) { if (array[i] == 126 || array[i] == 128) { int num = BitConverter.ToInt32(array, i + 1); if (num == metadataToken || num == metadataToken2) { return true; } } } return false; } private static void RedirectScratch(ILContext il) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) MethodInfo getMethod = typeof(ParallelRuntime).GetProperty("InParallel").GetGetMethod(); Instruction[] array = il.Instrs.ToArray(); foreach (Instruction val in array) { if (val.OpCode != OpCodes.Ldsfld && val.OpCode != OpCodes.Stsfld) { continue; } object operand = val.Operand; FieldReference val2 = (FieldReference)((operand is FieldReference) ? operand : null); if (val2 == null) { continue; } FieldInfo fieldInfo; try { fieldInfo = ReflectionHelper.ResolveReflection(val2); } catch (Exception) { continue; } bool flag = fieldInfo == statModsField; bool flag2 = fieldInfo == customStatsField; if (!flag && !flag2) { continue; } bool flag3 = val.OpCode == OpCodes.Ldsfld; if (!(!flag3 && flag)) { val.OpCode = OpCodes.Call; val.Operand = il.Import((MethodBase)getMethod); ILCursor val3 = new ILCursor(il).Goto(val, (MoveType)2, false); ILLabel val4 = val3.DefineLabel(); ILLabel val5 = val3.DefineLabel(); val3.Emit(OpCodes.Brtrue, (object)val4); val3.Emit(flag3 ? OpCodes.Ldsfld : OpCodes.Stsfld, val2); val3.Emit(OpCodes.Br, (object)val5); val3.MarkLabel(val4); if (flag3) { val3.Emit(OpCodes.Call, (MethodBase)typeof(ParallelRuntime).GetMethod(flag ? "GetThreadStatMods" : "GetThreadCustomStats")); val3.Emit(OpCodes.Castclass, il.Import(fieldInfo.FieldType)); } else { val3.Emit(OpCodes.Call, (MethodBase)typeof(ParallelRuntime).GetMethod("SetThreadCustomStats")); } val3.MarkLabel(val5); } } } } }