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 Unmarked v1.0.0
plugins/Unmarked/Unmarked.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; 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: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("Unmarked")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("(c) 2026 Cody Keats")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+eb85cbb5adb1434d483f517c8b5d0b6bc87ab7f2")] [assembly: AssemblyProduct("Unmarked")] [assembly: AssemblyTitle("Unmarked")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Unmarked { internal static class Blob { public struct Result { public byte[] data; public int cleaned; public int removed; public bool understood; } private struct Record { public int start; public int end; public int cheats; public int prefab; public int stack; public int quality; } public struct Contents { public int prefab; public int stack; public int quality; } private const int CHUNKS_N_CHEATS = 109; private const int ABANDONED_DN = 107; private const int SMALLER = 108; private static readonly List<Record> records = new List<Record>(); public static Result Inventory(byte[] data, Func<int, bool> remove, bool reportOnly) { Result result = default(Result); if (data == null || data.Length < 6) { return result; } int num = BitConverter.ToInt32(data, 0); if (num < 108 || num > 129) { return result; } int num2 = data[4] | (data[5] << 8); int p = 6; records.Clear(); for (int i = 0; i < num2; i++) { if (!Read(data, ref p, HasCheats(num), out var rec)) { return result; } records.Add(rec); } if (p != data.Length) { return result; } result.understood = true; foreach (Record record in records) { if (remove != null && record.prefab != 0 && remove(record.prefab)) { result.removed++; } else if (record.cheats >= 0 && (data[record.cheats] & 1) != 0) { result.cleaned++; } } if ((result.cleaned == 0 && result.removed == 0) || reportOnly) { return result; } if (result.removed == 0) { result.data = (byte[])data.Clone(); { foreach (Record record2 in records) { if (record2.cheats >= 0) { result.data[record2.cheats] &= 254; } } return result; } } List<byte> list = new List<byte>(data.Length); for (int j = 0; j < 4; j++) { list.Add(data[j]); } int num3 = records.Count - result.removed; list.Add((byte)num3); list.Add((byte)(num3 >> 8)); foreach (Record record3 in records) { if (record3.prefab == 0 || !remove(record3.prefab)) { for (int k = record3.start; k < record3.end; k++) { list.Add((k == record3.cheats) ? ((byte)(data[k] & 0xFE)) : data[k]); } } } result.data = list.ToArray(); return result; } public static Result Item(byte[] data, bool reportOnly, out int prefab) { Result result = default(Result); prefab = 0; if (data == null || data.Length < 9) { return result; } int version = data[0]; int p = 1; if (!Read(data, ref p, HasCheats(version), out var rec) || p != data.Length) { return result; } result.understood = true; prefab = rec.prefab; if (rec.cheats < 0 || (data[rec.cheats] & 1) == 0) { return result; } result.cleaned = 1; if (reportOnly) { return result; } result.data = (byte[])data.Clone(); result.data[rec.cheats] &= 254; return result; } public static void List(byte[] data, bool single, List<Contents> into) { into.Clear(); if (data == null || data.Length < 9) { return; } bool cheats; int p; int num; if (single) { cheats = HasCheats(data[0]); p = 1; num = 1; } else { int num2 = BitConverter.ToInt32(data, 0); if (num2 < 108 || num2 > 129) { return; } cheats = HasCheats(num2); num = data[4] | (data[5] << 8); p = 6; } for (int i = 0; i < num; i++) { if (!Read(data, ref p, cheats, out var rec)) { into.Clear(); return; } into.Add(new Contents { prefab = rec.prefab, stack = rec.stack, quality = rec.quality }); } if (p != data.Length) { into.Clear(); } } private static bool HasCheats(int version) { if (version < 109) { return version == 107; } return true; } private static bool Read(byte[] d, ref int p, bool cheats, out Record rec) { rec = new Record { start = p, cheats = -1, stack = 1, quality = 1 }; if (!Skip(d, ref p, 7) || p >= d.Length) { return false; } int num = d[p++]; if ((num & 4) != 0) { if (p + 2 > d.Length) { return false; } rec.quality = d[p] | (d[p + 1] << 8); p += 2; } if ((num & 8) != 0) { if (p + 2 > d.Length) { return false; } rec.stack = d[p] | (d[p + 1] << 8); p += 2; } if ((num & 0x10) != 0 && !Skip(d, ref p, 4)) { return false; } if ((num & 0x20) != 0 && (!Skip(d, ref p, 8) || !SkipString(d, ref p))) { return false; } if ((num & 0x40) != 0) { if (p + 4 > d.Length) { return false; } rec.prefab = BitConverter.ToInt32(d, p); p += 4; } if ((num & 0x80) != 0) { if (p >= d.Length) { return false; } int num2 = d[p++]; if ((num2 & 0x80) != 0) { if (p >= d.Length) { return false; } num2 = ((num2 & 0x7F) << 8) | d[p++]; } for (int i = 0; i < num2; i++) { if (!SkipString(d, ref p) || !SkipString(d, ref p)) { return false; } } } if (cheats) { if (p >= d.Length) { return false; } rec.cheats = p++; } rec.end = p; return true; } private static bool Skip(byte[] d, ref int p, int n) { if (p + n > d.Length) { return false; } p += n; return true; } private static bool SkipString(byte[] d, ref int p) { int num = 0; int num2 = 0; while (true) { if (p >= d.Length || num2 > 28) { return false; } byte b = d[p++]; num |= (b & 0x7F) << num2; if ((b & 0x80) == 0) { break; } num2 += 7; } return Skip(d, ref p, num); } } internal static class Cleaner { [HarmonyPatch(typeof(ZDO), "Deserialize")] private static class Arrived { private static void Postfix(ZDO __instance) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (Plugin.CleanLive.Value && interesting != null && interesting.Contains(__instance.GetPrefab())) { arrived.Add(__instance.m_uid); } } } private static readonly int ItemsHash; private static readonly int ItemDataHash; private static readonly int InUseHash; private static readonly int[] StandHashes; private static readonly int[] FlagHashes; private const int ROUND_BUDGET = 1000; private const int ROUND_CHANGES = 25; private static HashSet<int> interesting; private static HashSet<int> removable; private static readonly HashSet<ZDOID> arrived; private static readonly HashSet<ZDOID> waiting; private static readonly List<ZDOID> work; private static readonly List<Blob.Contents> contents; private static readonly HashSet<long> flagged; private static readonly Dictionary<int, (int stack, int quality, string name)> limits; internal static int Flagged; private static float nextWaiting; private static float nextRound; private static int sector; private static int index; private static bool loadRound; private static int rItems; private static int rObjects; private static int rRemoved; private static int rZdos; private static readonly Dictionary<string, int> rKinds; private static readonly Stopwatch clock; private static double roundMs; private static double slowestMs; private static int roundFrames; private static bool Active { get { if (Plugin.Enabled.Value && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZDOMan.instance != null) { return (Object)(object)ZNetScene.instance != (Object)null; } return false; } } static Cleaner() { ItemsHash = ZDOVars.s_items; ItemDataHash = ZDOVars.s_itemData; InUseHash = ZDOVars.s_inUse; StandHashes = new int[32]; FlagHashes = new int[33]; arrived = new HashSet<ZDOID>(); waiting = new HashSet<ZDOID>(); work = new List<ZDOID>(); contents = new List<Blob.Contents>(); flagged = new HashSet<long>(); limits = new Dictionary<int, (int, int, string)>(); sector = -1; rKinds = new Dictionary<string, int>(); clock = new Stopwatch(); for (int i = 0; i < 32; i++) { StandHashes[i] = StringExtensionMethods.GetStableHashCode(i + "_itemData"); } FlagHashes[0] = ZDOVars.s_cheated; for (int j = 0; j < 32; j++) { FlagHashes[j + 1] = ZDOVars.s_cheatedQueued + j; } } public static void SettingsChanged() { removable = null; } public static void Update() { //IL_0060: 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_006b: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (!Active || (interesting == null && !Load())) { return; } clock.Restart(); if (arrived.Count > 0) { work.Clear(); work.AddRange(arrived); arrived.Clear(); foreach (ZDOID item in work) { Look(ZDOMan.instance.GetZDO(item), live: true); } } if (waiting.Count > 0 && Time.time >= nextWaiting) { nextWaiting = Time.time + 1f; work.Clear(); work.AddRange(waiting); waiting.Clear(); foreach (ZDOID item2 in work) { Look(ZDOMan.instance.GetZDO(item2), live: true); } } Round(); double totalMilliseconds = clock.Elapsed.TotalMilliseconds; if (sector >= 0) { roundMs += totalMilliseconds; roundFrames++; if (totalMilliseconds > slowestMs) { slowestMs = totalMilliseconds; } } } private static bool Load() { ZNetScene instance = ZNetScene.instance; if (instance.m_prefabs == null || instance.m_prefabs.Count == 0) { return false; } interesting = new HashSet<int>(); foreach (GameObject prefab in instance.m_prefabs) { if (!((Object)(object)prefab == (Object)null) && ((Object)(object)prefab.GetComponentInChildren<Container>(true) != (Object)null || (Object)(object)prefab.GetComponent<ItemDrop>() != (Object)null || (Object)(object)prefab.GetComponentInChildren<ItemStand>(true) != (Object)null || (Object)(object)prefab.GetComponentInChildren<ArmorStand>(true) != (Object)null || (Object)(object)prefab.GetComponent<Smelter>() != (Object)null || (Object)(object)prefab.GetComponent<Fermenter>() != (Object)null || (Object)(object)prefab.GetComponent<CookingStation>() != (Object)null || (Object)(object)prefab.GetComponent<Piece>() != (Object)null || (Object)(object)prefab.GetComponent<Destructible>() != (Object)null || (Object)(object)prefab.GetComponent<MineRock5>() != (Object)null || (Object)(object)prefab.GetComponent<Character>() != (Object)null)) { interesting.Add(StringExtensionMethods.GetStableHashCode(((Object)prefab).name)); } } interesting.Remove(StringExtensionMethods.GetStableHashCode("Player")); loadRound = Plugin.CleanOnLoad.Value; nextRound = (loadRound ? 0f : (Time.time + Minutes() * 60f)); return true; } private static float Minutes() { if (!(Plugin.RoundMinutes.Value > 0f)) { return 2.8356862E+36f; } return Plugin.RoundMinutes.Value; } private static void Round() { List<ZDO>[] objectsBySector = ZDOMan.instance.m_objectsBySector; if (objectsBySector == null) { return; } if (sector < 0) { if (Time.time < nextRound) { return; } sector = 0; index = 0; rItems = (rObjects = (rRemoved = (rZdos = 0))); rKinds.Clear(); roundMs = (slowestMs = 0.0); roundFrames = 0; } int num = 1000; int num2 = 0; while (sector < objectsBySector.Length && num > 0 && num2 < 25) { List<ZDO> list = objectsBySector[sector]; if (list == null || index >= list.Count) { sector++; index = 0; continue; } while (index < list.Count && num > 0 && num2 < 25) { if (interesting.Contains(list[index].GetPrefab()) && Look(list[index], live: false)) { num2++; } index++; num--; } } if (sector >= objectsBySector.Length) { sector = -1; nextRound = Time.time + Minutes() * 60f; string text = (Plugin.ReportOnly.Value ? "found (ReportOnly: nothing changed)" : "cleaned"); bool flag = loadRound; loadRound = false; if (flag || rItems + rObjects + rRemoved > 0) { ZLog.Log((object)(string.Format("Unmarked: {0}: {1} marked items {2} in {3} objects, {4} object marks {5}", flag ? "world on load" : "round", rItems, text, rZdos, rObjects, text) + ((rRemoved > 0) ? $", {rRemoved} debug items removed" : "") + $"; round took {roundMs:0} ms over {roundFrames} frames, slowest frame {slowestMs:0.00} ms" + ((Plugin.LogEachClean.Value && rKinds.Count > 0) ? ("; most: " + Top()) : ""))); } } } private static string Top() { List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(rKinds); list.Sort((KeyValuePair<string, int> a, KeyValuePair<string, int> b) => b.Value.CompareTo(a.Value)); List<string> list2 = new List<string>(); for (int num = 0; num < list.Count && num < 12; num++) { list2.Add($"{list[num].Key} x{list[num].Value}"); } return string.Join(", ", list2) + ((list.Count > 12) ? $" and {list.Count - 12} more kinds" : ""); } private static bool Look(ZDO zdo, bool live) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !zdo.IsValid()) { return false; } bool value = Plugin.ReportOnly.Value; int num = 0; int num2 = 0; int num3 = 0; HashSet<int> hashSet = (Plugin.RemoveDebugItems.Value ? Removable() : null); bool flag = false; if (ZDOExtraData.s_byteArrays.TryGetValue(zdo.m_uid, out var value2)) { byte[] data = default(byte[]); if (value2.TryGetValue(ItemsHash, ref data)) { if (zdo.GetInt(InUseHash, 0) == 1) { Blob.Result result = Blob.Inventory(data, (hashSet != null) ? new Func<int, bool>(hashSet.Contains) : null, reportOnly: true); if (result.cleaned + result.removed > 0) { waiting.Add(zdo.m_uid); } } else { Blob.Result result2 = Blob.Inventory(data, (hashSet != null) ? new Func<int, bool>(hashSet.Contains) : null, value); if (result2.data != null) { zdo.Set(ItemsHash, result2.data); } num += result2.cleaned; num2 += result2.removed; } } byte[] data2 = default(byte[]); if (value2.TryGetValue(ItemDataHash, ref data2)) { int prefab; Blob.Result result3 = Blob.Item(data2, value, out prefab); if (hashSet != null && prefab != 0 && hashSet.Contains(prefab)) { num2++; flag = !value; } else { if (result3.data != null) { zdo.Set(ItemDataHash, result3.data); } num += result3.cleaned; } } int[] standHashes = StandHashes; byte[] data3 = default(byte[]); foreach (int num4 in standHashes) { if (value2.TryGetValue(num4, ref data3)) { int prefab2; Blob.Result result4 = Blob.Item(data3, value, out prefab2); if (result4.data != null) { zdo.Set(num4, result4.data); } num += result4.cleaned; } } if (Plugin.FlagImpossible.Value) { Impossible(zdo, value2); } } if (Plugin.CleanObjects.Value && ZDOExtraData.s_ints.TryGetValue(zdo.m_uid, out var value3)) { int[] standHashes = FlagHashes; int num6 = default(int); foreach (int num5 in standHashes) { if (value3.TryGetValue(num5, ref num6) && num6 != 0) { num3++; if (!value) { zdo.Set(num5, 0, false); } } } } if (num + num2 + num3 == 0) { return false; } if (!live) { rItems += num; rRemoved += num2; rObjects += num3; rZdos++; GameObject prefab3 = ZNetScene.instance.GetPrefab(zdo.GetPrefab()); string key = ((prefab3 != null) ? ((Object)prefab3).name : null) ?? zdo.GetPrefab().ToString(); rKinds.TryGetValue(key, out var value4); rKinds[key] = value4 + 1; } else { Tell(zdo, num, num2, num3); } if (flag) { Remove(zdo); } return true; } private static void Impossible(ZDO zdo, BinarySearchDictionary<int, byte[]> arrays) { byte[] data = default(byte[]); if (arrays.TryGetValue(ItemsHash, ref data)) { Blob.List(data, single: false, contents); Impossible(zdo, contents); } byte[] data2 = default(byte[]); if (arrays.TryGetValue(ItemDataHash, ref data2)) { Blob.List(data2, single: true, contents); Impossible(zdo, contents); } int[] standHashes = StandHashes; byte[] data3 = default(byte[]); foreach (int num in standHashes) { if (arrays.TryGetValue(num, ref data3)) { Blob.List(data3, single: true, contents); Impossible(zdo, contents); } } } private static void Impossible(ZDO zdo, List<Blob.Contents> items) { //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) foreach (Blob.Contents item2 in items) { if (item2.prefab == 0) { continue; } if (!limits.TryGetValue(item2.prefab, out (int, int, string) value)) { GameObject prefab = ZNetScene.instance.GetPrefab(item2.prefab); SharedData val = ((prefab == null) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared); value = ((val != null) ? (Mathf.Max(1, val.m_maxStackSize), Mathf.Max(1, val.m_maxQuality), ((Object)ZNetScene.instance.GetPrefab(item2.prefab)).name) : (0, 0, null)); limits[item2.prefab] = value; } if (value.Item3 == null) { continue; } bool flag = item2.stack > value.Item1; bool flag2 = item2.quality > value.Item2; if (!flag && !flag2) { continue; } long item = ((long)((object)Unsafe.As<ZDOID, ZDOID>(ref zdo.m_uid)/*cast due to .constrained prefix*/).GetHashCode() << 32) ^ (uint)(item2.prefab * 31 + item2.stack * 7919 + item2.quality); if (flagged.Add(item)) { Flagged++; GameObject prefab2 = ZNetScene.instance.GetPrefab(zdo.GetPrefab()); Vector3 position = zdo.GetPosition(); ZNetPeer val2 = (zdo.HasOwner() ? ZNet.instance.GetPeer(zdo.GetOwner()) : null); string text = (flag ? $"{value.Item3} x{item2.stack} (most a stack can hold: {value.Item1})" : $"{value.Item3} at quality {item2.quality} (highest: {value.Item2})"); if (flag && flag2) { text += $", quality {item2.quality} (highest: {value.Item2})"; } ZLog.LogWarning((object)(string.Format("Unmarked: impossible item: {0} in {1} at {2:0},{3:0}", text, ((Object)(object)prefab2 != (Object)null) ? ((Object)prefab2).name : "?", position.x, position.z) + ((val2 != null) ? (" (from " + val2.m_playerName + ")") : ""))); } } } private static void Tell(ZDO zdo, int items, int removed, int objects) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab()); string text = (((Object)(object)prefab != (Object)null) ? ((Object)prefab).name : zdo.GetPrefab().ToString()); Vector3 position = zdo.GetPosition(); ZNetPeer val = (zdo.HasOwner() ? ZNet.instance.GetPeer(zdo.GetOwner()) : null); string text2 = (Plugin.ReportOnly.Value ? "found" : "cleaned"); if (Plugin.LogEachClean.Value) { List<string> list = new List<string>(); if (items > 0) { list.Add(string.Format("{0} marked item{1} {2}", items, (items == 1) ? "" : "s", text2)); } if (removed > 0) { list.Add(string.Format("{0} debug item{1} {2}", removed, (removed == 1) ? "" : "s", Plugin.ReportOnly.Value ? "found" : "removed")); } if (objects > 0) { list.Add("its own mark " + text2); } ZLog.Log((object)(string.Format("Unmarked: {0} at {1:0},{2:0}: {3}", text, position.x, position.z, string.Join(", ", list)) + ((val != null) ? (" (from " + val.m_playerName + ")") : ""))); } if (Plugin.NotifyPlayers.Value && val != null && !Plugin.ReportOnly.Value && items + removed > 0 && zdo.GetByteArray(ItemsHash, (byte[])null) != null) { ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, "ShowMessage", new object[2] { 2, string.Format("{0} item{1} here had the cheat mark: cleaned", items + removed, (items + removed == 1) ? "" : "s") }); } } private static HashSet<int> Removable() { if (removable != null) { return removable; } removable = new HashSet<int>(); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<ItemDrop>() != (Object)null && ((Object)prefab).name.EndsWith("Cheat", StringComparison.Ordinal)) { removable.Add(StringExtensionMethods.GetStableHashCode(((Object)prefab).name)); } } string[] array = Plugin.ExtraItems.Value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } removable.Add(StringExtensionMethods.GetStableHashCode(text)); foreach (GameObject prefab2 in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab2 != (Object)null && string.Equals(((Object)prefab2).name, text, StringComparison.OrdinalIgnoreCase)) { removable.Add(StringExtensionMethods.GetStableHashCode(((Object)prefab2).name)); } } } return removable; } internal static void Remove(ZDO zdo) { zdo.SetOwner(ZDOMan.GetSessionID()); ZDOMan.instance.DestroyZDO(zdo); } } [BepInPlugin("com.cocopuff2u.unmarked", "Unmarked", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.cocopuff2u.unmarked"; public const string NAME = "Unmarked"; public const string VERSION = "1.0.0"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> CleanOnLoad; internal static ConfigEntry<bool> CleanLive; internal static ConfigEntry<bool> CleanObjects; internal static ConfigEntry<float> RoundMinutes; internal static ConfigEntry<bool> RemoveDebugItems; internal static ConfigEntry<string> ExtraItems; internal static ConfigEntry<bool> ReportOnly; internal static ConfigEntry<bool> FlagImpossible; internal static ConfigEntry<bool> LogEachClean; internal static ConfigEntry<bool> NotifyPlayers; internal static int Reloads; private FileSystemWatcher watcher; private volatile bool changed; private float reloadAt; private void Awake() { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_0186: Unknown result type (might be due to invalid IL or missing references) Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turns the whole mod on or off"); CleanOnLoad = ((BaseUnityPlugin)this).Config.Bind<bool>("Clean", "CleanOnLoad", true, "Right after the world loads, clear the mark from every item stored in the world and from every marked object"); CleanLive = ((BaseUnityPlugin)this).Config.Bind<bool>("Clean", "CleanLive", true, "Clear the mark from items as players' games send them to the server: chests (once closed), carts, ships, tombstones, items on the ground, item and armour stands. Putting marked items in a chest, closing it and opening it again gives them back clean"); CleanObjects = ((BaseUnityPlugin)this).Config.Bind<bool>("Clean", "CleanObjects", true, "Clear the mark from objects too: smelters, kilns, fermenters and cooking stations that were fed marked items (their output would come out marked), and pieces or containers that are marked themselves"); RoundMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Clean", "RoundMinutes", 10f, new ConfigDescription("Minutes between checks of the whole world, for anything not caught live. 0 turns the rounds off", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>())); RemoveDebugItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Remove", "RemoveDebugItems", false, "Delete the game's developer-only items wherever they are stored: SwordCheat, SledgeCheat, and any other prefab whose name ends in Cheat. These can only come from cheat commands"); ExtraItems = ((BaseUnityPlugin)this).Config.Bind<string>("Remove", "ExtraItems", "", "More items to delete when RemoveDebugItems is on, by prefab name, comma separated (e.g. \"HammerCheat, MyModItem\")"); ReportOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "ReportOnly", false, "Find and log marked items and objects, but change nothing"); FlagImpossible = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "FlagImpossibleItems", false, "Log items the game could never make: a stack bigger than the item's stack size, or a higher quality than it can be upgraded to. Outside cheat tools leave no cheat mark, but they often leave these. Only logs, never changes or deletes anything. Off by default, as mods that raise stack sizes or add upgrade levels would make every big stack show up"); LogEachClean = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "LogEachClean", true, "A log line for every chest, item or object cleaned: what it is, where, and the player whose game sent it"); NotifyPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "NotifyPlayers", true, "Tell the player in the middle of their screen when items they just stored or dropped were cleaned"); new Harmony("com.cocopuff2u.unmarked").PatchAll(Assembly.GetExecutingAssembly()); WatchConfig(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Unmarked 1.0.0 loaded"); } private void Update() { ReloadIfChanged(); Cleaner.Update(); } private void WatchConfig() { try { watcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); watcher.Changed += delegate { changed = true; }; watcher.Created += delegate { changed = true; }; watcher.Renamed += delegate { changed = true; }; watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; watcher.EnableRaisingEvents = true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Settings won't reload by themselves: " + ex.Message)); } } private void ReloadIfChanged() { if (changed) { changed = false; reloadAt = Time.time + 1f; } if (reloadAt == 0f || Time.time < reloadAt) { return; } reloadAt = 0f; try { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { ((BaseUnityPlugin)this).Config.Reload(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } Cleaner.SettingsChanged(); Reloads++; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Settings reloaded"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Settings not reloaded: " + ex.Message)); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }