using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PapysLandVSGQoL")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PapysLandVSGQoL")] [assembly: AssemblyTitle("PapysLandVSGQoL")] [assembly: AssemblyVersion("1.0.0.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 PapysLandVSGQoL { [BepInPlugin("papysland.vsgqol", "PapysLand VSG QoL", "0.3.6")] public sealed class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static Plugin Instance; private Harmony _harmony; private int _reconcileFrames = -1; private bool _reconciling; private float _nextPeriodicReconcile; private static Type _playerType; private static Type _dispatcherType; private static Type _chainStateType; private static Type _itemAcquiredType; private static FieldInfo _indexedConfigField; private static MethodInfo _getStep; private static MethodInfo _getCounter; private static MethodInfo _setCounter; private static MethodInfo _isComplete; private static MethodInfo _advanceChain; private static MethodInfo _countInInventory; private static MethodInfo _getEffectiveGoals; private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("papysland.vsgqol"); Log.LogInfo((object)"PapysLand VSG QoL 0.3.6 LOOP GUARD chargé."); Log.LogInfo((object)"Mode: compteur = contenu REEL de l'inventaire; contrôle périodique 0,20 s; aucun faux item_acquired."); try { ResolveVsg(); PatchInventoryChanged(); PatchInventoryRemoveMethods(); PatchHumanoidDropItem(); PatchAdvanceChain(); ScheduleReconcile(3); Log.LogInfo((object)"PapysLand VSG QoL 0.3.6 prêt."); } catch (Exception ex) { Log.LogError((object)("Initialisation 0.3.0 impossible: " + ex)); } } private void OnDestroy() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } Instance = null; } private void Update() { if (Time.unscaledTime >= _nextPeriodicReconcile) { _nextPeriodicReconcile = Time.unscaledTime + 0.2f; ReconcileAllActiveInventoryCounters(); } if (_reconcileFrames >= 0) { if (_reconcileFrames > 0) { _reconcileFrames--; return; } _reconcileFrames = -1; ReconcileAllActiveInventoryCounters(); } } internal static void ScheduleReconcile(int frames = 2) { if (!((Object)(object)Instance == (Object)null) && Instance._reconcileFrames < frames) { Instance._reconcileFrames = frames; } } private static void ResolveVsg() { _playerType = AccessTools.TypeByName("Player") ?? throw new Exception("Type Player introuvable."); _dispatcherType = AccessTools.TypeByName("ValheimServerGuide.Triggers.GuidanceDispatcher") ?? throw new Exception("GuidanceDispatcher introuvable."); _chainStateType = AccessTools.TypeByName("ValheimServerGuide.State.ChainState") ?? throw new Exception("ChainState introuvable."); _itemAcquiredType = AccessTools.TypeByName("ValheimServerGuide.Triggers.ItemAcquiredTrigger") ?? throw new Exception("ItemAcquiredTrigger introuvable."); _indexedConfigField = AccessTools.Field(_dispatcherType, "_indexedConfig") ?? throw new Exception("GuidanceDispatcher._indexedConfig introuvable."); _getStep = AccessTools.Method(_chainStateType, "GetStep", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.GetStep introuvable."); _getCounter = AccessTools.Method(_chainStateType, "GetCounter", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.GetCounter introuvable."); _setCounter = AccessTools.Method(_chainStateType, "SetCounter", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.SetCounter introuvable."); _isComplete = AccessTools.Method(_chainStateType, "IsComplete", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.IsComplete introuvable."); _advanceChain = AccessTools.Method(_dispatcherType, "AdvanceChain", (Type[])null, (Type[])null) ?? throw new Exception("GuidanceDispatcher.AdvanceChain introuvable."); _countInInventory = AccessTools.Method(_itemAcquiredType, "CountInInventory", (Type[])null, (Type[])null) ?? throw new Exception("ItemAcquiredTrigger.CountInInventory introuvable."); _getEffectiveGoals = AccessTools.Method(_itemAcquiredType, "GetEffectiveGoals", (Type[])null, (Type[])null) ?? throw new Exception("ItemAcquiredTrigger.GetEffectiveGoals introuvable."); Log.LogInfo((object)"VSG 0.16.1: méthodes LIVE INVENTORY + CHAIN GOALS résolues."); } private void PatchInventoryChanged() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown Type type = AccessTools.TypeByName("Inventory"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "Changed", (Type[])null, (Type[])null)); if (methodInfo == null) { throw new Exception("Inventory.Changed introuvable."); } _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "InventoryChangedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patch Inventory.Changed actif."); } private void PatchInventoryRemoveMethods() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown Type type = AccessTools.TypeByName("Inventory"); if (type == null) { throw new Exception("Type Inventory introuvable."); } int num = 0; foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (!(declaredMethod == null) && (string.Equals(declaredMethod.Name, "RemoveItem", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "RemoveOneItem", StringComparison.Ordinal))) { _harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "InventoryMutationPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } } Log.LogInfo((object)$"Patch Inventory.RemoveItem/RemoveOneItem actif ({num} surcharge(s))."); } private void PatchHumanoidDropItem() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown Type type = AccessTools.TypeByName("Humanoid"); if (type == null) { throw new Exception("Type Humanoid introuvable."); } int num = 0; foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (!(declaredMethod == null) && string.Equals(declaredMethod.Name, "DropItem", StringComparison.Ordinal)) { _harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "DropItemPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } } Log.LogInfo((object)$"Patch Humanoid.DropItem actif ({num} surcharge(s))."); } public static void InventoryMutationPostfix(object __instance) { try { object localInventory = GetLocalInventory(); if (localInventory != null && localInventory == __instance) { ScheduleReconcile(); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("InventoryMutationPostfix: " + ex.Message)); } } } public static void DropItemPostfix(object __instance) { try { object localPlayer = GetLocalPlayer(); if (localPlayer != null && localPlayer == __instance) { ScheduleReconcile(3); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("DropItemPostfix: " + ex.Message)); } } } private void PatchAdvanceChain() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown _harmony.Patch((MethodBase)_advanceChain, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AdvanceChainPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patch GuidanceDispatcher.AdvanceChain actif."); } public static void InventoryChangedPostfix(object __instance) { try { object localInventory = GetLocalInventory(); if (localInventory != null && localInventory == __instance) { ScheduleReconcile(); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("InventoryChangedPostfix: " + ex.Message)); } } } public static void AdvanceChainPostfix() { ScheduleReconcile(3); } private void ReconcileAllActiveInventoryCounters() { if (_reconciling) { return; } object localPlayer = GetLocalPlayer(); if (localPlayer == null) { return; } object value = _indexedConfigField.GetValue(null); if (value == null || !(GetMember(value, "Guidances") is IEnumerable enumerable)) { return; } _reconciling = true; try { for (int i = 0; i < 12; i++) { bool flag = false; foreach (object item in enumerable) { if (item == null) { continue; } string text = GetString(item, "Id"); if (string.IsNullOrWhiteSpace(text) || Convert.ToBoolean(_isComplete.Invoke(null, new object[2] { localPlayer, text })) || !(GetMember(item, "Steps") is IList { Count: not 0 } list)) { continue; } int num = Convert.ToInt32(_getStep.Invoke(null, new object[2] { localPlayer, text })); if (num < 0 || num >= list.Count) { continue; } object obj = list[num]; if (obj == null) { continue; } object member = GetMember(obj, "Trigger"); if (member != null && Eq(GetString(member, "Type"), "entry_finished")) { string text2 = GetString(member, "Entry"); if (!string.IsNullOrWhiteSpace(text2) && Convert.ToBoolean(_isComplete.Invoke(null, new object[2] { localPlayer, text2 }))) { List list2 = new List(); _advanceChain.Invoke(null, new object[4] { item, num, localPlayer, list2 }); Log.LogInfo((object)("[PREREQ] " + text + ": prérequis " + text2 + " déjà terminé -> étape avancée.")); string[] array = list2.ToArray(); for (int j = 0; j < array.Length; j++) { PropagateEntryFinished(array[j], localPlayer, enumerable); } flag = true; break; } } int num2 = GetInt(obj, "ProgressGoal", 0); object member2 = GetMember(obj, "ProgressTrigger"); if (num2 > 0 && member2 != null && Eq(GetString(member2, "Type"), "item_acquired")) { string text3 = GetString(member2, "Item"); if (string.IsNullOrWhiteSpace(text3)) { text3 = GetString(member2, "Prefab"); } if (!string.IsNullOrWhiteSpace(text3)) { int num3 = Convert.ToInt32(_countInInventory.Invoke(null, new object[2] { localPlayer, text3 })); int num4 = Convert.ToInt32(_getCounter.Invoke(null, new object[3] { localPlayer, text, num })); if (num4 != num3) { _setCounter.Invoke(null, new object[4] { localPlayer, text, num, num3 }); Log.LogInfo((object)$"[LIVE] {text} étape {num + 1}: {text3} = {num3}/{num2} (ancien {num4})"); } if (num3 >= num2) { List list3 = new List(); _advanceChain.Invoke(null, new object[4] { item, num, localPlayer, list3 }); Log.LogInfo((object)$"[LIVE] Objectif compteur satisfait: {text} {text3} {num3}/{num2} -> étape avancée."); string[] array = list3.ToArray(); for (int j = 0; j < array.Length; j++) { PropagateEntryFinished(array[j], localPlayer, enumerable); } flag = true; break; } } } object member3 = GetMember(obj, "Trigger"); if (member3 == null || !Eq(GetString(member3, "Type"), "item_acquired")) { continue; } object obj2 = null; try { obj2 = _getEffectiveGoals.Invoke(null, new object[1] { member3 }); } catch (Exception ex) { Log.LogWarning((object)("GetEffectiveGoals: " + ex.Message)); } if (!(obj2 is IEnumerable enumerable2)) { continue; } bool flag2 = false; bool flag3 = true; List list4 = new List(); foreach (object item2 in enumerable2) { if (item2 == null) { continue; } flag2 = true; string text4 = GetString(item2, "Item"); if (string.IsNullOrWhiteSpace(text4)) { text4 = GetString(item2, "Prefab"); } int num5 = GetInt(item2, "Count", 1); if (num5 <= 0) { num5 = 1; } if (string.IsNullOrWhiteSpace(text4)) { flag3 = false; continue; } int num6 = Convert.ToInt32(_countInInventory.Invoke(null, new object[2] { localPlayer, text4 })); list4.Add($"{text4} {num6}/{num5}"); if (num6 < num5) { flag3 = false; } } if (flag2 && flag3) { List list5 = new List(); _advanceChain.Invoke(null, new object[4] { item, num, localPlayer, list5 }); Log.LogInfo((object)string.Format("[LIVE-GOALS] {0} étape {1}: {2} -> étape avancée.", text, num + 1, string.Join(", ", list4))); string[] array = list5.ToArray(); for (int j = 0; j < array.Length; j++) { PropagateEntryFinished(array[j], localPlayer, enumerable); } flag = true; break; } } if (!flag) { break; } } } catch (TargetInvocationException ex2) { Log.LogError((object)("Réconciliation VSG: " + (ex2.InnerException ?? ex2))); } catch (Exception ex3) { Log.LogError((object)("Réconciliation VSG: " + ex3)); } finally { _reconciling = false; } } private static void PropagateEntryFinished(string completedId, object player, IEnumerable guidances) { if (string.IsNullOrWhiteSpace(completedId) || player == null || guidances == null) { return; } try { foreach (object guidance in guidances) { if (guidance == null) { continue; } string text = GetString(guidance, "Id"); if (string.IsNullOrWhiteSpace(text) || Convert.ToBoolean(_isComplete.Invoke(null, new object[2] { player, text })) || !(GetMember(guidance, "Steps") is IList { Count: not 0 } list)) { continue; } int num = Convert.ToInt32(_getStep.Invoke(null, new object[2] { player, text })); if (num < 0 || num >= list.Count) { continue; } object member = GetMember(list[num], "Trigger"); if (member != null && Eq(GetString(member, "Type"), "entry_finished") && string.Equals(GetString(member, "Entry")?.Trim(), completedId.Trim(), StringComparison.OrdinalIgnoreCase)) { List list2 = new List(); _advanceChain.Invoke(null, new object[4] { guidance, num, player, list2 }); Log.LogInfo((object)("[CHAIN] " + completedId + " terminé -> " + text + " déverrouillée.")); string[] array = list2.ToArray(); for (int i = 0; i < array.Length; i++) { PropagateEntryFinished(array[i], player, guidances); } } } } catch (Exception ex) { Log.LogError((object)("Propagation entry_finished: " + ex)); } } private static object GetLocalPlayer() { if (_playerType == null) { return null; } return AccessTools.Field(_playerType, "m_localPlayer")?.GetValue(null); } private static object GetLocalInventory() { object localPlayer = GetLocalPlayer(); if (localPlayer == null) { return null; } return AccessTools.Method(localPlayer.GetType(), "GetInventory", (Type[])null, (Type[])null)?.Invoke(localPlayer, null); } private static object GetMember(object obj, string name) { if (obj == null) { return null; } Type type = obj.GetType(); PropertyInfo propertyInfo = AccessTools.Property(type, name); if (propertyInfo != null) { try { return propertyInfo.GetValue(obj, null); } catch { } } FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo != null) { try { return fieldInfo.GetValue(obj); } catch { } } return null; } private static string GetString(object obj, string name) { return GetMember(obj, name)?.ToString(); } private static int GetInt(object obj, string name, int fallback) { object member = GetMember(obj, name); if (member == null) { return fallback; } try { return Convert.ToInt32(member); } catch { return fallback; } } private static bool Eq(string a, string b) { return string.Equals(a?.Trim(), b, StringComparison.OrdinalIgnoreCase); } } }