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 Valheim10Compatibility v1.4.1
patchers/Valheim10Compatibility.Patcher.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using MonoMod.RuntimeDetour; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Valheim10Compatibility.Patcher")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("BepInEx preloader patcher providing binary backward compatibility for Valheim 1.0.")] [assembly: AssemblyFileVersion("1.4.1.0")] [assembly: AssemblyInformationalVersion("1.4.1+392146eab253da24bd329d86438263eeac9a4c7d")] [assembly: AssemblyProduct("Valheim10Compatibility.Patcher")] [assembly: AssemblyTitle("Valheim10Compatibility.Patcher")] [assembly: AssemblyVersion("1.4.1.0")] [module: RefSafetyRules(11)] 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 Valheim10Compatibility.Patcher { internal sealed class AmbiguityGuard { private sealed class PatchInfo { public string Type; public string Method; public bool HasArgTypes; public int MethodType; public bool Any; } private static readonly string[] PatchMethodNames = new string[5] { "Prefix", "Postfix", "Transpiler", "Finalizer", "ILManipulator" }; public static readonly string[] AliasedFieldNames = new string[1] { "m_availablePieces" }; private readonly Dictionary<string, HashSet<string>> _byName = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal); private readonly Dictionary<string, HashSet<string>> _who = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal); private readonly Dictionary<string, Dictionary<string, string>> _fieldShape = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal); private readonly Dictionary<string, HashSet<string>> _reflectFatal = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal); private readonly Dictionary<string, HashSet<string>> _reflectHooked = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal); public bool Available { get; private set; } public int PluginsScanned { get; private set; } public int ByNameTargets { get; private set; } public int OldShapeFieldUsers { get; private set; } public string Root { get; private set; } public bool HookInstalled { get; set; } public static AmbiguityGuard Empty() { return new AmbiguityGuard { Available = false }; } public static AmbiguityGuard Build(string pluginRoot, IEnumerable<string> extraSearchDirs, ManualLogSource log) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown AmbiguityGuard ambiguityGuard = new AmbiguityGuard { Root = pluginRoot }; if (string.IsNullOrEmpty(pluginRoot) || !Directory.Exists(pluginRoot)) { log.LogWarning((object)("Ambiguity guard: plugin folder not found ('" + pluginRoot + "'); overload bridges are NOT protected against by-name Harmony patches.")); return ambiguityGuard; } Stopwatch stopwatch = Stopwatch.StartNew(); DefaultAssemblyResolver val = new DefaultAssemblyResolver(); foreach (string item in extraSearchDirs ?? Enumerable.Empty<string>()) { if (!string.IsNullOrEmpty(item) && Directory.Exists(item)) { ((BaseAssemblyResolver)val).AddSearchDirectory(item); } } string[] array; try { array = (from f in Directory.GetFiles(pluginRoot, "*.dll", SearchOption.AllDirectories) where f.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) select f).ToArray(); } catch (Exception ex) { log.LogWarning((object)("Ambiguity guard: could not enumerate '" + pluginRoot + "': " + ex.Message)); return ambiguityGuard; } int num = 0; string[] array2 = array; foreach (string path in array2) { try { ((BaseAssemblyResolver)val).AddSearchDirectory(Path.GetDirectoryName(path)); byte[] array3 = File.ReadAllBytes(path); AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly((Stream)new MemoryStream(array3), new ReaderParameters { ReadingMode = (ReadingMode)2, AssemblyResolver = (IAssemblyResolver)(object)val, ReadSymbols = false }); try { ModuleDefinition mainModule = val2.MainModule; string fileName = Path.GetFileName(path); if (((IEnumerable<AssemblyNameReference>)mainModule.AssemblyReferences).Any((AssemblyNameReference r) => r.Name == "0Harmony")) { ambiguityGuard.PluginsScanned++; foreach (TypeDefinition type in mainModule.GetTypes()) { num += ambiguityGuard.ScanType(type, fileName); } } string[] aliasedFieldNames = AliasedFieldNames; foreach (string text in aliasedFieldNames) { bool flag = ContainsBytes(array3, Encoding.UTF8.GetBytes(text)); bool flag2 = ContainsBytes(array3, Encoding.Unicode.GetBytes(text)); if (flag || flag2) { ambiguityGuard.ScanFieldUse(mainModule, fileName, text, flag2); } } } finally { ((IDisposable)val2)?.Dispose(); } } catch (Exception ex2) { num++; log.LogDebug((object)("Ambiguity guard: skipped '" + Path.GetFileName(path) + "': " + ex2.GetType().Name + ": " + ex2.Message)); } } ambiguityGuard.Available = true; log.LogInfo((object)($"Ambiguity guard: scanned {ambiguityGuard.PluginsScanned} Harmony plugins under '{pluginRoot}' in {stopwatch.ElapsedMilliseconds} ms; {ambiguityGuard.ByNameTargets} by-name patch targets recorded; {ambiguityGuard.OldShapeFieldUsers} plugin(s) reference a pre-1.0 field shape this patcher aliases" + ((num > 0) ? $"; {num} attribute(s)/file(s) could not be parsed" : "") + ".")); return ambiguityGuard; } private static bool ContainsBytes(byte[] hay, byte[] needle) { if (needle.Length == 0 || hay.Length < needle.Length) { return false; } byte b = needle[0]; int num = hay.Length - needle.Length; for (int i = 0; i <= num; i++) { if (hay[i] == b) { int j; for (j = 1; j < needle.Length && hay[i + j] == needle[j]; j++) { } if (j == needle.Length) { return true; } } } return false; } private static PatchInfo Merge(PatchInfo baseInfo, IEnumerable<CustomAttribute> attrs, out int unparsable) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) unparsable = 0; PatchInfo patchInfo = new PatchInfo { Type = baseInfo?.Type, Method = baseInfo?.Method, HasArgTypes = (baseInfo?.HasArgTypes ?? false), MethodType = (baseInfo?.MethodType ?? 0), Any = (baseInfo?.Any ?? false) }; foreach (CustomAttribute attr in attrs) { if (((MemberReference)attr.AttributeType).Name != "HarmonyPatch") { continue; } patchInfo.Any = true; IList<CustomAttributeArgument> constructorArguments; try { constructorArguments = (IList<CustomAttributeArgument>)attr.ConstructorArguments; } catch (Exception) { unparsable++; continue; } List<string> list = new List<string>(); foreach (CustomAttributeArgument item2 in constructorArguments) { CustomAttributeArgument current2 = item2; object value = ((CustomAttributeArgument)(ref current2)).Value; TypeReference val = (TypeReference)((value is TypeReference) ? value : null); if (val != null) { patchInfo.Type = ((MemberReference)val).FullName; } else if (((CustomAttributeArgument)(ref current2)).Value is string item) { list.Add(item); } else if (((MemberReference)((CustomAttributeArgument)(ref current2)).Type).FullName == "System.Type[]") { patchInfo.HasArgTypes = true; } else if (((MemberReference)((CustomAttributeArgument)(ref current2)).Type).Name == "MethodType") { try { patchInfo.MethodType = Convert.ToInt32(((CustomAttributeArgument)(ref current2)).Value); } catch (Exception) { unparsable++; } } } if (list.Count == 1) { patchInfo.Method = list[0]; } else if (list.Count >= 2) { patchInfo.Type = NormalizeTypeName(list[0]); patchInfo.Method = list[1]; } } return patchInfo; } private static string NormalizeTypeName(string harmonyTypeName) { if (string.IsNullOrEmpty(harmonyTypeName)) { return harmonyTypeName; } int num = harmonyTypeName.IndexOf(','); if (num > 0) { harmonyTypeName = harmonyTypeName.Substring(0, num); } return harmonyTypeName.Trim().Replace('+', '/'); } private int ScanType(TypeDefinition type, string plugin) { //IL_0079: 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) int unparsable; PatchInfo patchInfo = Merge(null, (IEnumerable<CustomAttribute>)type.CustomAttributes, out unparsable); if (!patchInfo.Any && !((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((IEnumerable<CustomAttribute>)m.CustomAttributes).Any((CustomAttribute c) => ((MemberReference)c.AttributeType).Name == "HarmonyPatch"))) { return unparsable; } if (((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "TargetMethod" || ((MemberReference)m).Name == "TargetMethods")) { return unparsable; } Enumerator<MethodDefinition> enumerator = type.Methods.GetEnumerator(); try { while (enumerator.MoveNext()) { MethodDefinition current = enumerator.Current; bool num = ((IEnumerable<CustomAttribute>)current.CustomAttributes).Any((CustomAttribute c) => ((MemberReference)c.AttributeType).Name == "HarmonyPatch"); bool flag = ((IEnumerable<CustomAttribute>)current.CustomAttributes).Any((CustomAttribute c) => ((MemberReference)c.AttributeType).Name.StartsWith("Harmony", StringComparison.Ordinal) && ((MemberReference)c.AttributeType).Name != "HarmonyPatch") || PatchMethodNames.Contains(((MemberReference)current).Name); if (!num && !(patchInfo.Any && flag)) { continue; } int unparsable2; PatchInfo patchInfo2 = Merge(patchInfo, (IEnumerable<CustomAttribute>)current.CustomAttributes, out unparsable2); unparsable += unparsable2; if (patchInfo2.Type == null || patchInfo2.HasArgTypes) { continue; } int methodType = patchInfo2.MethodType; string text; if (methodType != 0) { if (methodType != 3) { continue; } text = ".ctor"; } else { text = patchInfo2.Method; } if (!string.IsNullOrEmpty(text)) { Record(patchInfo2.Type, text, plugin); } } return unparsable; } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } private void ScanFieldUse(ModuleDefinition module, string plugin, string fieldName, bool checkLiterals) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_0124: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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_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) string name = ((AssemblyNameReference)module.Assembly.Name).Name; bool flag = false; foreach (TypeDefinition type in module.GetTypes()) { Enumerator<MethodDefinition> enumerator2 = type.Methods.GetEnumerator(); try { while (enumerator2.MoveNext()) { MethodDefinition current2 = enumerator2.Current; if (!current2.HasBody) { continue; } bool flag2 = false; List<MethodReference> list = new List<MethodReference>(); Enumerator<Instruction> enumerator3 = current2.Body.Instructions.GetEnumerator(); try { while (enumerator3.MoveNext()) { Instruction current3 = enumerator3.Current; object operand = current3.Operand; FieldReference val = (FieldReference)((operand is FieldReference) ? operand : null); if (val != null && ((MemberReference)val).Name == fieldName) { string key = ((MemberReference)((MemberReference)val).DeclaringType).FullName.Replace('/', '+') + "::" + ((MemberReference)val).Name; if (!_fieldShape.TryGetValue(key, out var value)) { value = (_fieldShape[key] = new Dictionary<string, string>(StringComparer.Ordinal)); } if (!value.ContainsKey(name)) { value[name] = ((MemberReference)val.FieldType).Name; flag = true; } } else { if (!checkLiterals) { continue; } if (current3.OpCode == OpCodes.Ldstr && current3.Operand as string == fieldName) { flag2 = true; continue; } object operand2 = current3.Operand; MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (val2 != null && (current3.OpCode == OpCodes.Call || current3.OpCode == OpCodes.Callvirt)) { list.Add(val2); } } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } if (flag2) { bool flag3 = list.Any((MethodReference c) => ((MemberReference)((MemberReference)c).DeclaringType).FullName == "HarmonyLib.AccessTools" && (((MemberReference)c).Name == "Field" || ((MemberReference)c).Name == "DeclaredField")); Add((list.Any((MethodReference c) => (((MemberReference)((MemberReference)c).DeclaringType).FullName == "System.Type" && ((MemberReference)c).Name == "GetField") || (((MemberReference)((MemberReference)c).DeclaringType).FullName == "HarmonyLib.Traverse" && ((MemberReference)c).Name == "Field") || (((MemberReference)((MemberReference)c).DeclaringType).FullName == "HarmonyLib.AccessTools" && ((MemberReference)c).Name == "FieldRefAccess")) || !flag3) ? _reflectFatal : _reflectHooked, fieldName, plugin + " (" + ((MemberReference)type).Name + "." + ((MemberReference)current2).Name + ")"); } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } if (flag) { OldShapeFieldUsers++; } } private static void Add(Dictionary<string, HashSet<string>> map, string key, string value) { if (!map.TryGetValue(key, out var value2)) { value2 = (map[key] = new HashSet<string>(StringComparer.OrdinalIgnoreCase)); } value2.Add(value); } private void Record(string type, string method, string plugin) { if (!_byName.TryGetValue(type, out var value)) { value = (_byName[type] = new HashSet<string>(StringComparer.Ordinal)); } if (value.Add(method)) { ByNameTargets++; } string key = type + "::" + method; if (!_who.TryGetValue(key, out var value2)) { value2 = (_who[key] = new HashSet<string>(StringComparer.OrdinalIgnoreCase)); } value2.Add(plugin); } private bool PatchedByName(string typeFullName, string method) { if (_byName.TryGetValue(typeFullName, out var value)) { return value.Contains(method); } return false; } public bool WouldBreak(TypeDefinition type, string methodName, out string offenders) { offenders = null; if (!Available) { return false; } if (((IEnumerable<MethodDefinition>)type.Methods).Count((MethodDefinition m) => ((MemberReference)m).Name == methodName) != 1) { return false; } List<string> list = new List<string>(); if (PatchedByName(((MemberReference)type).FullName, methodName)) { list.AddRange(_who[((MemberReference)type).FullName + "::" + methodName]); } foreach (TypeDefinition type2 in ((MemberReference)type).Module.GetTypes()) { if (type2 != type && DerivesFrom(type2, type) && !((IEnumerable<MethodDefinition>)type2.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == methodName) && PatchedByName(((MemberReference)type2).FullName, methodName)) { list.AddRange(_who[((MemberReference)type2).FullName + "::" + methodName]); } } if (list.Count == 0) { return false; } offenders = string.Join(", ", (from s in list.Distinct<string>(StringComparer.OrdinalIgnoreCase) orderby s select s).ToArray()); return true; } public bool FieldAliasWouldBreak(string fieldName, out string offenders) { offenders = null; if (!Available) { return false; } List<string> list = new List<string>(); if (_reflectFatal.TryGetValue(fieldName, out var value)) { list.AddRange(value); } if (!HookInstalled && _reflectHooked.TryGetValue(fieldName, out value)) { list.AddRange(value); } if (list.Count == 0) { return false; } offenders = string.Join(", ", (from s in list.Distinct<string>(StringComparer.OrdinalIgnoreCase) orderby s select s).ToArray()); return true; } public string OldFieldShapeFor(string fieldKey, string assemblyName) { if (assemblyName == null) { return null; } if (!_fieldShape.TryGetValue(fieldKey, out var value) || !value.TryGetValue(assemblyName, out var value2)) { return null; } return value2; } private static bool DerivesFrom(TypeDefinition sub, TypeDefinition baseType) { TypeReference baseType2 = sub.BaseType; int num = 0; while (baseType2 != null && num++ < 32) { if (((MemberReference)baseType2).FullName == ((MemberReference)baseType).FullName) { return true; } TypeDefinition val; try { val = baseType2.Resolve(); } catch (Exception) { return false; } if (val == null) { return false; } baseType2 = val.BaseType; } return false; } } internal static class HarmonyResolver { private delegate MethodInfo orig_Method(Type type, string name, Type[] parameters, Type[] generics); private delegate FieldInfo orig_Field(Type type, string name); private delegate void orig_PatchProcessorCtor(PatchProcessor self, Harmony instance, MethodBase original); public const string BridgeTag = "Valheim10Compatibility bridge"; private const string TargetSeparator = " -> "; private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty; private const BindingFlags AllDeclared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty; private static ManualLogSource _log; private static AmbiguityGuard _guard; private static readonly List<Hook> Hooks = new List<Hook>(); private static readonly HashSet<string> Logged = new HashSet<string>(StringComparer.Ordinal); private static readonly Dictionary<MemberInfo, bool> BridgeCache = new Dictionary<MemberInfo, bool>(); private static readonly object Lock = new object(); private static readonly HashSet<string> PatchingTypes = new HashSet<string>(StringComparer.Ordinal) { "HarmonyLib.PatchClassProcessor", "HarmonyLib.PatchProcessor", "HarmonyLib.Harmony", "HarmonyLib.PatchTools", "HarmonyLib.HarmonyMethod", "HarmonyLib.HarmonyMethodExtensions" }; public static bool Installed { get; private set; } public static string Status { get; private set; } = "not installed"; public static int Resolutions { get; private set; } public static bool Install(ManualLogSource log, AmbiguityGuard guard) { //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown _log = log; _guard = guard; if (Installed) { return true; } try { Type typeFromHandle = typeof(AccessTools); Type[] sig = new Type[4] { typeof(Type), typeof(string), typeof(Type[]), typeof(Type[]) }; Type[] sig2 = new Type[2] { typeof(Type), typeof(string) }; Add(typeFromHandle, "DeclaredMethod", sig, "DeclaredMethodHook"); Add(typeFromHandle, "Method", sig, "MethodHook"); Add(typeFromHandle, "DeclaredField", sig2, "DeclaredFieldHook"); Add(typeFromHandle, "Field", sig2, "FieldHook"); Installed = true; string arg; try { ConstructorInfo constructor = typeof(PatchProcessor).GetConstructor(new Type[2] { typeof(Harmony), typeof(MethodBase) }); if (constructor == null) { throw new MissingMethodException("HarmonyLib.PatchProcessor", ".ctor(Harmony, MethodBase)"); } Hooks.Add(new Hook((MethodBase)constructor, typeof(HarmonyResolver).GetMethod("PatchProcessorCtorHook", BindingFlags.Static | BindingFlags.NonPublic))); arg = "PatchProcessor..ctor"; } catch (Exception ex) { arg = "PatchProcessor..ctor NOT hooked, " + ex.GetType().Name + ": " + ex.Message; } Status = $"installed on HarmonyX {typeFromHandle.Assembly.GetName().Version} (AccessTools.DeclaredMethod, Method, DeclaredField, Field; {arg})"; return true; } catch (Exception ex2) { foreach (Hook hook in Hooks) { try { hook.Dispose(); } catch (Exception) { } } Hooks.Clear(); Installed = false; Status = "NOT installed (" + ex2.GetType().Name + ": " + ex2.Message + ")"; return false; } } private static void Add(Type at, string name, Type[] sig, string hookName) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown MethodInfo method = at.GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, sig, null); if (method == null) { throw new MissingMethodException("HarmonyLib.AccessTools", name); } MethodInfo method2 = typeof(HarmonyResolver).GetMethod(hookName, BindingFlags.Static | BindingFlags.NonPublic); Hooks.Add(new Hook((MethodBase)method, method2)); } private static MethodInfo DeclaredMethodHook(orig_Method orig, Type type, string name, Type[] parameters, Type[] generics) { if (type == null || name == null) { return orig(type, name, parameters, generics); } if (parameters != null) { return Redirect(orig(type, name, parameters, generics), type, name, "AccessTools.DeclaredMethod"); } try { return orig(type, name, parameters, generics); } catch (AmbiguousMatchException) { string how; MethodInfo methodInfo = Disambiguate(Named(type, name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty), type, allowParameterless: false, out how); if (methodInfo == null) { throw; } Report("AccessTools.DeclaredMethod", type, name, Describe(methodInfo), how); return (generics != null) ? methodInfo.MakeGenericMethod(generics) : methodInfo; } } private static MethodInfo MethodHook(orig_Method orig, Type type, string name, Type[] parameters, Type[] generics) { if (type == null || name == null) { return orig(type, name, parameters, generics); } if (parameters != null) { return Redirect(orig(type, name, parameters, generics), type, name, "AccessTools.Method"); } Type type2 = type; while (type2 != null) { MethodInfo[] array = Named(type2, name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty); if (array.Length != 0) { string how = null; MethodInfo methodInfo; if (array.Length == 1) { methodInfo = array[0]; } else { methodInfo = Disambiguate(array, type2, allowParameterless: true, out how); if (methodInfo == null) { throw new AmbiguousMatchException($"Ambiguous match in Harmony patch for {type}:{name}"); } if (how != null) { Report("AccessTools.Method", type, name, Describe(methodInfo), how); } } if (generics == null) { return methodInfo; } return methodInfo.MakeGenericMethod(generics); } type2 = type2.BaseType; } return orig(type, name, parameters, generics); } private static MethodInfo[] Named(Type t, string name, BindingFlags flags) { return (from m in t.GetMethods(flags) where m.Name == name select m).ToArray(); } private static MethodInfo Disambiguate(MethodInfo[] named, Type t, bool allowParameterless, out string how) { how = null; MethodInfo[] array = named.Where((MethodInfo m) => !IsBridge(m)).ToArray(); int num = named.Length - array.Length; string text = ((num > 0) ? $"{num} bridge overload(s) excluded" : null); if (array.Length == 0) { return null; } if (array.Length == 1) { how = text; return array[0]; } if (array.Select(Sig).Distinct().Count() == 1) { how = text; return MostDerived(array, t); } MethodInfo methodInfo = PreferredOverloads.Pick(array); if (methodInfo != null) { how = "vanilla added an overload in 1.0; the pre-1.0 shape is taken" + ((text != null) ? (", " + text) : ""); return methodInfo; } if (allowParameterless) { MethodInfo[] array2 = array.Where((MethodInfo m) => m.GetParameters().Length == 0).ToArray(); if (array2.Length == 1) { how = "HarmonyX's own parameterless fallback" + ((text != null) ? (", " + text) : ""); return array2[0]; } } return null; } private static MethodInfo MostDerived(MethodInfo[] natives, Type t) { while (t != null) { MethodInfo methodInfo = natives.FirstOrDefault((MethodInfo n) => n.DeclaringType == t); if (methodInfo != null) { return methodInfo; } t = t.BaseType; } return natives[0]; } private static MethodInfo Redirect(MethodInfo found, Type type, string name, string api) { if (found == null || !IsBridge(found)) { return found; } if (!HarmonyIsPatching()) { Report(api, type, name + "(" + SimpleParams(found) + ")", Describe(found), "typed lookup outside Harmony's patch machinery (a reflective call, delegate or Traverse); the bridge it named is returned unchanged so MethodInfo.Invoke keeps the pre-1.0 arity - a later Harmony.Patch on it is redirected at patch time"); return found; } MethodInfo methodInfo = NativeFor(found); if (methodInfo != null) { Report(api, type, name + "(" + SimpleParams(found) + ")", Describe(methodInfo), "typed lookup matched a compatibility bridge that only appends defaulted parameters; redirected to the native method it forwards to, so the patch fires on vanilla's own calls"); return methodInfo; } Report(api, type, name + "(" + SimpleParams(found) + ")", Describe(found), "typed lookup matched a compatibility bridge that TRANSFORMS its arguments; the patch stays on the bridge. Vanilla never calls a bridge, so this patch only fires when another pre-1.0 plugin calls the old shape", null, warn: true); return found; } private static MethodInfo NativeFor(MethodInfo bridge) { string text = BridgeTarget(bridge); MethodInfo methodInfo = ((text == null) ? null : FindBySimpleSig(bridge.DeclaringType, text)); if (!(methodInfo != null) || IsBridge(methodInfo) || !LeadingParametersMatch(bridge, methodInfo)) { return null; } return methodInfo; } [MethodImpl(MethodImplOptions.NoInlining)] private static bool HarmonyIsPatching() { try { Assembly assembly = typeof(AccessTools).Assembly; StackTrace stackTrace = new StackTrace(fNeedFileInfo: false); for (int i = 0; i < stackTrace.FrameCount; i++) { Type type = stackTrace.GetFrame(i)?.GetMethod()?.DeclaringType; if (!(type == null) && !(type.Assembly != assembly)) { while (type.DeclaringType != null) { type = type.DeclaringType; } string text = type.FullName ?? string.Empty; if (PatchingTypes.Contains(text)) { return true; } if (text == "HarmonyLib.Traverse" || text == "HarmonyLib.AccessCache") { return false; } } } } catch (Exception) { } return false; } private static void PatchProcessorCtorHook(orig_PatchProcessorCtor orig, PatchProcessor self, Harmony instance, MethodBase original) { MethodInfo methodInfo = original as MethodInfo; if (methodInfo != null && IsBridge(methodInfo)) { MethodInfo methodInfo2 = NativeFor(methodInfo); if (methodInfo2 != null) { Report("PatchProcessor", methodInfo.DeclaringType, methodInfo.Name + "(" + SimpleParams(methodInfo) + ")", Describe(methodInfo2), "Harmony.Patch on a compatibility bridge redirected to the native method it forwards to, so the patch fires on vanilla's own calls"); original = methodInfo2; } } orig(self, instance, original); } private static bool LeadingParametersMatch(MethodInfo bridge, MethodInfo native) { if (bridge.IsStatic != native.IsStatic || bridge.ReturnType != native.ReturnType) { return false; } ParameterInfo[] parameters = bridge.GetParameters(); ParameterInfo[] parameters2 = native.GetParameters(); if (parameters2.Length < parameters.Length) { return false; } for (int i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType != parameters2[i].ParameterType) { return false; } } return true; } private static MethodInfo FindBySimpleSig(Type declaring, string sig) { int num = sig.IndexOf('('); if (num < 0 || !sig.EndsWith(")")) { return null; } string name = sig.Substring(0, num); string text = sig.Substring(num + 1, sig.Length - num - 2); string[] want = ((text.Length == 0) ? new string[0] : text.Split(new char[1] { ',' })); MethodInfo[] array = (from m in declaring.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty) where m.Name == name && (from p in m.GetParameters() select p.ParameterType.Name).SequenceEqual(want) select m).ToArray(); if (array.Length != 1) { return null; } return array[0]; } private static FieldInfo DeclaredFieldHook(orig_Field orig, Type type, string name) { if (type == null || name == null) { return orig(type, name); } try { return orig(type, name); } catch (AmbiguousMatchException) { FieldInfo fieldInfo = PickField((from f in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty) where f.Name == name select f).ToArray(), type, name, "AccessTools.DeclaredField"); if (fieldInfo == null) { throw; } return fieldInfo; } } private static FieldInfo FieldHook(orig_Field orig, Type type, string name) { if (type == null || name == null) { return orig(type, name); } try { return orig(type, name); } catch (AmbiguousMatchException) { Type type2 = type; while (type2 != null) { FieldInfo[] array = (from f in type2.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty) where f.Name == name select f).ToArray(); if (array.Length != 0) { FieldInfo fieldInfo = PickField(array, type, name, "AccessTools.Field"); if (fieldInfo == null) { throw; } return fieldInfo; } type2 = type2.BaseType; } throw; } } private static FieldInfo PickField(FieldInfo[] named, Type type, string name, string api) { FieldInfo[] array = named.Where((FieldInfo f) => !IsBridge(f)).ToArray(); FieldInfo[] array2 = named.Where(IsBridge).ToArray(); if (array.Length != 1 || array2.Length == 0) { return null; } string text = CallerAssembly(); string oldShape = _guard?.OldFieldShapeFor(type.FullName + "::" + name, text); if (oldShape != null) { FieldInfo fieldInfo = array2.FirstOrDefault((FieldInfo a) => a.FieldType.Name == oldShape) ?? array2[0]; Report(api, type, name, Describe(fieldInfo), text + " references the pre-1.0 " + oldShape + " field in its IL, so the compatibility alias is returned", text); return fieldInfo; } Report(api, type, name, Describe(array[0]), (text ?? "the caller") + " does not reference the pre-1.0 shape in its IL, so the native field is returned", text); return array[0]; } public static bool IsBridge(MemberInfo m) { if (m == null) { return false; } lock (Lock) { if (BridgeCache.TryGetValue(m, out var value)) { return value; } bool flag = false; try { object[] customAttributes = m.GetCustomAttributes(typeof(ObsoleteAttribute), inherit: false); for (int i = 0; i < customAttributes.Length; i++) { if (customAttributes[i] is ObsoleteAttribute { Message: not null } obsoleteAttribute && obsoleteAttribute.Message.StartsWith("Valheim10Compatibility bridge", StringComparison.Ordinal)) { flag = true; break; } } } catch (Exception) { } BridgeCache[m] = flag; return flag; } } private static string BridgeTarget(MemberInfo m) { try { object[] customAttributes = m.GetCustomAttributes(typeof(ObsoleteAttribute), inherit: false); for (int i = 0; i < customAttributes.Length; i++) { if (customAttributes[i] is ObsoleteAttribute { Message: not null } obsoleteAttribute) { int num = obsoleteAttribute.Message.IndexOf(" -> ", StringComparison.Ordinal); if (num >= 0) { return obsoleteAttribute.Message.Substring(num + " -> ".Length).Trim(); } } } } catch (Exception) { } return null; } private static string Sig(MethodInfo m) { return string.Join(",", (from p in m.GetParameters() select p.ParameterType.ToString()).ToArray()); } private static string SimpleParams(MethodBase m) { return string.Join(", ", (from p in m.GetParameters() select p.ParameterType.Name).ToArray()); } private static string Describe(MethodInfo m) { return m.ReturnType.Name + " " + m.DeclaringType?.Name + "." + m.Name + "(" + SimpleParams(m) + ")"; } private static string Describe(FieldInfo f) { return f.FieldType.Name + " " + f.DeclaringType?.Name + "." + f.Name; } private static string CallerAssembly() { try { StackTrace stackTrace = new StackTrace(fNeedFileInfo: false); for (int i = 0; i < stackTrace.FrameCount; i++) { MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod(); if (methodBase == null || methodBase.DeclaringType == null) { continue; } string name = methodBase.DeclaringType.Assembly.GetName().Name; switch (name) { case "0Harmony": case "Valheim10Compatibility.Patcher": case "mscorlib": case "netstandard": continue; } if (!name.StartsWith("MonoMod", StringComparison.Ordinal) && !name.StartsWith("System", StringComparison.Ordinal) && !name.StartsWith("BepInEx", StringComparison.Ordinal)) { return name; } } } catch (Exception) { } return null; } private static void Report(string api, Type type, string name, string picked, string how, string caller = null, bool warn = false) { if (caller == null) { caller = CallerAssembly(); } string item = api + "|" + type.FullName + "|" + name + "|" + picked + "|" + caller; lock (Lock) { if (!Logged.Add(item)) { return; } Resolutions++; } string text = "Harmony hook: " + api + "(" + type.FullName + ", \"" + name + "\") -> " + picked + " [" + how + "]" + ((caller != null) ? (" <- " + caller) : ""); if (warn) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)text); } } else { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)text); } } } } internal enum ArgKind { FromParameter, ConstI4, ConstNull, DefaultOf, Vector2sFromV2i, ZonePosOfV2i, SimulationDist, ThisField, OutLocal } internal struct Arg { public ArgKind Kind; public int Index; public int Index2; public int Value; public TypeReference TypeRef; public FieldReference Field; public bool NarrowToI2; public static Arg P(int i) { return new Arg { Kind = ArgKind.FromParameter, Index = i }; } public static Arg I4(int v) { return new Arg { Kind = ArgKind.ConstI4, Value = v }; } public static Arg I2(int v) { return new Arg { Kind = ArgKind.ConstI4, Value = v, NarrowToI2 = true }; } public static Arg False() { return I4(0); } public static Arg True() { return I4(1); } public static Arg Null() { return new Arg { Kind = ArgKind.ConstNull }; } public static Arg Default(TypeReference t) { return new Arg { Kind = ArgKind.DefaultOf, TypeRef = t }; } public static Arg V2s(int i) { return new Arg { Kind = ArgKind.Vector2sFromV2i, Index = i }; } public static Arg ZonePos(int i) { return new Arg { Kind = ArgKind.ZonePosOfV2i, Index = i }; } public static Arg SimDist(int near, int far) { return new Arg { Kind = ArgKind.SimulationDist, Index = near, Index2 = far }; } public static Arg Fld(FieldReference f) { return new Arg { Kind = ArgKind.ThisField, Field = f }; } public static Arg Out(TypeReference t) { return new Arg { Kind = ArgKind.OutLocal, TypeRef = t }; } } internal sealed class Injector { private readonly ModuleDefinition _module; private readonly ManualLogSource _log; private readonly AmbiguityGuard _guard; public readonly List<string> SkipDetails = new List<string>(); public readonly List<string> BlockDetails = new List<string>(); public readonly List<string> AdvisoryDetails = new List<string>(); public TypeReference Vector2i; public TypeReference Vector2s; public MethodReference Vector2sCtor; public FieldReference V2iX; public FieldReference V2iY; public MethodReference GetZonePosV2s; public MethodReference SimulationDistanceCtor; public int Injected { get; private set; } public int Skipped { get; private set; } public int Blocked { get; private set; } public int BlockedReturnTypeOnly { get; private set; } public int AlreadyPresent { get; private set; } public int Advisory { get; private set; } public Injector(ModuleDefinition module, ManualLogSource log, AmbiguityGuard guard) { _module = module; _log = log; _guard = guard; } private static bool SameShape(MethodDefinition m, string name, IList<TypeReference> paramTypes) { if (((MemberReference)m).Name != name || ((MethodReference)m).Parameters.Count != paramTypes.Count) { return false; } for (int i = 0; i < paramTypes.Count; i++) { if (paramTypes[i] != null && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[i]).ParameterType).FullName != ((MemberReference)paramTypes[i]).FullName) { return false; } } return true; } private bool RefusedByGuard(TypeDefinition type, string name, string label) { if (!_guard.WouldBreak(type, name, out var offenders)) { return false; } if (HarmonyResolver.Installed) { Advisory++; AdvisoryDetails.Add(label + " (by-name patch in " + offenders + ")"); _log.LogInfo((object)(" by-name Harmony patch on " + ((MemberReference)type).Name + "." + name + " in " + offenders + ": the resolution hook steers it to the native overload, so this bridge is injected anyway.")); return false; } Blocked++; BlockDetails.Add(label + " (would break: " + offenders + ")"); _log.LogWarning((object)("BLOCKED " + label + ": adding this overload would make the by-name Harmony patch in " + offenders + " ambiguous, which kills that plugin at PatchAll(). Left unbridged deliberately (the Harmony resolution hook is not installed).")); return true; } public bool Forward(TypeDefinition type, string name, MethodDefinition target, IList<Tuple<string, TypeReference>> bridgeParams, IList<Arg> plan, string label, bool isStatic = false, bool virtualCall = false, bool isConstructor = false) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) if (target == null) { Skipped++; SkipDetails.Add(label); _log.LogWarning((object)("SKIPPED " + label + ": no matching target on this build - plugins calling it stay broken.")); return false; } List<TypeReference> wanted = bridgeParams.Select((Tuple<string, TypeReference> p) => p.Item2).ToList(); if (((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => SameShape(m, name, wanted))) { AlreadyPresent++; _log.LogInfo((object)("present natively, no bridge needed: " + label)); return false; } if (!isConstructor && RefusedByGuard(type, name, label)) { return false; } MethodAttributes val = (MethodAttributes)134; if (isStatic) { val = (MethodAttributes)(val | 0x10); } if (isConstructor) { val = (MethodAttributes)(val | 0x1800); } MethodDefinition val2 = new MethodDefinition(name, val, isConstructor ? _module.TypeSystem.Void : _module.ImportReference(((MethodReference)target).ReturnType)); foreach (Tuple<string, TypeReference> bridgeParam in bridgeParams) { ((MethodReference)val2).Parameters.Add(new ParameterDefinition(bridgeParam.Item1, (ParameterAttributes)0, _module.ImportReference(bridgeParam.Item2))); } ILProcessor iLProcessor = val2.Body.GetILProcessor(); if (!isStatic) { iLProcessor.Emit(OpCodes.Ldarg_0); } foreach (Arg item in plan) { if (!EmitArg(iLProcessor, val2, item)) { Skipped++; SkipDetails.Add(label); _log.LogWarning((object)("SKIPPED " + label + ": a required helper (Vector2s / GetZonePos / SimulationDistance) is unavailable on this build.")); return false; } } iLProcessor.Emit((virtualCall && !target.IsStatic) ? OpCodes.Callvirt : OpCodes.Call, _module.ImportReference((MethodReference)(object)target)); iLProcessor.Emit(OpCodes.Ret); type.Methods.Add(val2); Tag(val2, target); Injected++; _log.LogInfo((object)("Injected " + label)); return true; } private bool EmitArg(ILProcessor il, MethodDefinition bridge, Arg a) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) switch (a.Kind) { case ArgKind.FromParameter: il.Emit(OpCodes.Ldarg_S, ((MethodReference)bridge).Parameters[a.Index]); return true; case ArgKind.ConstI4: EmitI4(il, a.Value); if (a.NarrowToI2) { il.Emit(OpCodes.Conv_I2); } return true; case ArgKind.ConstNull: il.Emit(OpCodes.Ldnull); return true; case ArgKind.DefaultOf: { VariableDefinition val2 = new VariableDefinition(_module.ImportReference(a.TypeRef)); bridge.Body.Variables.Add(val2); bridge.Body.InitLocals = true; il.Emit(OpCodes.Ldloca_S, val2); il.Emit(OpCodes.Initobj, _module.ImportReference(a.TypeRef)); il.Emit(OpCodes.Ldloc_S, val2); return true; } case ArgKind.OutLocal: { VariableDefinition val = new VariableDefinition(_module.ImportReference(a.TypeRef)); bridge.Body.Variables.Add(val); bridge.Body.InitLocals = true; il.Emit(OpCodes.Ldloca_S, val); return true; } case ArgKind.Vector2sFromV2i: if (Vector2sCtor == null || V2iX == null) { return false; } EmitVector2s(il, ((MethodReference)bridge).Parameters[a.Index]); return true; case ArgKind.ZonePosOfV2i: if (Vector2sCtor == null || V2iX == null || GetZonePosV2s == null) { return false; } EmitVector2s(il, ((MethodReference)bridge).Parameters[a.Index]); il.Emit(OpCodes.Call, GetZonePosV2s); return true; case ArgKind.SimulationDist: if (SimulationDistanceCtor == null) { return false; } il.Emit(OpCodes.Ldarg_S, ((MethodReference)bridge).Parameters[a.Index]); il.Emit(OpCodes.Ldarg_S, ((MethodReference)bridge).Parameters[a.Index2]); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Newobj, SimulationDistanceCtor); return true; case ArgKind.ThisField: il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, a.Field); return true; default: return false; } } private void EmitVector2s(ILProcessor il, ParameterDefinition p) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) il.Emit(OpCodes.Ldarga_S, p); il.Emit(OpCodes.Ldfld, V2iX); il.Emit(OpCodes.Conv_I2); il.Emit(OpCodes.Ldarga_S, p); il.Emit(OpCodes.Ldfld, V2iY); il.Emit(OpCodes.Conv_I2); il.Emit(OpCodes.Newobj, Vector2sCtor); } private static void EmitI4(ILProcessor il, int v) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (v == -1) { il.Emit(OpCodes.Ldc_I4_M1); } else { il.Emit(OpCodes.Ldc_I4, v); } } public MethodDefinition Find(TypeDefinition type, string name, Func<MethodDefinition, bool> pred) { if (type != null) { return ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == name && pred(m))); } return null; } public bool Has(TypeDefinition type, string name, int paramCount, string firstParamFullName = null) { if (type != null) { return ((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == name && ((MethodReference)m).Parameters.Count == paramCount && (firstParamFullName == null || (((MethodReference)m).Parameters.Count > 0 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == firstParamFullName))); } return false; } public void Skip(string label, string why) { Skipped++; SkipDetails.Add(label); _log.LogWarning((object)("SKIPPED " + label + ": " + why)); } public MethodDefinition Begin(TypeDefinition type, string name, TypeReference returnType, IList<Tuple<string, TypeReference>> bridgeParams, string label, bool isStatic, out bool ok) { //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Expected O, but got Unknown //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Expected O, but got Unknown ok = false; List<TypeReference> wanted = bridgeParams.Select((Tuple<string, TypeReference> p) => p.Item2).ToList(); if (((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => SameShape(m, name, wanted) && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)returnType).FullName)) { AlreadyPresent++; _log.LogInfo((object)("present natively, no bridge needed: " + label)); return null; } MethodDefinition val = ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => SameShape(m, name, wanted))); if (val != null) { Blocked++; BlockedReturnTypeOnly++; BlockDetails.Add(label + " (return-type-only overload of " + ((MemberReference)((MethodReference)val).ReturnType).Name + " " + name + ")"); _log.LogWarning((object)("BLOCKED " + label + ": this would differ from the existing " + ((MemberReference)((MethodReference)val).ReturnType).Name + " " + name + "(...) by RETURN TYPE ALONE. Type.GetMethod cannot disambiguate that, so every plugin reflecting on this name would throw AmbiguousMatchException at startup. Left unbridged deliberately; pre-1.0 call sites to it stay broken.")); return null; } if (RefusedByGuard(type, name, label)) { return null; } MethodAttributes val2 = (MethodAttributes)134; if (isStatic) { val2 = (MethodAttributes)(val2 | 0x10); } MethodDefinition val3 = new MethodDefinition(name, val2, _module.ImportReference(returnType)); foreach (Tuple<string, TypeReference> bridgeParam in bridgeParams) { ((MethodReference)val3).Parameters.Add(new ParameterDefinition(bridgeParam.Item1, (ParameterAttributes)0, _module.ImportReference(bridgeParam.Item2))); } ok = true; return val3; } public void Commit(TypeDefinition type, MethodDefinition bridge, string label) { type.Methods.Add(bridge); Tag(bridge, null); Injected++; _log.LogInfo((object)("Injected " + label)); } public bool AliasField(TypeDefinition type, FieldDefinition native, string aliasName, string label) { //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Expected O, but got Unknown //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) if (native == null) { Skip(label, "the native field is not on this build"); return false; } if (native.IsStatic) { Skip(label, "static fields are not aliased"); return false; } if (((IEnumerable<FieldDefinition>)type.Fields).Any((FieldDefinition f) => ((MemberReference)f).Name == aliasName && ((MemberReference)((FieldReference)f).FieldType).FullName == ((MemberReference)((FieldReference)native).FieldType).FullName)) { AlreadyPresent++; _log.LogInfo((object)("present natively, no alias needed: " + label)); return false; } if (_guard.FieldAliasWouldBreak(aliasName, out var offenders)) { Blocked++; BlockDetails.Add(label + " (reflected by name in " + offenders + ")"); _log.LogWarning((object)("BLOCKED " + label + ": " + offenders + " look this field up by name through Type.GetField / Traverse.Field, which throws AmbiguousMatchException the moment two fields share the name. Left unaliased deliberately.")); return false; } List<string> list = (from m in _module.GetTypes().SelectMany((TypeDefinition t) => (IEnumerable<MethodDefinition>)t.Methods) where m.HasBody && (!m.IsConstructor || m.DeclaringType != type) && ((IEnumerable<Instruction>)m.Body.Instructions).Any(delegate(Instruction i) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (i.OpCode == OpCodes.Stfld) { object operand = i.Operand; FieldReference val6 = (FieldReference)((operand is FieldReference) ? operand : null); if (val6 != null) { return SameField(val6, native); } } return false; }) select ((MemberReference)m).FullName).ToList(); if (list.Count > 0) { Skip(label, ((MemberReference)native).Name + " is reassigned outside the constructor (" + string.Join(", ", list.ToArray()) + "); an alias could not be kept in sync"); return false; } FieldDefinition val = new FieldDefinition(aliasName, (FieldAttributes)6, _module.ImportReference(((FieldReference)native).FieldType)); int num = 0; foreach (MethodDefinition item in ((IEnumerable<MethodDefinition>)type.Methods).Where((MethodDefinition m) => m.IsConstructor && !m.IsStatic && m.HasBody)) { ILProcessor iLProcessor = item.Body.GetILProcessor(); foreach (Instruction item2 in ((IEnumerable<Instruction>)item.Body.Instructions).Where(delegate(Instruction i) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (i.OpCode == OpCodes.Stfld) { object operand = i.Operand; FieldReference val6 = (FieldReference)((operand is FieldReference) ? operand : null); if (val6 != null) { return SameField(val6, native); } } return false; }).ToList()) { Instruction val2 = iLProcessor.Create(OpCodes.Ldarg_0); Instruction val3 = iLProcessor.Create(OpCodes.Ldarg_0); Instruction val4 = iLProcessor.Create(OpCodes.Ldfld, (FieldReference)(object)native); Instruction val5 = iLProcessor.Create(OpCodes.Stfld, (FieldReference)(object)val); iLProcessor.InsertAfter(item2, val2); iLProcessor.InsertAfter(val2, val3); iLProcessor.InsertAfter(val3, val4); iLProcessor.InsertAfter(val4, val5); num++; } } if (num == 0) { Skip(label, "no constructor of " + ((MemberReference)type).Name + " assigns " + ((MemberReference)native).Name + "; the alias would stay null"); return false; } type.Fields.Add(val); TagField(val, native); Injected++; _log.LogInfo((object)$"Injected {label} (alias of {((MemberReference)((FieldReference)native).FieldType).Name} {((MemberReference)native).Name}, mirrored in {num} constructor assignment(s))"); return true; } public bool DeLiteralizeField(TypeDefinition type, string fieldName, string label) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (type == null) { return false; } FieldDefinition val = ((IEnumerable<FieldDefinition>)type.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == fieldName)); if (val == null) { Skip(label, "field not found on this build"); return false; } if (!val.IsLiteral) { AlreadyPresent++; _log.LogInfo((object)("present as regular field, no de-literalization needed: " + label)); return false; } val.Attributes = (FieldAttributes)(val.Attributes & 0xFFBF); val.Attributes = (FieldAttributes)(val.Attributes & 0x7FFF); val.HasConstant = false; val.Constant = null; Injected++; _log.LogInfo((object)("Injected " + label + " (de-literalized const -> static field for ldsfld compatibility)")); return true; } public bool DefaultizeInterfaceMethod(TypeDefinition iface, string methodName, Action<ILProcessor> emitBody, string label) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown if (iface == null) { return false; } MethodDefinition val = ((IEnumerable<MethodDefinition>)iface.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == methodName && ((MethodReference)m).Parameters.Count == 0)); if (val == null) { Skip(label, "method not found on this build"); return false; } if (!val.IsAbstract) { AlreadyPresent++; _log.LogInfo((object)("present as a concrete method, no default needed: " + label)); return false; } val.Attributes = (MethodAttributes)(val.Attributes & 0xFBFF); val.Body = new MethodBody(val); emitBody(val.Body.GetILProcessor()); Injected++; _log.LogInfo((object)("Injected " + label + " (gave the interface's abstract method a default body, so non-overriding implementers still pass VTable setup)")); return true; } private static bool SameField(FieldReference fr, FieldDefinition def) { if ((object)fr == def) { return true; } if (((MemberReference)fr).Name != ((MemberReference)def).Name || ((MemberReference)((MemberReference)fr).DeclaringType).FullName != ((MemberReference)def.DeclaringType).FullName) { return false; } return ((MemberReference)fr.FieldType).FullName == ((MemberReference)((FieldReference)def).FieldType).FullName; } private static string SimpleSig(MethodDefinition m) { return ((MemberReference)m).Name + "(" + string.Join(",", ((IEnumerable<ParameterDefinition>)((MethodReference)m).Parameters).Select((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).Name).ToArray()) + ")"; } private void Tag(MethodDefinition bridge, MethodDefinition target) { string text = "Valheim10Compatibility bridge"; if (target != null && !target.IsConstructor) { text = text + " -> " + SimpleSig(target); } AddObsolete((ICollection<CustomAttribute>)bridge.CustomAttributes, text, ((MemberReference)bridge).Name); } private void TagField(FieldDefinition alias, FieldDefinition native) { AddObsolete((ICollection<CustomAttribute>)alias.CustomAttributes, "Valheim10Compatibility bridge -> " + ((MemberReference)((FieldReference)native).FieldType).Name + " " + ((MemberReference)native).Name, ((MemberReference)alias).Name); } private void AddObsolete(ICollection<CustomAttribute> attributes, string message, string what) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) try { CustomAttribute val = new CustomAttribute(_module.ImportReference((MethodBase)typeof(ObsoleteAttribute).GetConstructor(new Type[1] { typeof(string) }))); val.ConstructorArguments.Add(new CustomAttributeArgument(_module.TypeSystem.String, (object)message)); attributes.Add(val); } catch (Exception ex) { _log.LogWarning((object)("Could not tag bridge " + what + " with ObsoleteAttribute: " + ex.Message)); } } } public static class Patcher { private static readonly ManualLogSource Logger = Logger.CreateLogSource("Valheim10Compatibility"); private static AmbiguityGuard _guard; private static bool _hookWanted = true; private static bool _pieceTableAliasWanted = true; private static bool _zRoutedRpcWanted = true; private static bool _hoverableDefaultWanted = true; public static IEnumerable<string> TargetDLLs { get; } = new string[2] { "assembly_valheim.dll", "assembly_utils.dll" }; public static void Initialize() { Version version = typeof(Patcher).Assembly.GetName().Version; Logger.LogInfo((object)("Valheim 1.0 Compatibility Patcher " + version.ToString(3) + " initialized (Valheim 1.0.x+; built against 1.0.7, verified on 1.0.12).")); ReadConfig(); try { List<string> list = new List<string>(); string text = Environment.GetEnvironmentVariable("VALHEIM10COMPAT_PLUGIN_PATH"); if (string.IsNullOrEmpty(text)) { text = Paths.PluginPath; if (!string.IsNullOrEmpty(Paths.ManagedPath)) { list.Add(Paths.ManagedPath); } if (!string.IsNullOrEmpty(Paths.BepInExRootPath)) { list.Add(Path.Combine(Paths.BepInExRootPath, "core")); } } else { Logger.LogInfo((object)("Ambiguity guard: using VALHEIM10COMPAT_PLUGIN_PATH='" + text + "'.")); string environmentVariable = Environment.GetEnvironmentVariable("VALHEIM10COMPAT_MANAGED_PATH"); if (!string.IsNullOrEmpty(environmentVariable)) { list.Add(environmentVariable); } } _guard = AmbiguityGuard.Build(text, list, Logger); } catch (Exception ex) { Logger.LogWarning((object)("Ambiguity guard unavailable (" + ex.GetType().Name + ": " + ex.Message + "); overload bridges are NOT protected against by-name Harmony patches.")); _guard = AmbiguityGuard.Empty(); } if (_hookWanted) { if (HarmonyResolver.Install(Logger, _guard)) { Logger.LogInfo((object)$"Harmony resolution hook {HarmonyResolver.Status}; by-name patches resolve past compatibility bridges, and {PreferredOverloads.Count} vanilla-added overloads resolve to their pre-1.0 shape."); } else { Logger.LogWarning((object)("Harmony resolution hook " + HarmonyResolver.Status + ". Falling back to 1.1.x behaviour: every bridge beside a by-name Harmony patch is BLOCKED, and pre-1.0 plugins patching Inventory.Load / PieceTable.SetCategory / Attack.GetAttackEitr by name die at PatchAll() as they do on plain 1.0.7.")); } } else { Logger.LogWarning((object)"Harmony resolution hook disabled in Valheim10Compatibility.cfg; running with 1.1.x guard behaviour."); } _guard.HookInstalled = HarmonyResolver.Installed; } private static void ReadConfig() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) try { string configPath = Paths.ConfigPath; if (!string.IsNullOrEmpty(configPath)) { ConfigFile val = new ConfigFile(Path.Combine(configPath, "Valheim10Compatibility.cfg"), true); _hookWanted = val.Bind<bool>("Harmony", "ResolutionHook", true, "Detour HarmonyX's AccessTools lookups so by-name [HarmonyPatch] targets ignore compatibility bridges and resolve vanilla-added overloads (Inventory.Load, PieceTable.SetCategory, Attack.GetAttackEitr, ...) to their pre-1.0 shape. Off = 1.1.x behaviour: every bridge beside a by-name patch is refused.").Value; _pieceTableAliasWanted = val.Bind<bool>("Bridges", "PieceTableAvailablePiecesAlias", true, "Inject the pre-1.0 List<List<Piece>> PieceTable.m_availablePieces beside 1.0's HashSet<Piece> field, sharing the per-category lists with m_availablePiecesByCategory. Restores 15 build-piece mods on the reference profile. Caveat: 1.0 inserted PieceCategory.DeepNorth = 5, so a pre-1.0 mod that bakes category numbers (Feasts=5, Food=6, Meads=7, custom tabs from old Max=8) files pieces one tab off. Turn off if build-menu tabs look wrong.").Value; _zRoutedRpcWanted = val.Bind<bool>("Bridges", "ZRoutedRpcEverybodyStatic", true, "De-literalize ZRoutedRpc.Everybody from a const to a static field so legacy mods and ServerSync using ldsfld instructions do not throw MissingFieldException.").Value; _hoverableDefaultWanted = val.Bind<bool>("Bridges", "HoverableGetHoverOffsetDefault", true, "Give the Hoverable.GetHoverOffset() interface method a default body (returns 0f) instead of leaving it abstract, so a pre-1.0 class implementing Hoverable without it still passes VTable setup. Off = pre-1.3.0 behaviour: every such class throws TypeLoadException the moment its assembly is scanned, taking every Harmony patch in that assembly down with it.").Value; val.Bind<bool>("Shields", "ObjectDBDuplicateItems", true, "Acted on by the Adapter plugin (BepInEx/plugins), declared here so this file documents it. Before ObjectDB.UpdateRegisters rebuilds its hash table, drop any later item prefab whose name hash duplicates an earlier one - the first registration wins, which is vanilla's whenever vanilla owns the name - and log which. Vanilla's Dictionary.Add would otherwise throw and abort FejdStartup.Start on client and server (server never loads a world, client menu spams NullReferenceException). Reached on 1.0 through a Jotunn ordering bug whenever a mod's custom item name collides with new 1.0 content (More Ore Deposits 1.3.5's GoldOre). The shield keeps the game up; the colliding mod still needs a 1.0 build. Off = startup aborts as on plain 1.0."); } } catch (Exception ex) { Logger.LogWarning((object)("Could not read Valheim10Compatibility.cfg (" + ex.GetType().Name + ": " + ex.Message + "); using defaults (all five switches on).")); } } public static void Patch(AssemblyDefinition assembly) { ModuleDefinition mainModule = assembly.MainModule; if (_guard == null) { _guard = AmbiguityGuard.Empty(); } Injector injector = new Injector(mainModule, Logger, _guard); Logger.LogInfo((object)("Patching " + ((AssemblyNameReference)assembly.Name).Name + " for 1.0 backward compatibility...")); if (string.Equals(((AssemblyNameReference)assembly.Name).Name, "assembly_utils", StringComparison.OrdinalIgnoreCase)) { PatchAssemblyUtils(mainModule, injector); } else { ResolveSectorHelpers(mainModule, injector); PatchZDO(mainModule, injector); PatchZoneSystem(mainModule, injector); PatchZNetScene(mainModule, injector); PatchZDOMan(mainModule, injector); PatchItemDrop(mainModule, injector); PatchTerminal(mainModule, injector); PatchCommonSignatureDrift(mainModule, injector); PatchSmallAppendedParameters(mainModule, injector); PatchRenamedPatchTargets(mainModule, injector); PatchPieceTableAlias(mainModule, injector); PatchZRoutedRpc(mainModule, injector); PatchHoverable(mainModule, injector); } int num = injector.Blocked - injector.BlockedReturnTypeOnly; Logger.LogInfo((object)$"{((AssemblyNameReference)assembly.Name).Name}: {injector.Injected} bridge(s) injected ({injector.Advisory} of them beside a by-name Harmony patch that the resolution hook steers past them), {injector.AlreadyPresent} already native, {injector.Skipped} skipped (no target on this build), {injector.Blocked} blocked ({num} would break a by-name Harmony patch or a reflective field lookup, {injector.BlockedReturnTypeOnly} would be a return-type-only overload)."); if (injector.Advisory > 0) { Logger.LogInfo((object)(" hook-dependent: " + string.Join("; ", injector.AdvisoryDetails.ToArray()))); } if (injector.Skipped > 0) { Logger.LogWarning((object)(" skipped: " + string.Join("; ", injector.SkipDetails.ToArray()))); } if (injector.Blocked > 0) { Logger.LogWarning((object)(" blocked: " + string.Join("; ", injector.BlockDetails.ToArray()))); } } private static List<Tuple<string, TypeReference>> Ps(params Tuple<string, TypeReference>[] p) { return p.ToList(); } private static Tuple<string, TypeReference> P(string n, TypeReference t) { return Tuple.Create<string, TypeReference>(n, t); } private static List<Arg> Plan(params Arg[] a) { return a.ToList(); } private static List<Arg> PassThroughPlus(int count, params Arg[] extra) { List<Arg> list = new List<Arg>(); for (int i = 0; i < count; i++) { list.Add(Arg.P(i)); } list.AddRange(extra); return list; } private static void PatchAssemblyUtils(ModuleDefinition module, Injector inj) { TypeDefinition type = module.GetType("StringExtensionMethods"); if (type != null) { if (inj.Has(type, "GetStableHashCode", 1, "System.String")) { Logger.LogInfo((object)"present natively, no bridge needed: StringExtensionMethods.GetStableHashCode(string) (the playtest's 2-arg form was reverted before 1.0 release)"); } else { MethodDefinition target = inj.Find(type, "GetStableHashCode", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "System.String" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Boolean"); inj.Forward(type, "GetStableHashCode", target, Ps(P("str", module.TypeSystem.String)), Plan(Arg.P(0), Arg.True()), "StringExtensionMethods.GetStableHashCode(string)", isStatic: true); } } TypeDefinition type2 = module.GetType("ZInput"); if (type2 != null) { MethodDefinition target2 = inj.Find(type2, "get_pointerPosition", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 0); inj.Forward(type2, "get_mousePosition", target2, Ps(), Plan(), "ZInput.get_mousePosition() -> pointerPosition", isStatic: true); } } private static void ResolveSectorHelpers(ModuleDefinition module, Injector inj) { TypeReference val = (TypeReference)(object)((IEnumerable<TypeDefinition>)module.Types).FirstOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((MemberReference)t).Name == "Vector2i")); TypeReference v2s = (TypeReference)(object)((IEnumerable<TypeDefinition>)module.Types).FirstOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((MemberReference)t).Name == "Vector2s")); if (val == null || v2s == null) { AssemblyNameReference val2 = ((IEnumerable<AssemblyNameReference>)module.AssemblyReferences).FirstOrDefault((Func<AssemblyNameReference, bool>)((AssemblyNameReference a) => a.Name == "assembly_utils")); if (val2 != null) { AssemblyDefinition val3 = module.AssemblyResolver.Resolve(val2); if (val3 != null) { TypeDefinition type = val3.MainModule.GetType("Vector2i"); TypeDefinition type2 = val3.MainModule.GetType("Vector2s"); if (type != null) { val = module.ImportReference((TypeReference)(object)type); } if (type2 != null) { v2s = module.ImportReference((TypeReference)(object)type2); } } } } if (val == null || v2s == null) { Logger.LogError((object)"Could not resolve Vector2i or Vector2s; every sector bridge will be skipped."); return; } inj.Vector2i = val; inj.Vector2s = v2s; TypeDefinition val4 = val.Resolve(); TypeDefinition val5 = v2s.Resolve(); inj.V2iX = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val4.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "x")); inj.V2iY = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val4.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "y")); inj.Vector2sCtor = module.ImportReference((MethodReference)(object)((IEnumerable<MethodDefinition>)val5.Methods).First((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 2 && (int)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType.MetadataType != 18)); TypeDefinition type3 = module.GetType("ZoneSystem"); MethodDefinition val6 = inj.Find(type3, "GetZonePos", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == ((MemberReference)v2s).FullName); if (val6 != null) { inj.GetZonePosV2s = module.ImportReference((MethodReference)(object)val6); } TypeDefinition type4 = module.GetType("SimulationDistance"); if (type4 != null) { MethodDefinition val7 = ((IEnumerable<MethodDefinition>)type4.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 3 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "System.Int32" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Int32" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[2]).ParameterType).FullName == "System.Boolean")); if (val7 != null) { inj.SimulationDistanceCtor = module.ImportReference((MethodReference)(object)val7); } } } private static void PatchZDO(ModuleDefinition module, Injector inj) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) TypeDefinition type = module.GetType("ZDO"); if (type == null || inj.Vector2i == null || ((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "GetSector" && ((MethodReference)m).Parameters.Count == 0 && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)inj.Vector2i).FullName)) { return; } MethodDefinition val = inj.Find(type, "GetSector", (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 0 && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)inj.Vector2s).FullName); if (val == null) { inj.Skip("ZDO.GetSector() -> Vector2i", "no GetSector() -> Vector2s on this build"); return; } bool ok; MethodDefinition val2 = inj.Begin(type, "GetSector", inj.Vector2i, Ps(), "ZDO.GetSector() -> Vector2i", isStatic: false, out ok); if (ok) { VariableDefinition val3 = new VariableDefinition(module.ImportReference(inj.Vector2s)); val2.Body.Variables.Add(val3); val2.Body.InitLocals = true; ILProcessor iLProcessor = val2.Body.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldarg_0); iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val)); iLProcessor.Emit(OpCodes.Stloc_S, val3); EmitV2iFromV2s(module, iLProcessor, val3, inj); iLProcessor.Emit(OpCodes.Ret); inj.Commit(type, val2, "ZDO.GetSector() -> Vector2i"); } } private static void EmitV2iFromV2s(ModuleDefinition module, ILProcessor il, VariableDefinition v2sLocal, Injector inj) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) TypeDefinition val = inj.Vector2s.Resolve(); TypeDefinition val2 = inj.Vector2i.Resolve(); FieldReference val3 = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "x")); FieldReference val4 = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "y")); MethodReference val5 = module.ImportReference((MethodReference)(object)((IEnumerable<MethodDefinition>)val2.Methods).First((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 2)); il.Emit(OpCodes.Ldloca_S, v2sLocal); il.Emit(OpCodes.Ldfld, val3); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Ldloca_S, v2sLocal); il.Emit(OpCodes.Ldfld, val4); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Newobj, val5); } private static void PatchZoneSystem(ModuleDefinition module, Injector inj) { //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Expected O, but got Unknown //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) TypeDefinition zs = module.GetType("ZoneSystem"); if (zs == null || inj.Vector2i == null) { return; } string v2sName = ((MemberReference)inj.Vector2s).FullName; Func<string, int, MethodDefinition> func = (string name, int count) => inj.Find(zs, name, (MethodDefinition m) => ((MethodReference)m).Parameters.Count == count && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName); inj.Forward(zs, "PokeLocalZone", func("PokeLocalZone", 1), Ps(P("zoneID", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.PokeLocalZone(Vector2i)"); inj.Forward(zs, "IsZoneGenerated", func("IsZoneGenerated", 1), Ps(P("zoneID", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.IsZoneGenerated(Vector2i)"); inj.Forward(zs, "IsZoneLoaded", func("IsZoneLoaded", 1), Ps(P("zoneID", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.IsZoneLoaded(Vector2i)"); inj.Forward(zs, "GetZonePos", func("GetZonePos", 1), Ps(P("id", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.GetZonePos(Vector2i)", isStatic: true); MethodDefinition val = inj.Find(zs, "GetZone", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "Vector3" && ((MemberReference)((MethodReference)m).ReturnType).FullName == v2sName); if (!((IEnumerable<MethodDefinition>)zs.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "GetZone" && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)inj.Vector2i).FullName)) { if (val == null) { inj.Skip("ZoneSystem.GetZone(Vector3) -> Vector2i", "no GetZone(Vector3) -> Vector2s on this build"); } else { bool ok; MethodDefinition val2 = inj.Begin(zs, "GetZone", inj.Vector2i, Ps(P("point", ((ParameterReference)((MethodReference)val).Parameters[0]).ParameterType)), "ZoneSystem.GetZone(Vector3) -> Vector2i", isStatic: true, out ok); if (ok) { VariableDefinition val3 = new VariableDefinition(module.ImportReference(inj.Vector2s)); val2.Body.Variables.Add(val3); val2.Body.InitLocals = true; ILProcessor iLProcessor = val2.Body.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldarg_0); iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val)); iLProcessor.Emit(OpCodes.Stloc_S, val3); EmitV2iFromV2s(module, iLProcessor, val3, inj); iLProcessor.Emit(OpCodes.Ret); inj.Commit(zs, val2, "ZoneSystem.GetZone(Vector3) -> Vector2i"); } } } else { Logger.LogInfo((object)"present natively, no bridge needed: ZoneSystem.GetZone(Vector3) -> Vector2i"); } MethodDefinition val4 = func("SpawnZone", 3); inj.Forward(zs, "SpawnZone", val4, Ps(P("zoneID", inj.Vector2i), P("mode", (val4 != null) ? ((ParameterReference)((MethodReference)val4).Parameters[1]).ParameterType : module.TypeSystem.Int32), P("root", (val4 != null) ? ((ParameterReference)((MethodReference)val4).Parameters[2]).ParameterType : module.TypeSystem.Object)), Plan(Arg.V2s(0), Arg.P(1), Arg.P(2)), "ZoneSystem.SpawnZone(Vector2i, SpawnMode, out GameObject)"); MethodDefinition val5 = func("PlaceVegetation", 7); if (val5 != null) { List<Tuple<string, TypeReference>> list = Ps(P("zoneID", inj.Vector2i)); List<Arg> list2 = new List<Arg> { Arg.V2s(0) }; for (int num = 1; num < ((MethodReference)val5).Parameters.Count; num++) { list.Add(P(((ParameterReference)((MethodReference)val5).Parameters[num]).Name, ((ParameterReference)((MethodReference)val5).Parameters[num]).ParameterType)); list2.Add(Arg.P(num)); } inj.Forward(zs, "PlaceVegetation", val5, list, list2, "ZoneSystem.PlaceVegetation(Vector2i, ...)"); } else { inj.Skip("ZoneSystem.PlaceVegetation(Vector2i, ...)", "no 7-parameter PlaceVegetation(Vector2s, ...) on this build"); } } private static void PatchZNetScene(ModuleDefinition module, Injector inj) { TypeDefinition type = module.GetType("ZNetScene"); if (type != null && inj.Vector2i != null) { string v2sName = ((MemberReference)inj.Vector2s).FullName; MethodDefinition target = inj.Find(type, "InActiveArea", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "Vector3" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == v2sName); MethodDefinition val = inj.Find(type, "InActiveArea", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "Vector3" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).Name == "Vector3"); inj.Forward(type, "InActiveArea", target, Ps(P("zone", inj.Vector2i), P("refCenterZone", inj.Vector2i)), Plan(Arg.ZonePos(0), Arg.V2s(1)), "ZNetScene.InActiveArea(Vector2i zone, Vector2i refCenterZone) -> InActiveArea(GetZonePos(zone), refCenterZone)", isStatic: true); if (val != null) { inj.Forward(type, "InActiveArea", val, Ps(P("zone", inj.Vector2i), P("refPoint", ((ParameterReference)((MethodReference)val).Parameters[1]).ParameterType)), Plan(Arg.ZonePos(0), Arg.P(1)), "ZNetScene.InActiveArea(Vector2i zone, Vector3 refPoint) -> InActiveArea(GetZonePos(zone), refPoint)", isStatic: true); } else { inj.Skip("ZNetScene.InActiveArea(Vector2i, Vector3)", "no static InActiveArea(Vector3, Vector3) on this build"); } inj.Forward(type, "HaveInstanceInSector", inj.Find(type, "HaveInstanceInSector", (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName), Ps(P("sector", inj.Vector2i)), Plan(Arg.V2s(0)), "ZNetScene.HaveInstanceInSector(Vector2i)"); } } private static void PatchZDOMan(ModuleDefinition module, Injector inj) { TypeDefinition type = module.GetType("ZDOMan"); TypeDefinition type2 = module.GetType("ZDO"); if (type != null && type2 != null && inj.Vector2i != null) { string v2sName = ((MemberReference)inj.Vector2s).FullName; MethodDefinition val = inj.Find(type, "FindSectorObjects", (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 4 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName); if (val != null && inj.SimulationDistanceCtor == null) { inj.Skip("ZDOMan.FindSectorObjects(Vector2i, int, int, List<ZDO>, List<ZDO>)", "SimulationDistance(int, int, bool) not found on this build"); } else { inj.Forward(type, "FindSectorObjects", val, Ps(P("sector", inj.Vector2i), P("area", module.TypeSystem.Int32), P("distantArea", module.TypeSystem.Int32), P("sectorObjects", (val != null) ? ((ParameterReference)((MethodReference)val).Parameters[2]).ParameterType : module.TypeSystem.Object), P("distantSectorObjects", (val != null) ? ((ParameterReference)((MethodReference)val).Parameters[3]).ParameterType : module.TypeSystem.Object)), Plan(Arg.V2s(0), Arg.SimDist(1, 2), Arg.P(3), Arg.P(4)), "ZDOMan.FindSectorObjects(Vector2i, int, int, List<ZDO>, List<ZDO>) -> SimulationDistance(area, distantArea, classic: true)"); } FieldDefinition val2 = ((IEnumerable<FieldDefinition>)type.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "m_visitedSectorIndices")); if (val2 == null) { inj.Skip("ZDOMan.FindObjects/FindDistantObjects(Vector2i, List<ZDO>)", "ZDOMan.m_visitedSectorIndices not found on this build"); } else { BridgeFindObjects(module, inj, type, "FindObjects", val2); BridgeFindObjects(module, inj, type, "FindDistantObjects", val2); } BridgeSectorToIndex(module, inj, type); } } private static void BridgeFindObjects(ModuleDefinition module, Injector inj, TypeDefinition zm, string name, FieldDefinition visited) { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) string v2sName = ((MemberReference)inj.Vector2s).FullName; MethodDefinition val = inj.Find(zm, name, (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 3 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName); string label = "ZDOMan." + name + "(Vector2i, List<ZDO>) (clears and reuses this.m_visitedSectorIndices)"; if (val == null) { inj.Skip(label, "no 3-parameter " + name + "(Vector2s, List<ZDO>, HashSet<SectorIndex>) on this build"); } else if (!inj.Has(zm, name, 2, ((MemberReference)inj.Vector2i).FullName)) { bool ok; MethodDefinition val2 = inj.Begin(zm, name, module.TypeSystem.Void, Ps(P("sector", inj.Vector2i), P("objects", ((ParameterReference)((MethodReference)val).Parameters[1]).ParameterType)), label, isStatic: false, out ok); if (ok) { FieldReference val3 = module.ImportReference((FieldReference)(object)visited); MethodReference val4 = new MethodReference("Clear", module.TypeSystem.Void, val3.FieldType) { HasThis = true }; ILProcessor iLProcessor = val2.Body.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldarg_0); iLProcessor.Emit(OpCodes.Ldfld, val3); iLProcessor.Emit(OpCodes.Callvirt, val4); iLProcessor.Emit(OpCodes.Ldarg_0); iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val2).Parameters[0]); iLProcessor.Emit(OpCodes.Ldfld, inj.V2iX); iLProcessor.Emit(OpCodes.Conv_I2); iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val2).Parameters[0]); iLProcessor.Emit(OpCodes.Ldfld, inj.V2iY); iLProcessor.Emit(OpCodes.Conv_I2); iLProcessor.Emit(OpCodes.Newobj, inj.Vector2sCtor); iLProcessor.Emit(OpCodes.Ldarg_S, ((MethodReference)val2).Parameters[1]); iLProcessor.Emit(OpCodes.Ldarg_0); iLProcessor.Emit(OpCodes.Ldfld, val3); iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val)); iLProcessor.Emit(OpCodes.Ret); inj.Commit(zm, val2, label); } } } private static void BridgeSectorToIndex(ModuleDefinition module, Injector inj, TypeDefinition zm) { //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) if (inj.Has(zm, "SectorToIndex", 1, ((MemberReference)inj.Vector2i).FullName)) { return; } TypeDefinition type = module.GetType("ZoneSystem"); MethodDefinition val = inj.Find(type, "SectorToIndex", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "System.Int32" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Int32"); FieldDefinition val2 = ((IEnumerable<FieldDefinition>)zm.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "m_objectsBySector")); if (val == null || val2 == null) { inj.Skip("ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)", "ZoneSystem.SectorToIndex(int, int) or ZDOMan.m_objectsBySector not found on this build"); return; } TypeDefinition obj = ((MethodReference)val).ReturnType.Resolve(); FieldDefinition val3 = ((obj != null) ? ((IEnumerable<FieldDefinition>)obj.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "Sector" && !f.IsStatic)) : null); if (val3 == null) { inj.Skip("ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)", "ZoneSystem.SectorIndex.Sector not found on this build"); return; } bool ok; MethodDefinition val4 = inj.Begin(zm, "SectorToIndex", module.TypeSystem.Int32, Ps(P("s", inj.Vector2i)), "ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)", isStatic: false, out ok); if (ok) { VariableDefinition val5 = new VariableDefinition(module.ImportReference(((MethodReference)val).ReturnType)); val4.Body.Variables.Add(val5); val4.Body.InitLocals = true; ILProcessor iLProcessor = val4.Body.GetILProcessor(); Instruction val6 = iLProcessor.Create(OpCodes.Ldc_I4_M1); Instruction val7 = iLProcessor.Create(OpCodes.Ldloc_S, val5); iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]); iLProcessor.Emit(OpCodes.Ldfld, inj.V2iX); iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]); iLProcessor.Emit(OpCodes.Ldfld, inj.V2iY); iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val)); iLProcessor.Emit(OpCodes.Stloc_S, val5); iLProcessor.Emit(OpCodes.Ldloca_S, val5); iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference((FieldReference)(object)val3)); iLProcessor.Emit(OpCodes.Brtrue_S, val7); iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]); iLProcessor.Emit(OpCodes.Ldfld, inj.V2iX); iLProcessor.Emit(OpCodes.Ldc_I4, -256); iLProcessor.Emit(OpCodes.Bne_Un_S, val6); iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]); iLProcessor.Emit(OpCodes.Ldfld, inj.V2iY); iLProcessor.Emit(OpCodes.Ldc_I4, -256); iLProcessor.Emit(OpCodes.Bne_Un_S, val6); iLProcessor.Append(val7); iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference((FieldReference)(object)val3)); iLProcessor.Emit(OpCodes.Ret); iLProcessor.Append(val6); iLProcessor.Emit(OpCodes.Ret); inj.Commit(zm, val4, "ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)"); } } private static void PatchItemDrop(ModuleDefinition module, Injector inj) { TypeDefinition type = module.GetType("ItemDrop"); if (type == null) { return; } ForwardItemDropZdo(module, inj, type, "SaveToZDO", bridgeTwoArg: true); ForwardItemDropZdo(module, inj, type, "LoadFromZDO", bridgeTwoArg: false); string[] array = new string[2] { "GameObject", "ItemDrop" }; foreach (string firstParam in array) { MethodDefinition val = inj.Find(type, "OnCreateNew", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == firstParam && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Boolean"); if (val == null) { inj.Skip("ItemDrop.OnCreateNew(" + firstParam + ")", "no OnCreateNew(" + firstParam + ", bool) on this build"); continue; } inj.Forward(type, "OnCreateNew", val, Ps(P(((ParameterReference)((MethodReference)val).Parameters[0]).Name, ((ParameterReference)((MethodReference)val).Parameters[0]).ParameterType)), PassThroughPlus(1, Arg.False()), "ItemDrop.OnCreateNew(" + firstParam + ")", isStatic: true); } TypeDefinition val2 = ((IEnumerable<TypeDefinition>)type.NestedTypes).FirstOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((MemberReference)t).Name == "ItemData")); if (val2 == null) { return; } MethodDefinition val3 = inj.Find(val2, "GetTooltip", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 6); if (val3 != null) { List<Tuple<string, TypeReference>> bridgeParams = (from p in ((IEnumerable<ParameterDefinition>)((MethodReference)val3).Parameters).Take(5) select P(((ParameterReference)p).Name, ((ParameterReference)p).ParameterType)).ToList(); inj.Forward(val2, "GetTooltip", val3, bridgeParams, PassThroughPlus(5, Arg.False()), "ItemDrop.ItemData.GetTooltip(ItemData, int, bool, float, int)", isStatic: true); } else { inj.Skip("ItemDrop.ItemData.GetTooltip(ItemData, int, bool, float, int)", "no 6-parameter GetTooltip on this build"); } } private static void ForwardItemDropZdo(ModuleDefinition module, Injector inj, TypeDefinition itemDrop, string name, bool bridgeTwoArg) { MethodDefinition val = inj.Find(itemDrop, name, (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 3 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "ItemData" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[2]).ParameterType).FullName == "System.Int32"); if (val == null) { inj.Skip("ItemDrop." + na
plugins/Valheim10Compatibility.Adapter.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Valheim10Compatibility.Adapter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("BepInEx runtime adapter plugin for Valheim 1.0.")] [assembly: AssemblyFileVersion("1.4.1.0")] [assembly: AssemblyInformationalVersion("1.4.1+392146eab253da24bd329d86438263eeac9a4c7d")] [assembly: AssemblyProduct("Valheim10Compatibility.Adapter")] [assembly: AssemblyTitle("Valheim10Compatibility.Adapter")] [assembly: AssemblyVersion("1.4.1.0")] [module: RefSafetyRules(11)] 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 Valheim10Compatibility.Adapter { [BepInPlugin("com.wubarrk.valheim10adapter", "Valheim10CompatibilityAdapter", "1.4.1")] public class AdapterPlugin : BaseUnityPlugin { public static class ObjectDBShield { [HarmonyPatch(typeof(ObjectDB), "UpdateRegisters")] [HarmonyPrefix] [HarmonyPriority(0)] public static void PrefixUpdateRegisters(ObjectDB __instance) { List<GameObject> list = __instance?.m_items; if (list == null) { return; } Dictionary<int, GameObject> dictionary = new Dictionary<int, GameObject>(list.Count); for (int i = 0; i < list.Count; i++) { GameObject val = list[i]; if (!((Object)(object)val == (Object)null)) { int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)val).name); if (dictionary.TryGetValue(stableHashCode, out var value)) { Log.LogWarning((object)$"[ObjectDBShield] Duplicate item prefab '{((Object)val).name}' (hash {stableHashCode}) in ObjectDB.m_items: kept the first registration ({Describe(value)}), dropped the later one ({Describe(val)}). Vanilla's Dictionary.Add would have thrown here and aborted FejdStartup.Start. A mod registered an item under a name Valheim 1.0 now uses itself; the game stays up, but that mod needs a 1.0 build."); list.RemoveAt(i--); } else { dictionary.Add(stableHashCode, val); } } } } private static string Describe(GameObject prefab) { string text = prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name; if (!string.IsNullOrEmpty(text)) { return $"'{text}', instance {((Object)prefab).GetInstanceID()}"; } return $"no ItemDrop, instance {((Object)prefab).GetInstanceID()}"; } } public const string PluginGUID = "com.wubarrk.valheim10adapter"; public const string PluginName = "Valheim10CompatibilityAdapter"; public const string PluginVersion = "1.4.1"; private static ManualLogSource Log; private Harmony harmony; private static readonly FieldInfo AvailablePiecesByCategory = AccessTools.Field(typeof(PieceTable), "m_availablePiecesByCategory"); private void Awake() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Valheim10CompatibilityAdapter v1.4.1 loading..."); harmony = new Harmony("com.wubarrk.valheim10adapter"); harmony.PatchAll(typeof(AdapterPlugin)); if (ReadObjectDBShieldSwitch()) { harmony.PatchAll(typeof(ObjectDBShield)); Log.LogInfo((object)"[ObjectDBShield] armed on ObjectDB.UpdateRegisters: a later item prefab whose name hash duplicates an earlier one is dropped (first registration wins) instead of aborting FejdStartup.Start."); } else { Log.LogWarning((object)"[ObjectDBShield] disabled in Valheim10Compatibility.cfg ([Shields] ObjectDBDuplicateItems = false); a duplicate item name hash aborts startup as on plain 1.0."); } Log.LogInfo((object)"Valheim10CompatibilityAdapter successfully patched runtime hooks."); } private static bool ReadObjectDBShieldSwitch() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) try { string configPath = Paths.ConfigPath; if (string.IsNullOrEmpty(configPath)) { return true; } return new ConfigFile(Path.Combine(configPath, "Valheim10Compatibility.cfg"), false) { SaveOnConfigSet = false }.Bind<bool>("Shields", "ObjectDBDuplicateItems", true, "Read by the adapter; the patcher writes this key's description.").Value; } catch (Exception ex) { Log.LogWarning((object)("[ObjectDBShield] Could not read Valheim10Compatibility.cfg (" + ex.GetType().Name + ": " + ex.Message + "); the shield stays on.")); return true; } } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } [HarmonyPatch(typeof(World), "SaveWorldFWLData", new Type[] { typeof(DateTime) })] [HarmonyPrefix] public static void PrefixSaveWorldFWLData(World __instance) { try { if (__instance == null || __instance.IsChunkedSave()) { return; } string dBPath = __instance.GetDBPath(); string saveFWLPath = __instance.GetSaveFWLPath(); if (File.Exists(dBPath)) { string text = Path.Combine(Path.GetDirectoryName(dBPath) ?? "", "pre_1.0_chunked_backups", __instance.m_name); Directory.CreateDirectory(text); string text2 = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string text3 = Path.Combine(text, __instance.m_name + "_" + text2 + ".db"); File.Copy(dBPath, text3, overwrite: true); if (File.Exists(saveFWLPath)) { string destFileName = Path.Combine(text, __instance.m_name + "_" + text2 + ".fwl"); File.Copy(saveFWLPath, destFileName, overwrite: true); } Log.LogWarning((object)("[SaveWatchdog] World '" + __instance.m_name + "' is migrating from monolithic DB to Chunked Saves!")); Log.LogInfo((object)("[SaveWatchdog] Successfully generated pre-migration safety backup at: " + text3)); } } catch (Exception arg) { Log.LogError((object)$"[SaveWatchdog] Error executing pre-migration backup for world '{__instance?.m_name}': {arg}"); } } public static void EnsurePieceTableCapacity(PieceTable table, int requiredCategory = -1) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected I4, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected I4, but got Unknown if ((Object)(object)table == (Object)null) { return; } int num = requiredCategory; if (num < 0) { num = (int)table.GetSelectedCategory(); } int num2 = Math.Max(num, 8); if (table.m_pieces != null) { foreach (GameObject piece in table.m_pieces) { if ((Object)(object)piece == (Object)null) { continue; } Piece component = piece.GetComponent<Piece>(); if ((Object)(object)component != (Object)null && (int)component.m_category != 100) { int num3 = (int)component.m_category; if (num3 > num2 && num3 < 100) { num2 = num3; } } } } int num4 = num2 + 1; if (table.m_selectedPiece == null || table.m_selectedPiece.Length < num4) { Array.Resize(ref table.m_selectedPiece, num4); } if (table.m_lastSelectedPiece == null || table.m_lastSelectedPiece.Length < num4) { Array.Resize(ref table.m_lastSelectedPiece, num4); } if (AvailablePiecesByCategory?.GetValue(table) is List<List<Piece>> { Count: >0 } list && list.Count < num4) { while (list.Count < num4) { list.Add(new List<Piece>()); } } } [HarmonyPatch(typeof(PieceTable), "SetSelected")] [HarmonyPrefix] public static void PrefixSetSelected(PieceTable __instance) { EnsurePieceTableCapacity(__instance); } [HarmonyPatch(typeof(PieceTable), "SetCategory", new Type[] { typeof(int) })] [HarmonyPrefix] public static void PrefixSetCategory(PieceTable __instance, int index) { EnsurePieceTableCapacity(__instance, index); } [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] [HarmonyPrefix] public static void PrefixUpdateAvailable(PieceTable __instance) { EnsurePieceTableCapacity(__instance); } [HarmonyPatch(typeof(PieceTable), "LeftPiece")] [HarmonyPatch(typeof(PieceTable), "RightPiece")] [HarmonyPatch(typeof(PieceTable), "DownPiece")] [HarmonyPatch(typeof(PieceTable), "UpPiece")] [HarmonyPrefix] public static void PrefixPieceNavigation(PieceTable __instance) { EnsurePieceTableCapacity(__instance); } [HarmonyPatch(typeof(Hud), "UpdatePieceList")] [HarmonyFinalizer] public static Exception FinalizerUpdatePieceList(Exception __exception) { if (__exception != null) { Log.LogWarning((object)("[BuildMenuShield] Handled UI exception in Hud.UpdatePieceList: " + __exception.Message)); } return null; } [HarmonyPatch(typeof(ZNet), "SendPeerInfo")] [HarmonyPrefix] public static void PrefixSendPeerInfo(ZRpc rpc, string password) { Log.LogInfo((object)("[NetHandshake] Outgoing peer handshake to " + rpc.GetSocket().GetHostName() + " (adapter 1.4.1 for Valheim 1.0.x+; running game " + GameVersionText() + ", network version " + NetworkVersionText() + ").")); } private static string GameVersionText() { try { return Version.GetVersionString(false); } catch { return "unknown"; } } private static string NetworkVersionText() { try { FieldInfo field = typeof(Version).GetField("c_networkVersion", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return "unknown"; } return (field.IsLiteral ? field.GetRawConstantValue() : field.GetValue(null))?.ToString() ?? "unknown"; } catch { return "unknown"; } } } }