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 NoCheatedTag v1.0.1
NoCheatedTag.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; 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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("FlorpyDorp")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 FlorpyDorp")] [assembly: AssemblyDescription("Removes Valheim's \"summoned through cheating means\" marker from every item and object in the world and in character saves, and stops it coming back.")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+fc466c07e971947027151585c4417e99612fe438")] [assembly: AssemblyProduct("NoCheatedTag")] [assembly: AssemblyTitle("NoCheatedTag")] [assembly: AssemblyVersion("1.0.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 NoCheatedTag { internal static class Blob { private static void Need(byte[] b, int p, int n) { if (n < 0 || p < 0 || p + n > b.Length) { throw new FormatException("truncated"); } } private static int ReadInt(byte[] b, ref int p) { Need(b, p, 4); int result = BitConverter.ToInt32(b, p); p += 4; return result; } private static int ReadUShort(byte[] b, ref int p) { Need(b, p, 2); ushort result = BitConverter.ToUInt16(b, p); p += 2; return result; } private static int ReadByte(byte[] b, ref int p) { Need(b, p, 1); return b[p++]; } private static void Skip(byte[] b, ref int p, int n) { Need(b, p, n); p += n; } private static void SkipString(byte[] b, ref int p) { int num = 0; int num2 = 0; while (true) { if (num2 > 28) { throw new FormatException("bad string length"); } int num3 = ReadByte(b, ref p); num |= (num3 & 0x7F) << num2; if ((num3 & 0x80) == 0) { break; } num2 += 7; } Skip(b, ref p, num); } private static int ReadNumItems(byte[] b, ref int p) { int num = ReadByte(b, ref p); if ((num & 0x80) != 0) { num = ((num & 0x7F) << 8) | ReadByte(b, ref p); } return num; } private static void WalkRecord(byte[] b, ref int p, bool hasCheat, List<int> flagged) { Skip(b, ref p, 7); int num = ReadByte(b, ref p); if ((num & 4) != 0) { Skip(b, ref p, 2); } if ((num & 8) != 0) { Skip(b, ref p, 2); } if ((num & 0x10) != 0) { Skip(b, ref p, 4); } if ((num & 0x20) != 0) { Skip(b, ref p, 8); SkipString(b, ref p); } if ((num & 0x40) != 0) { Skip(b, ref p, 4); } if ((num & 0x80) != 0) { int num2 = ReadNumItems(b, ref p); for (int i = 0; i < num2; i++) { SkipString(b, ref p); SkipString(b, ref p); } } if (hasCheat) { Need(b, p, 1); if ((b[p] & 1) != 0) { flagged.Add(p); } p++; } } private static void WalkOldRecord(byte[] b, ref int p, List<int> flagged) { SkipString(b, ref p); Skip(b, ref p, 17); Skip(b, ref p, 16); SkipString(b, ref p); int num = ReadInt(b, ref p); if (num < 0 || num > 4096) { throw new FormatException("bad custom data count"); } for (int i = 0; i < num; i++) { SkipString(b, ref p); SkipString(b, ref p); } Skip(b, ref p, 5); Need(b, p, 1); if ((b[p] & 1) != 0) { flagged.Add(p); } p++; } public static bool FindInventoryFlags(byte[] b, List<int> flagged) { flagged.Clear(); if (b == null || b.Length < 4) { return true; } try { int p = 0; int num = ReadInt(b, ref p); switch (num) { case 108: case 109: { int num3 = ReadUShort(b, ref p); for (int j = 0; j < num3; j++) { WalkRecord(b, ref p, num >= 109, flagged); } break; } case 107: { int num2 = ReadInt(b, ref p); if (num2 < 0 || num2 > 65535) { throw new FormatException("bad item count"); } for (int i = 0; i < num2; i++) { WalkOldRecord(b, ref p, flagged); } break; } default: return true; } if (p != b.Length) { flagged.Clear(); return false; } return true; } catch { flagged.Clear(); return false; } } public static bool FindItemDataFlags(byte[] b, List<int> flagged) { flagged.Clear(); if (b == null || b.Length <= 2) { return true; } try { int p = 0; int num = ReadByte(b, ref p); if (num != 107 && num != 108 && num != 109) { return true; } WalkRecord(b, ref p, num >= 109 || num == 107, flagged); if (p != b.Length) { flagged.Clear(); return false; } return true; } catch { flagged.Clear(); return false; } } public static byte[] Cleared(byte[] b, List<int> flagged) { byte[] array = (byte[])b.Clone(); foreach (int item in flagged) { array[item] &= 0xFE; } return array; } } internal static class WorldCleaner { public sealed class Result { public int ItemRecords; public int Blobs; public int ObjectFlags; public int SkippedOwned; public int NotUnderstood; public int ZdosWithData; public override string ToString() { return $"{ItemRecords} item record(s) in {Blobs} inventory/item blob(s), {ObjectFlags} object marker(s)" + ((SkippedOwned > 0) ? $", {SkippedOwned} skipped because a connected player owns them (cleaned at the next world load)" : "") + ((NotUnderstood > 0) ? $", {NotUnderstood} blob(s) not understood and left alone" : ""); } } private struct PendingBlob { public ZDOID Id; public int Hash; public byte[] Data; public int[] Offsets; } private struct PendingFlag { public ZDOID Id; public int Hash; } private static readonly int s_items = ZDOVars.s_items; private static readonly HashSet<int> s_itemDataHashes = BuildItemDataHashes(); private static readonly HashSet<int> s_flagHashes = BuildFlagHashes(); private static readonly FieldRef<ZDOMan, Dictionary<ZDOID, ZDO>> f_objectsByID = SafeFieldRef(); private static FieldRef<ZDOMan, Dictionary<ZDOID, ZDO>> SafeFieldRef() { try { return AccessTools.FieldRefAccess<ZDOMan, Dictionary<ZDOID, ZDO>>("m_objectsByID"); } catch { return null; } } private static HashSet<int> BuildItemDataHashes() { HashSet<int> hashSet = new HashSet<int> { ZDOVars.s_itemData }; for (int i = 0; i < 32; i++) { hashSet.Add(StringExtensionMethods.GetStableHashCode(i + "_itemData")); } return hashSet; } private static HashSet<int> BuildFlagHashes() { HashSet<int> hashSet = new HashSet<int> { ZDOVars.s_cheated, ZDOVars.s_cheatedQueued }; for (int i = 0; i < 8; i++) { hashSet.Add(ZDOVars.s_cheatedQueued + i); } return hashSet; } private static bool OwnedByConnectedPeer(ZDO zdo) { long owner = zdo.GetOwner(); if (owner != 0L && owner != ZDOMan.GetSessionID() && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.GetPeer(owner) != null; } return false; } public static Result Run(bool apply, bool live) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) Result result = new Result(); ZDOMan instance = ZDOMan.instance; if (instance == null) { return result; } List<PendingBlob> list = new List<PendingBlob>(); List<PendingFlag> list2 = new List<PendingFlag>(); List<int> list3 = new List<int>(); if (!Collect(list, list2, list3, result)) { CollectSlow(instance, list, list2, list3, result); } foreach (PendingBlob item in list) { result.Blobs++; result.ItemRecords += item.Offsets.Length; } result.ObjectFlags = list2.Count; if (!apply) { return result; } int num = 0; int num2 = 0; int num3 = 0; foreach (PendingBlob item2 in list) { ZDO zDO = instance.GetZDO(item2.Id); if (zDO != null) { if (live && OwnedByConnectedPeer(zDO)) { result.SkippedOwned++; continue; } list3.Clear(); list3.AddRange(item2.Offsets); zDO.Set(item2.Hash, Blob.Cleared(item2.Data, list3)); num++; num2 += item2.Offsets.Length; } } foreach (PendingFlag item3 in list2) { ZDO zDO2 = instance.GetZDO(item3.Id); if (zDO2 != null) { if (live && OwnedByConnectedPeer(zDO2)) { result.SkippedOwned++; continue; } zDO2.Set(item3.Hash, false); num3++; } } result.Blobs = num; result.ItemRecords = num2; result.ObjectFlags = num3; return result; } private static bool Collect(List<PendingBlob> blobs, List<PendingFlag> flags, List<int> scratch, Result r) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) try { FieldInfo fieldInfo = AccessTools.Field(typeof(ZDOExtraData), "s_byteArrays"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(ZDOExtraData), "s_ints"); if (fieldInfo == null || fieldInfo2 == null) { return false; } Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>> dictionary = fieldInfo.GetValue(null) as Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>>; Dictionary<ZDOID, BinarySearchDictionary<int, int>> dictionary2 = fieldInfo2.GetValue(null) as Dictionary<ZDOID, BinarySearchDictionary<int, int>>; if (dictionary == null || dictionary2 == null) { return false; } foreach (KeyValuePair<ZDOID, BinarySearchDictionary<int, byte[]>> item in dictionary) { r.ZdosWithData++; foreach (KeyValuePair<int, byte[]> item2 in item.Value) { Consider(item.Key, item2.Key, item2.Value, blobs, scratch, r); } } foreach (KeyValuePair<ZDOID, BinarySearchDictionary<int, int>> item3 in dictionary2) { foreach (KeyValuePair<int, int> item4 in item3.Value) { if (item4.Value != 0 && s_flagHashes.Contains(item4.Key)) { flags.Add(new PendingFlag { Id = item3.Key, Hash = item4.Key }); } } } return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("fast world scan unavailable (" + ex.GetType().Name + ": " + ex.Message + "); using the slow scan")); blobs.Clear(); flags.Clear(); r.NotUnderstood = 0; r.ZdosWithData = 0; return false; } } private static void CollectSlow(ZDOMan man, List<PendingBlob> blobs, List<PendingFlag> flags, List<int> scratch, Result r) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_00eb: Unknown result type (might be due to invalid IL or missing references) if (f_objectsByID == null) { Plugin.Log.LogError((object)"cannot enumerate world objects; nothing cleaned"); return; } Dictionary<ZDOID, ZDO> dictionary = f_objectsByID.Invoke(man); if (dictionary == null) { return; } foreach (ZDO value in dictionary.Values) { byte[] byteArray = value.GetByteArray(s_items, (byte[])null); if (byteArray != null) { Consider(value.m_uid, s_items, byteArray, blobs, scratch, r); } foreach (int s_itemDataHash in s_itemDataHashes) { byte[] byteArray2 = value.GetByteArray(s_itemDataHash, (byte[])null); if (byteArray2 != null) { Consider(value.m_uid, s_itemDataHash, byteArray2, blobs, scratch, r); } } foreach (int s_flagHash in s_flagHashes) { if (value.GetBool(s_flagHash, false)) { flags.Add(new PendingFlag { Id = value.m_uid, Hash = s_flagHash }); } } } } private static void Consider(ZDOID id, int hash, byte[] data, List<PendingBlob> blobs, List<int> scratch, Result r) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) bool flag; if (hash == s_items) { flag = Blob.FindInventoryFlags(data, scratch); } else { if (!s_itemDataHashes.Contains(hash)) { return; } flag = Blob.FindItemDataFlags(data, scratch); } if (!flag) { r.NotUnderstood++; } else if (scratch.Count > 0) { blobs.Add(new PendingBlob { Id = id, Hash = hash, Data = data, Offsets = scratch.ToArray() }); } } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class Terminal_InitTerminal_Patch { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__1_0; public static ConsoleEvent <>9__1_1; internal void <Postfix>b__1_0(ConsoleEventArgs args) { try { WorldCleaner.Result result = WorldCleaner.Run(apply: false, live: true); args.Context.AddString((Plugin.IsServer ? "world: " : "objects known to this client: ") + result); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return; } int num = 0; foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems()) { if (allItem.m_cheated) { num++; } } args.Context.AddString($"your inventory: {num} marked item(s) in memory"); PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (val != null) { args.Context.AddString("character 'used cheats' marker: " + (val.m_usedCheats ? "set" : "not set")); } } catch (Exception ex) { args.Context.AddString("scan failed: " + ex.Message); } } internal void <Postfix>b__1_1(ConsoleEventArgs args) { if (!Plugin.IsServer) { return; } try { string text = "[nct_clean] " + WorldCleaner.Run(apply: true, live: true); Plugin.Log.LogWarning((object)text); args.Context.AddString(text); } catch (Exception ex) { args.Context.AddString("clean failed: " + ex.Message); Plugin.Log.LogError((object)("nct_clean failed: " + ex)); } } } private static bool s_done; private static void Postfix() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown if (s_done) { return; } s_done = true; object obj = <>c.<>9__1_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { try { WorldCleaner.Result result = WorldCleaner.Run(apply: false, live: true); args.Context.AddString((Plugin.IsServer ? "world: " : "objects known to this client: ") + result); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { int num = 0; foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems()) { if (allItem.m_cheated) { num++; } } args.Context.AddString($"your inventory: {num} marked item(s) in memory"); PlayerProfile val3 = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (val3 != null) { args.Context.AddString("character 'used cheats' marker: " + (val3.m_usedCheats ? "set" : "not set")); } } } catch (Exception ex) { args.Context.AddString("scan failed: " + ex.Message); } }; <>c.<>9__1_0 = val; obj = (object)val; } new ConsoleCommand("nct_scan", "NoCheatedTag: count items and objects that carry the cheated marker (read-only)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__1_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { if (!Plugin.IsServer) { return; } try { string text = "[nct_clean] " + WorldCleaner.Run(apply: true, live: true); Plugin.Log.LogWarning((object)text); args.Context.AddString(text); } catch (Exception ex) { args.Context.AddString("clean failed: " + ex.Message); Plugin.Log.LogError((object)("nct_clean failed: " + ex)); } }; <>c.<>9__1_1 = val2; obj2 = (object)val2; } new ConsoleCommand("nct_clean", "NoCheatedTag: remove the cheated marker from every item and object in the world now. Runs on the server; from a client it is forwarded and needs admin.", (ConsoleEvent)obj2, false, false, true, false, false, false, (ConsoleOptionsFetcher)null, false, true, false); } } [BepInPlugin("FlorpyDorp.NoCheatedTag", "NoCheatedTag", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string GUID = "FlorpyDorp.NoCheatedTag"; public const string NAME = "NoCheatedTag"; public const string VERSION = "1.0.1"; public const string AUTHOR = "FlorpyDorp"; internal static ManualLogSource Log; internal static ConfigEntry<bool> CleanWorldOnLoad; internal static ConfigEntry<bool> CleanItemsOnLoadAndSave; internal static ConfigEntry<bool> PreventNewMarks; internal static ConfigEntry<bool> HideTooltipLine; internal static ConfigEntry<bool> ResetCharacterUsedCheats; private Harmony _harmony; internal static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } private void Awake() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; CleanWorldOnLoad = ((BaseUnityPlugin)this).Config.Bind<bool>("World", "CleanWorldOnLoad", true, "Server / host: right after the world is loaded, clear the cheated bit in every stored item record and remove the cheated marker from every object. Written to disk with the next world save."); CleanItemsOnLoadAndSave = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "CleanItemsOnLoadAndSave", true, "Clear the cheated bit on every item as it is read (inventories, chests you open, items on the ground) and before it is written. This is what fixes a character save: load the character with the mod, and its next save is clean."); PreventNewMarks = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "PreventNewMarks", true, "Stop the game from marking anything new, items or objects: crafting from marked materials or at marked stations, smelting, cooking, fermenting, creature and piece drops, spawned items and creatures, no-cost pieces, hits in god mode."); HideTooltipLine = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "HideTooltipLine", true, "Never show the 'summoned through cheating means' line in item tooltips (it cannot appear anyway once items are clean; this covers items held in memory by other mods)."); ResetCharacterUsedCheats = ((BaseUnityPlugin)this).Config.Bind<bool>("Character", "ResetCharacterUsedCheats", false, "Also clear the character file's permanent 'this character used cheats' marker when the character is loaded (it disables achievements). Off by default: the item tag does not depend on it. The game has its own switch for achievements: the console command 'yesiuseddevcommandsbutiwantmyachievementsanyway 1'."); _harmony = new Harmony("FlorpyDorp.NoCheatedTag"); int num = 0; int num2 = 0; Type[] types = typeof(Plugin).Assembly.GetTypes(); foreach (Type type in types) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { try { _harmony.CreateClassProcessor(type).Patch(); num++; } catch (Exception ex) { num2++; Log.LogError((object)("patch " + type.Name + " failed and is disabled: " + ex.GetType().Name + ": " + ex.Message)); } } } Log.LogInfo((object)string.Format("{0} {1} loaded ({2} patch group(s) applied{3}, {4} methods patched).", "NoCheatedTag", "1.0.1", num, (num2 > 0) ? $", {num2} FAILED" : "", _harmony.GetPatchedMethods().Count())); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static void OnWorldLoaded() { if (!CleanWorldOnLoad.Value || ZDOMan.instance == null) { return; } try { Stopwatch stopwatch = Stopwatch.StartNew(); WorldCleaner.Result result = WorldCleaner.Run(apply: true, live: false); stopwatch.Stop(); if (result.ItemRecords > 0 || result.ObjectFlags > 0 || result.NotUnderstood > 0) { Log.LogWarning((object)$"[clean on load] {result} ({stopwatch.ElapsedMilliseconds} ms). Persisted with the next world save."); } else { Log.LogInfo((object)$"[clean on load] world is clean ({stopwatch.ElapsedMilliseconds} ms)."); } } catch (Exception ex) { Log.LogError((object)("clean on load failed: " + ex)); } } } [HarmonyPatch(typeof(ZDOMan), "LoadChunks")] internal static class ZDOMan_LoadChunks_Patch { private static void Postfix() { Plugin.OnWorldLoaded(); } } [HarmonyPatch(typeof(ZDOMan), "Load")] internal static class ZDOMan_Load_Patch { private static void Postfix() { Plugin.OnWorldLoaded(); } } [HarmonyPatch(typeof(ItemData), "Load", new Type[] { typeof(ZPackage), typeof(ItemData), typeof(Item) })] internal static class ItemData_Load_Patch { private static void Postfix(ItemData itemData) { if (itemData != null && Plugin.CleanItemsOnLoadAndSave.Value) { itemData.m_cheated = false; } } } [HarmonyPatch(typeof(ItemData), "Save", new Type[] { typeof(ZPackage) })] internal static class ItemData_Save_Patch { private static void Prefix(ItemData __instance) { if (Plugin.CleanItemsOnLoadAndSave.Value) { __instance.m_cheated = false; } } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool) })] internal static class ItemData_GetTooltip_Patch { private static void Prefix(ItemData item) { if (item != null && Plugin.HideTooltipLine.Value) { item.m_cheated = false; } } } [HarmonyPatch(typeof(ItemDrop), "OnCreateNew", new Type[] { typeof(ItemDrop), typeof(bool) })] internal static class ItemDrop_OnCreateNew_Patch { private static void Prefix(ref bool cheated) { if (Plugin.PreventNewMarks.Value) { cheated = false; } } } [HarmonyPatch(typeof(CharacterDrop), "DropItems")] internal static class CharacterDrop_DropItems_Patch { private static void Prefix(ref bool cheated) { if (Plugin.PreventNewMarks.Value) { cheated = false; } } } [HarmonyPatch] internal static class Inventory_AddItem_CheatedFlag_Patch { private static IEnumerable<MethodBase> TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Inventory))) { if (declaredMethod.Name == "AddItem" && declaredMethod.GetParameters().Any((ParameterInfo p) => p.ParameterType == typeof(bool) && p.Name == "cheated")) { yield return declaredMethod; } } } private static void Prefix(ref bool cheated) { if (Plugin.PreventNewMarks.Value) { cheated = false; } } } [HarmonyPatch] internal static class Inventory_AddItem_Item_Patch { private static IEnumerable<MethodBase> TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Inventory))) { if (declaredMethod.Name == "AddItem" && declaredMethod.GetParameters().Any((ParameterInfo p) => p.ParameterType == typeof(ItemData))) { yield return declaredMethod; } } } private static void Prefix(object[] __args) { if (__args == null || !Plugin.PreventNewMarks.Value) { return; } foreach (object obj in __args) { ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null) { val.m_cheated = false; } } } } [HarmonyPatch(typeof(ZDO), "Set", new Type[] { typeof(int), typeof(bool) })] internal static class ZDO_SetBool_Patch { private const int MaxQueuedSlots = 8; private static readonly int s_cheated = ZDOVars.s_cheated; private static readonly int s_queued = ZDOVars.s_cheatedQueued; private static void Prefix(int hash, ref bool value) { if (value && Plugin.PreventNewMarks.Value && (hash == s_cheated || (uint)(hash - s_queued) < 8u)) { value = false; } } } [HarmonyPatch(typeof(PlayerProfile), "LoadPlayerFromDisk")] internal static class PlayerProfile_LoadPlayerFromDisk_Patch { private static void Postfix(PlayerProfile __instance, bool __result) { if (__result && Plugin.ResetCharacterUsedCheats.Value && __instance != null && __instance.m_usedCheats) { __instance.m_usedCheats = false; Plugin.Log.LogInfo((object)("character '" + __instance.GetName() + "': 'used cheats' marker cleared (saved with the character)")); } } } }