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 DoNotTheHammer v1.0.0
DoNotTheHammer.dll
Decompiled 2 months agousing System; using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace CreatorProtect; [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class Patch_Terminal_InitTerminal { private static bool _registered; private static void Postfix() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!_registered) { _registered = true; new ConsoleCommand("cprot", "CreatorProtect. Subcommands: id | look | scan | list | add <id> | remove <id>", new ConsoleEvent(Commands.Handle), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); Plugin.Log.LogInfo((object)"Registered console command: cprot"); } } } internal static class Commands { internal static void Handle(ConsoleEventArgs args) { Terminal context = args.Context; switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "help") { case "id": Id(context); return; case "look": Look(context); return; case "scan": Scan(context); return; case "list": List(context); return; case "add": Mutate(context, args, add: true); return; case "remove": Mutate(context, args, add: false); return; } context.AddString("cprot id - show the character ID you are logged in as"); context.AddString("cprot look - show the creator ID of the piece you are looking at"); context.AddString("cprot scan - count loaded pieces per creator ID"); context.AddString("cprot list - show the current protection settings"); context.AddString("cprot add <id> - protect pieces created by that ID"); context.AddString("cprot remove <id> - stop protecting that ID"); } private static void Id(Terminal term) { long num = Plugin.LocalPlayerId(); if (num == 0) { term.AddString("No character loaded, so there is no ID to report yet."); return; } term.AddString("Your character ID is " + num.ToString(CultureInfo.InvariantCulture)); term.AddString("Add it to ProtectedCreatorIds to protect everything this character has built."); } private static void Look(Terminal term) { Piece val = RaycastPiece(); if ((Object)(object)val == (Object)null) { term.AddString("No build piece under the crosshair. Stand closer and look straight at it."); return; } long creator = val.GetCreator(); long creator2; bool flag = Plugin.IsProtected(val, out creator2); term.AddString("Piece: " + ((Object)val).name); term.AddString("Creator: " + creator.ToString(CultureInfo.InvariantCulture) + ((creator == 0) ? " (no creator recorded)" : "")); term.AddString("Protected right now: " + (flag ? "yes" : "no")); if (!flag && creator != 0) { term.AddString("Run: cprot add " + creator.ToString(CultureInfo.InvariantCulture) + " to protect this builder."); } } private static void Scan(Terminal term) { List<Piece> list = AllPieces(); if (list == null) { term.AddString("Could not read the piece list from the game."); return; } Dictionary<long, int> dictionary = new Dictionary<long, int>(); for (int i = 0; i < list.Count; i++) { Piece val = list[i]; if (!((Object)(object)val == (Object)null)) { long creator = val.GetCreator(); if (dictionary.TryGetValue(creator, out var value)) { dictionary[creator] = value + 1; } else { dictionary[creator] = 1; } } } List<KeyValuePair<long, int>> list2 = new List<KeyValuePair<long, int>>(dictionary); list2.Sort((KeyValuePair<long, int> a, KeyValuePair<long, int> b) => b.Value.CompareTo(a.Value)); term.AddString("Creators of the " + list.Count + " pieces currently loaded around you:"); int num = 0; int num2 = 0; while (num2 < list2.Count && num < 15) { long key = list2[num2].Key; string text = ((key == 0) ? "0 (no creator recorded)" : key.ToString(CultureInfo.InvariantCulture)); string text2 = (Plugin.IsCreatorProtected(key) ? " [protected]" : ""); term.AddString(" " + list2[num2].Value.ToString(CultureInfo.InvariantCulture) + " pieces <- creator " + text + text2); num2++; num++; } term.AddString("Only loaded zones are counted, so stand in the base you care about."); term.AddString("The creator with by far the most pieces is almost certainly your builder character."); } private static void List(Terminal term) { term.AddString("Protected creators: " + Plugin.DescribeProtected()); term.AddString("ProtectAllCreators: " + Plugin.ProtectAllCreators.Value); term.AddString("ProtectUnownedPieces: " + Plugin.ProtectUnownedPieces.Value); term.AddString("Bypass IDs: " + ((Plugin.BypassPlayerIds.Value.Length == 0) ? "(none)" : Plugin.BypassPlayerIds.Value)); term.AddString("BlockRemoval=" + Plugin.BlockRemoval.Value + " BlockPlayerDamage=" + Plugin.BlockPlayerDamage.Value + " BlockAllDamage=" + Plugin.BlockAllDamage.Value); term.AddString("BlockResourceDrops=" + Plugin.BlockResourceDrops.Value + " PreventCollapse=" + Plugin.PreventCollapse.Value + " ServerEnforcement=" + Plugin.ServerEnforcement.Value); } private static void Mutate(Terminal term, ConsoleEventArgs args, bool add) { if (args.Length < 3) { term.AddString("Usage: cprot " + (add ? "add" : "remove") + " <character id>"); return; } if (!long.TryParse(args[2].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { term.AddString(args[2] + " is not a number."); return; } if (!(add ? Plugin.AddProtectedId(result) : Plugin.RemoveProtectedId(result))) { term.AddString(add ? "That ID was already protected." : "That ID was not in the protected list."); return; } term.AddString((add ? "Now protecting creator " : "No longer protecting creator ") + result.ToString(CultureInfo.InvariantCulture)); term.AddString("Protected creators: " + Plugin.DescribeProtected()); term.AddString("Saved to the config file. Remember the server needs the same setting."); } private static Piece RaycastPiece() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) GameCamera instance = GameCamera.instance; if ((Object)(object)instance == (Object)null) { return null; } int mask = LayerMask.GetMask(new string[5] { "piece", "piece_nonsolid", "static_solid", "Default_small", "terrain" }); RaycastHit val = default(RaycastHit); if (!Physics.Raycast(((Component)instance).transform.position, ((Component)instance).transform.forward, ref val, 50f, mask)) { return null; } if ((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) { return null; } return ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Piece>(); } private static List<Piece> AllPieces() { FieldInfo fieldInfo = AccessTools.Field(typeof(Piece), "s_allPieces"); if (fieldInfo == null) { return null; } return fieldInfo.GetValue(null) as List<Piece>; } } [HarmonyPatch(typeof(Player), "CheckCanRemovePiece")] internal static class Patch_Player_CheckCanRemovePiece { private static void Postfix(Player __instance, Piece piece, ref bool __result) { if (__result && Plugin.BlockRemoval.Value && !Plugin.LocalMayModify(piece)) { __result = false; Plugin.NotifyBlocked(__instance); Plugin.IsProtected(piece, out var creator); Plugin.Trace("Blocked removal of " + ((Object)piece).name + " (creator " + creator + ") by " + Plugin.LocalPlayerId()); } } } [HarmonyPatch(typeof(WearNTear), "Remove")] internal static class Patch_WearNTear_Remove { private static bool Prefix(WearNTear __instance, bool blockDrop) { if (!Plugin.BlockRemoval.Value) { return true; } Piece piece = Plugin.PieceOf((Component)(object)__instance); if (!Plugin.IsProtected(piece, out var creator)) { return true; } if (!Plugin.MayModify(creator, Plugin.LocalPlayerId())) { Plugin.Trace("Blocked WearNTear.Remove on " + ((Object)__instance).name + " (creator " + creator + ")"); return false; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && !component.IsOwner()) { component.ClaimOwnership(); } return true; } } [HarmonyPatch(typeof(WearNTear), "RPC_Remove")] internal static class Patch_WearNTear_RPC_Remove { private static bool Prefix(WearNTear __instance, long sender, bool blockDrop) { if (!Plugin.BlockRemoval.Value || !Plugin.ServerEnforcement.Value) { return true; } Piece piece = Plugin.PieceOf((Component)(object)__instance); if (!Plugin.IsProtected(piece, out var creator)) { return true; } long num = Plugin.ResolveActor(sender); if (Plugin.MayModify(creator, num)) { return true; } Plugin.Trace("Vetoed networked removal of " + ((Object)__instance).name + " (creator " + creator + ") requested by character " + num); return false; } } [HarmonyPatch(typeof(WearNTear), "ApplyDamage")] internal static class Patch_WearNTear_ApplyDamage { private static bool Prefix(WearNTear __instance, float damage, HitData hitData, ref bool __result) { Piece piece = Plugin.PieceOf((Component)(object)__instance); if (!Plugin.IsProtected(piece, out var creator)) { return true; } if (Plugin.BlockAllDamage.Value) { __result = false; return false; } if (!Plugin.BlockPlayerDamage.Value) { return true; } if (hitData == null) { return true; } Character attacker = hitData.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if ((Object)(object)val == (Object)null) { return true; } long playerID = val.GetPlayerID(); if (Plugin.MayModify(creator, playerID)) { return true; } Plugin.Trace("Blocked " + damage + " damage to " + ((Object)__instance).name + " (creator " + creator + ") from character " + playerID); __result = false; return false; } } [HarmonyPatch(typeof(Piece), "DropResources")] internal static class Patch_Piece_DropResources { private static bool Prefix(Piece __instance, HitData hitData) { if (!Plugin.BlockResourceDrops.Value) { return true; } if (!Plugin.IsProtected(__instance, out var creator)) { return true; } if (Plugin.MayModify(creator, Plugin.LocalPlayerId())) { return true; } Plugin.Trace("Suppressed resource drop from " + ((Object)__instance).name + " (creator " + creator + ")"); return false; } } [HarmonyPatch(typeof(WearNTear), "HaveSupport")] internal static class Patch_WearNTear_HaveSupport { private static void Postfix(WearNTear __instance, ref bool __result) { if (!__result && Plugin.PreventCollapse.Value) { Piece piece = Plugin.PieceOf((Component)(object)__instance); if (Plugin.IsProtected(piece, out var _)) { __result = true; } } } } [BepInPlugin("creatorprotect", "CreatorProtect", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "creatorprotect"; public const string Name = "CreatorProtect"; public const string Version = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> ProtectAllCreators; internal static ConfigEntry<string> ProtectedCreatorIds; internal static ConfigEntry<string> BypassPlayerIds; internal static ConfigEntry<bool> ProtectUnownedPieces; internal static ConfigEntry<bool> BlockRemoval; internal static ConfigEntry<bool> BlockPlayerDamage; internal static ConfigEntry<bool> BlockAllDamage; internal static ConfigEntry<bool> BlockResourceDrops; internal static ConfigEntry<bool> PreventCollapse; internal static ConfigEntry<bool> ServerEnforcement; internal static ConfigEntry<bool> ShowMessage; internal static ConfigEntry<string> BlockedMessage; internal static ConfigEntry<bool> Verbose; private static readonly HashSet<long> ProtectedSet = new HashSet<long>(); private static readonly HashSet<long> BypassSet = new HashSet<long>(); private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; BindConfig(); ReloadIds(); new Harmony("creatorprotect").PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)("CreatorProtect 1.0.0 loaded. Protected creators: " + DescribeProtected())); } private void BindConfig() { ProtectedCreatorIds = ((BaseUnityPlugin)this).Config.Bind<string>("1 - Who is protected", "ProtectedCreatorIds", "", "Comma-separated character IDs whose placed pieces are protected. To find an ID, look at a piece in game and run: cprot look. Or run: cprot scan, to list every builder in the loaded area."); ProtectAllCreators = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Who is protected", "ProtectAllCreators", false, "If true, every piece is protected from everyone except whoever placed it, and ProtectedCreatorIds is ignored. This also stops players wrecking each other's builds."); BypassPlayerIds = ((BaseUnityPlugin)this).Config.Bind<string>("1 - Who is protected", "BypassPlayerIds", "", "Comma-separated character IDs allowed to remove and damage protected pieces. Put your admin character here."); ProtectUnownedPieces = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Who is protected", "ProtectUnownedPieces", false, "Also protect pieces with no recorded creator (ID 0). Those are usually world-generated structures, or pieces placed while the client did not own the object. Enabling this protects a great deal."); BlockRemoval = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - What is blocked", "BlockRemoval", true, "Block hammer deconstruction of protected pieces. This is the setting that stops material refunds, and therefore the unintended recipe unlocks."); BlockPlayerDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - What is blocked", "BlockPlayerDamage", true, "Block damage to protected pieces caused by players: pickaxes, weapons, stray arrows, fire."); BlockAllDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - What is blocked", "BlockAllDamage", false, "Block every source of damage to protected pieces, including enemies, raids, weather and decay. Leave this false to keep trolls and raids threatening."); BlockResourceDrops = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - What is blocked", "BlockResourceDrops", true, "Protected pieces drop no build materials unless their own creator removed them. This is the safety net for progression: an enemy smashing a wall cannot hand out materials the players should not have."); PreventCollapse = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - What is blocked", "PreventCollapse", true, "Protected pieces never collapse from lost structural support, so someone removing their own piece cannot bring a protected build down with it."); ServerEnforcement = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Enforcement", "ServerEnforcement", true, "Also veto removal requests that arrive over the network. On a dedicated server this blocks players who do not have the mod installed, for every object the server itself owns."); ShowMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Feedback", "ShowMessage", true, "Show a centre-screen message when a removal is blocked."); BlockedMessage = ((BaseUnityPlugin)this).Config.Bind<string>("4 - Feedback", "BlockedMessage", "Protected build - you did not place this", "The message shown when a removal is blocked."); Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Feedback", "Verbose", false, "Log every blocked action to the BepInEx log. Useful while setting the mod up."); ProtectedCreatorIds.SettingChanged += OnIdsChanged; BypassPlayerIds.SettingChanged += OnIdsChanged; } private static void OnIdsChanged(object sender, EventArgs e) { ReloadIds(); Log.LogInfo((object)("Reloaded IDs. Protected creators: " + DescribeProtected())); } internal static void ReloadIds() { ParseIds(ProtectedCreatorIds.Value, ProtectedSet); ParseIds(BypassPlayerIds.Value, BypassSet); } private static void ParseIds(string raw, HashSet<long> into) { into.Clear(); if (string.IsNullOrEmpty(raw)) { return; } string[] array = raw.Split(new char[4] { ',', ';', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { if (long.TryParse(array[i].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { into.Add(result); } else { Log.LogWarning((object)("Could not parse ID " + array[i] + " - ignoring it.")); } } } private static string Join(HashSet<long> set) { List<string> list = new List<string>(); foreach (long item in set) { list.Add(item.ToString(CultureInfo.InvariantCulture)); } return string.Join(",", list.ToArray()); } internal static string DescribeProtected() { if (ProtectAllCreators.Value) { return "(every creator protects their own pieces)"; } if (ProtectedSet.Count == 0) { return "(none configured)"; } return Join(ProtectedSet); } internal static bool AddProtectedId(long id) { if (!ProtectedSet.Add(id)) { return false; } ProtectedCreatorIds.Value = Join(ProtectedSet); return true; } internal static bool RemoveProtectedId(long id) { if (!ProtectedSet.Remove(id)) { return false; } ProtectedCreatorIds.Value = Join(ProtectedSet); return true; } internal static long LocalPlayerId() { Game instance = Game.instance; if ((Object)(object)instance == (Object)null) { return 0L; } PlayerProfile playerProfile = instance.GetPlayerProfile(); if (playerProfile == null) { return 0L; } return playerProfile.GetPlayerID(); } internal static Piece PieceOf(Component component) { if ((Object)(object)component == (Object)null) { return null; } Piece val = component.GetComponent<Piece>(); if ((Object)(object)val == (Object)null) { val = component.GetComponentInParent<Piece>(); } return val; } internal static bool IsCreatorProtected(long creator) { if (creator == 0) { return ProtectUnownedPieces.Value; } if (ProtectAllCreators.Value) { return true; } return ProtectedSet.Contains(creator); } internal static bool IsProtected(Piece piece, out long creator) { creator = 0L; if ((Object)(object)piece == (Object)null) { return false; } creator = piece.GetCreator(); return IsCreatorProtected(creator); } internal static bool MayModify(long creator, long actor) { if (actor == 0) { return false; } if (actor == creator) { return true; } return BypassSet.Contains(actor); } internal static bool LocalMayModify(Piece piece) { if (!IsProtected(piece, out var creator)) { return true; } return MayModify(creator, LocalPlayerId()); } internal static long ResolveActor(long sender) { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null) { ZNetPeer peer = instance.GetPeer(sender); if (peer != null && peer.m_playerID != 0) { return peer.m_playerID; } } return LocalPlayerId(); } internal static void NotifyBlocked(Player player) { if (!((Object)(object)player == (Object)null) && ShowMessage.Value) { ((Character)player).Message((MessageType)2, BlockedMessage.Value, 0, (Sprite)null, false); } } internal static void Trace(string message) { if (Verbose.Value) { Log.LogInfo((object)message); } } }