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 ServersideQoL DuplicateSummonFix v0.1.2
plugins\ServersideQoL.DuplicateSummonFix.dll
Decompiled 20 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ServersideQoL Duplicate Summon Fix")] [assembly: AssemblyDescription("Unofficial server-only compatibility fix for duplicate summon animation keys in ServersideQoL 1.8.0.")] [assembly: AssemblyProduct("ServersideQoL Duplicate Summon Fix")] [assembly: AssemblyCopyright("Copyright (c) 2026 laredson")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2")] [assembly: AssemblyMetadata("Author", "laredson")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.1.2.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 Laredson.ServersideQoLDuplicateSummonFix { [BepInPlugin("laredson.serversideqol.duplicatesummonfix", "ServersideQoL Duplicate Summon Fix", "0.1.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "laredson.serversideqol.duplicatesummonfix"; public const string PluginName = "ServersideQoL Duplicate Summon Fix"; public const string PluginVersion = "0.1.2"; public const string ServersideQoLGuid = "argusmagnus.ServersideQoL"; private Harmony _harmony; internal static ManualLogSource Log { get; private set; } internal static ConfigEntry<DuplicatePolicy> Policy { get; private set; } internal static ConfigEntry<bool> LogDuplicates { get; private set; } private void Awake() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Policy = ((BaseUnityPlugin)this).Config.Bind<DuplicatePolicy>("General", "DuplicatePolicy", DuplicatePolicy.Merge, "How duplicate summon animation keys are handled. Merge is recommended. KeepFirst and KeepLast are diagnostic fallback modes."); LogDuplicates = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogDuplicateKeys", true, "Log each duplicate animation key once."); Type type = AccessTools.TypeByName("Valheim.ServersideQoL.Processors.PlayerSpawnedProcessor"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "Initialize", new Type[1] { typeof(bool) }, (Type[])null)); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not find PlayerSpawnedProcessor.Initialize(bool). The installed ServersideQoL version is not compatible with this patch."); return; } _harmony = new Harmony("laredson.serversideqol.duplicatesummonfix"); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(PlayerSpawnedProcessorPatch), "Transpiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patch loaded. Duplicate summon animation keys will no longer abort initialization."); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } public enum DuplicatePolicy { Merge, KeepFirst, KeepLast } internal static class PlayerSpawnedProcessorPatch { private static readonly MethodInfo AddOrResolveDefinition = AccessTools.Method(typeof(PlayerSpawnedProcessorPatch), "AddOrResolve", (Type[])null, (Type[])null); private static readonly object LoggedKeysLock = new object(); private static readonly HashSet<string> LoggedKeys = new HashSet<string>(StringComparer.Ordinal); internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { int replacements = 0; foreach (CodeInstruction instruction in instructions) { if (IsTargetDictionaryAdd(instruction, out var keyType, out var valueType)) { MethodInfo operand = AddOrResolveDefinition.MakeGenericMethod(keyType, valueType); instruction.opcode = OpCodes.Call; instruction.operand = operand; replacements++; yield return instruction; } else { yield return instruction; } } switch (replacements) { case 1: Plugin.Log.LogInfo((object)"Patched PlayerSpawnedProcessor summon animation dictionary insertion."); break; case 0: Plugin.Log.LogError((object)"Expected dictionary insertion was not found. No compatibility fix was applied."); break; default: Plugin.Log.LogWarning((object)("Patched " + replacements + " matching dictionary insertions; expected exactly one.")); break; } } private static bool IsTargetDictionaryAdd(CodeInstruction instruction, out Type keyType, out Type valueType) { keyType = null; valueType = null; if (instruction.opcode != OpCodes.Callvirt) { return false; } MethodInfo methodInfo = instruction.operand as MethodInfo; if (methodInfo == null || methodInfo.Name != "Add" || methodInfo.DeclaringType == null) { return false; } Type declaringType = methodInfo.DeclaringType; if (!declaringType.IsGenericType || declaringType.GetGenericTypeDefinition() != typeof(Dictionary<, >)) { return false; } Type[] genericArguments = declaringType.GetGenericArguments(); if (genericArguments.Length != 2 || genericArguments[0] != typeof(string)) { return false; } Type type = genericArguments[1]; Type declaringType2 = type.DeclaringType; if (type.Name != "SpawnInfo" || declaringType2 == null || declaringType2.Name != "PlayerSpawnedProcessor") { return false; } keyType = genericArguments[0]; valueType = type; return true; } private static void AddOrResolve<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key, TValue incoming) { if (!dictionary.TryGetValue(key, out var value)) { dictionary.Add(key, incoming); return; } DuplicatePolicy duplicatePolicy = ((Plugin.Policy != null) ? Plugin.Policy.Value : DuplicatePolicy.Merge); bool flag = false; string details = string.Empty; switch (duplicatePolicy) { case DuplicatePolicy.KeepLast: dictionary[key] = incoming; details = "kept the last mapping"; break; case DuplicatePolicy.KeepFirst: details = "kept the first mapping"; break; default: flag = SpawnInfoMerger.TryMerge(value, incoming, out details); if (!flag) { details = "merge failed; kept the first mapping. " + details; } break; } LogDuplicateOnce(key, duplicatePolicy, flag, details); } private static void LogDuplicateOnce<TKey>(TKey key, DuplicatePolicy policy, bool merged, string details) { if (Plugin.LogDuplicates != null && !Plugin.LogDuplicates.Value) { return; } string text = ((key == null) ? "<null>" : key.ToString()); lock (LoggedKeysLock) { if (!LoggedKeys.Add(text)) { return; } } Plugin.Log.LogWarning((object)("Duplicate summon animation key '" + text + "' detected. Policy=" + policy.ToString() + "; merged=" + merged + ". " + details)); } } internal static class SpawnInfoMerger { private sealed class Accessors { internal FieldInfo MaxSpawned { get; private set; } internal FieldInfo MaxSummonReached { get; private set; } internal FieldInfo SpawnedByPrefab { get; private set; } internal bool IsValid { get { if (MaxSpawned != null && MaxSummonReached != null) { return SpawnedByPrefab != null; } return false; } } internal Accessors(FieldInfo maxSpawned, FieldInfo maxSummonReached, FieldInfo spawnedByPrefab) { MaxSpawned = maxSpawned; MaxSummonReached = maxSummonReached; SpawnedByPrefab = spawnedByPrefab; } } private static readonly object AccessorLock = new object(); private static readonly Dictionary<Type, Accessors> AccessorCache = new Dictionary<Type, Accessors>(); internal static bool TryMerge<TValue>(TValue existing, TValue incoming, out string details) { details = string.Empty; object obj = existing; object obj2 = incoming; if (obj == null || obj2 == null) { details = "one SpawnInfo value was null"; return false; } Type type = obj.GetType(); if (obj2.GetType() != type) { details = "SpawnInfo values had different runtime types"; return false; } try { Accessors accessors = GetAccessors(type); if (!accessors.IsValid) { details = "required SpawnInfo backing fields were not found"; return false; } int val = (int)accessors.MaxSpawned.GetValue(obj); int val2 = (int)accessors.MaxSpawned.GetValue(obj2); int num = Math.Max(val, val2); accessors.MaxSpawned.SetValue(obj, num); string value = accessors.MaxSummonReached.GetValue(obj) as string; string value2 = accessors.MaxSummonReached.GetValue(obj2) as string; if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(value2)) { accessors.MaxSummonReached.SetValue(obj, value2); } IDictionary dictionary = accessors.SpawnedByPrefab.GetValue(obj) as IDictionary; IDictionary dictionary2 = accessors.SpawnedByPrefab.GetValue(obj2) as IDictionary; if (dictionary == null || dictionary2 == null) { details = "SpawnedByPrefab was not an IDictionary"; return false; } int num2 = 0; int num3 = 0; foreach (DictionaryEntry item in dictionary2) { if (!dictionary.Contains(item.Key)) { dictionary.Add(item.Key, item.Value); num2++; continue; } IList list = dictionary[item.Key] as IList; IEnumerable enumerable = item.Value as IEnumerable; if (list == null || enumerable == null) { continue; } foreach (object item2 in enumerable) { if (!list.Contains(item2)) { list.Add(item2); num3++; } } } details = "merged SpawnInfo; MaxSpawned=" + num + ", added prefab mappings=" + num2 + ", added tracked summon IDs=" + num3 + ". Duplicate staves sharing one animation also share this combined cap."; return true; } catch (Exception ex) { details = ex.GetType().Name + ": " + ex.Message; Plugin.Log.LogError((object)("Failed to merge duplicate SpawnInfo values: " + ex)); return false; } } private static Accessors GetAccessors(Type type) { lock (AccessorLock) { if (AccessorCache.TryGetValue(type, out var value)) { return value; } value = new Accessors(type.GetField("<MaxSpawned>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), type.GetField("<MaxSummonReached>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), type.GetField("<SpawnedByPrefab>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); AccessorCache[type] = value; return value; } } } }