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 Boat Control v0.2.0
FrizzQOL.BoatControl.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; 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 Microsoft.CodeAnalysis; 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: AssemblyCompany("FrizzQOL.BoatControl")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+bfe6d78bc99e7d35d67824480ae96ef4b5484ae2")] [assembly: AssemblyProduct("FrizzQOL.BoatControl")] [assembly: AssemblyTitle("FrizzQOL.BoatControl")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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; } } } public static class DeconstructRules { public static bool? RemovalOverride(bool featureOn, bool isShip, bool playersAboard) { return RemovalOverride(featureOn, isShip, playersAboard, duringWear: false); } public static bool? RemovalOverride(bool featureOn, bool isShip, bool playersAboard, bool duringWear) { if (duringWear || !featureOn || !isShip) { return null; } return !playersAboard; } public static bool ShouldSpillCargo(bool featureOn, bool isShip, bool hammerRemove, bool blockDrop) { if (featureOn && isShip && hammerRemove) { return !blockDrop; } return false; } } public static class StorageRules { public const int MaxWidth = 8; public static int ResolveWidth(int config, int vanilla) { if (config <= 0) { return vanilla; } if (config > 8) { return 8; } return config; } public static int ResolveHeight(int config, int vanilla) { if (config > 0) { return config; } return vanilla; } public static bool CanAddWeight(float currentWeight, float itemWeight, float maxWeight) { if (maxWeight <= 0f) { return true; } return currentWeight + itemWeight <= maxWeight; } } namespace BoatControl { internal static class BoatCatalog { internal sealed class BoatConfig { public int VanillaWidth; public int VanillaHeight; public ConfigEntry<int> Width; public ConfigEntry<int> Height; public ConfigEntry<float> MaxWeight; } private static ConfigFile _config; private static readonly Dictionary<string, BoatConfig> Entries = new Dictionary<string, BoatConfig>(); private static readonly Dictionary<Inventory, string> Inventories = new Dictionary<Inventory, string>(); public static void Initialize(ConfigFile config) { _config = config; } public static void DiscoverFromScene(ZNetScene scene) { if (_config == null || (Object)(object)scene == (Object)null) { return; } if (AccessTools.Field(typeof(ZNetScene), "m_prefabs")?.GetValue(scene) is List<GameObject> { Count: not 0 } list) { int num = 0; foreach (GameObject item in list) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.GetComponent<Ship>() == (Object)null)) { Container componentInChildren = item.GetComponentInChildren<Container>(true); if (!((Object)(object)componentInChildren == (Object)null) && Bind(item, componentInChildren)) { num++; } } } if (num > 0) { Plugin.LogInfo($"Bound config for {num} boat chests."); } } else { Plugin.LogWarning("ZNetScene prefab list empty. Boat config will bind when chests load."); } } public static BoatConfig BindFromContainer(Container container) { Ship val = FindShip(container); if ((Object)(object)val == (Object)null) { return null; } Bind(((Component)val).gameObject, container); return Get(GetPrefabName(((Component)val).gameObject)); } public static BoatConfig Get(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return null; } if (!Entries.TryGetValue(prefabName, out var value)) { return null; } return value; } public static void RegisterInventory(Inventory inventory, string prefabName) { if (inventory != null && !string.IsNullOrEmpty(prefabName)) { Inventories[inventory] = prefabName; } } public static BoatConfig GetForInventory(Inventory inventory) { if (inventory == null) { return null; } if (!Inventories.TryGetValue(inventory, out var value)) { return null; } return Get(value); } public static string GetPrefabName(GameObject obj) { if ((Object)(object)obj == (Object)null) { return null; } string name = ((Object)obj).name; int num = name.IndexOf("(Clone)"); if (num < 0) { return name; } return name.Substring(0, num).Trim(); } public static Ship FindShip(Container container) { if ((Object)(object)container == (Object)null) { return null; } return ((Component)container).GetComponentInParent<Ship>(); } private static bool Bind(GameObject shipObj, Container chest) { string prefabName = GetPrefabName(shipObj); if (string.IsNullOrEmpty(prefabName) || _config == null || Entries.ContainsKey(prefabName)) { return false; } int num = ReadInt(chest, "m_width", 2); int num2 = ReadInt(chest, "m_height", 2); BoatConfig value = new BoatConfig { VanillaWidth = num, VanillaHeight = num2, Width = _config.Bind<int>(prefabName, "Width", num, "Chest columns. 1-8. Values <= 0 keep vanilla."), Height = _config.Bind<int>(prefabName, "Height", num2, "Chest rows. Values <= 0 keep vanilla."), MaxWeight = _config.Bind<float>(prefabName, "MaxWeight", 0f, "Cargo weight cap. 0 or less means no cap.") }; Entries[prefabName] = value; return true; } private static int ReadInt(object instance, string fieldName, int fallback) { FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), fieldName); if (fieldInfo == null) { return fallback; } object value = fieldInfo.GetValue(instance); if (value is int) { return (int)value; } return fallback; } } internal static class DeconstructApplier { private static readonly Dictionary<string, bool> Originals = new Dictionary<string, bool>(); private static readonly FieldInfo ContainerNview = AccessTools.Field(typeof(Container), "m_nview"); private static readonly MethodInfo DropAllItems = AccessTools.Method(typeof(Container), "DropAllItems", Type.EmptyTypes, (Type[])null); internal static Ship FindShip(Component component) { if ((Object)(object)component == (Object)null) { return null; } Ship component2 = component.GetComponent<Ship>(); if ((Object)(object)component2 != (Object)null) { return component2; } return component.GetComponentInChildren<Ship>(true); } internal static void ApplyPrefabs(ZNetScene scene, bool featureOn) { if ((Object)(object)scene == (Object)null || !(AccessTools.Field(typeof(ZNetScene), "m_prefabs")?.GetValue(scene) is List<GameObject> list)) { return; } foreach (GameObject item in list) { if ((Object)(object)item != (Object)null && (Object)(object)item.GetComponent<Ship>() != (Object)null) { ApplyPiece(item, featureOn); } } } internal static void ApplyAllLoaded(bool featureOn) { if ((Object)(object)ZNetScene.instance != (Object)null) { ApplyPrefabs(ZNetScene.instance, featureOn); } Ship[] array = Object.FindObjectsByType<Ship>((FindObjectsSortMode)0); if (array == null) { return; } Ship[] array2 = array; foreach (Ship val in array2) { if ((Object)(object)val != (Object)null) { ApplyPiece(((Component)val).gameObject, featureOn); } } } internal static void ApplyPiece(GameObject shipObject, bool featureOn) { if ((Object)(object)shipObject == (Object)null) { return; } Piece component = shipObject.GetComponent<Piece>(); if ((Object)(object)component == (Object)null) { return; } string prefabName = BoatCatalog.GetPrefabName(shipObject); if (!string.IsNullOrEmpty(prefabName)) { if (!Originals.ContainsKey(prefabName)) { Originals[prefabName] = component.m_canBeRemoved; } component.m_canBeRemoved = featureOn || Originals[prefabName]; } } internal static bool AllowDestroy(WearNTear wear, bool featureOn, bool hammerRemove, bool blockDrop) { if (!featureOn || !hammerRemove || blockDrop) { return true; } Ship val = FindShip((Component)(object)wear); if (!DeconstructRules.ShouldSpillCargo(featureOn, (Object)(object)val != (Object)null, hammerRemove, blockDrop)) { return true; } if (DropAllItems == null) { Plugin.LogError("Container.DropAllItems() was not found. Ship was not removed."); return false; } Container[] componentsInChildren = ((Component)val).GetComponentsInChildren<Container>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (!TryDrop(componentsInChildren[i])) { return false; } } return true; } private static bool TryDrop(Container chest) { if ((Object)(object)chest == (Object)null || chest.GetInventory() == null) { return true; } object? obj = ContainerNview?.GetValue(chest); ZNetView val = (ZNetView)((obj is ZNetView) ? obj : null); if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner()) { return true; } try { DropAllItems.Invoke(chest, null); return true; } catch (Exception ex) { Exception ex2 = ((ex is TargetInvocationException { InnerException: not null } ex3) ? ex3.InnerException : ex); Plugin.LogError("Cargo drop failed. Ship was not removed: " + ex2.Message); return false; } } } [HarmonyPatch(typeof(Ship), "Awake")] internal static class ShipAwakePatch { private static void Postfix(Ship __instance) { if ((Object)(object)__instance != (Object)null) { DeconstructApplier.ApplyPiece(((Component)__instance).gameObject, Plugin.FeatureOn()); } } } [HarmonyPatch(typeof(WearNTear), "UpdateWear")] internal static class WearNTearUpdateWearPatch { [ThreadStatic] private static int s_depth; internal static bool IsActive => s_depth > 0; private static void Prefix() { s_depth++; } private static Exception Finalizer(Exception __exception) { if (s_depth > 0) { s_depth--; } return __exception; } } [HarmonyPatch(typeof(WearNTear), "RPC_Remove")] internal static class WearNTearRpcRemovePatch { [ThreadStatic] private static int s_depth; internal static bool IsActive => s_depth > 0; private static void Prefix() { s_depth++; } private static Exception Finalizer(Exception __exception) { if (s_depth > 0) { s_depth--; } return __exception; } } [HarmonyPatch(typeof(Piece), "CanBeRemoved")] internal static class PieceCanBeRemovedPatch { private static void Postfix(Piece __instance, ref bool __result) { if (!WearNTearUpdateWearPatch.IsActive) { Ship val = DeconstructApplier.FindShip((Component)(object)__instance); bool playersAboard = (Object)(object)val != (Object)null && !val.CanBeRemoved(); bool? flag = DeconstructRules.RemovalOverride(Plugin.FeatureOn(), (Object)(object)val != (Object)null, playersAboard); if (flag.HasValue) { __result = flag.Value; } } } } [HarmonyPatch(typeof(WearNTear), "Destroy")] internal static class WearNTearDestroyPatch { private static bool Prefix(WearNTear __instance, bool blockDrop) { return DeconstructApplier.AllowDestroy(__instance, Plugin.FeatureOn(), WearNTearRpcRemovePatch.IsActive, blockDrop); } } [BepInPlugin("com.frizzqol.boatcontrol", "FrizzQOL Boat Control", "0.2.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.frizzqol.boatcontrol"; public const string PluginName = "FrizzQOL Boat Control"; public const string PluginVersion = "0.2.0"; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ConfigEntry<bool> HammerDeconstruct { get; private set; } private void Awake() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown Instance = this; HammerDeconstruct = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HammerDeconstruct", true, "Middle-click with the hammer deconstructs a ship. Cargo drops on the ground, then the boat materials return. Someone standing on the ship blocks it."); BoatCatalog.Initialize(((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { StorageApplier.ApplyAllLoaded(); DeconstructApplier.ApplyAllLoaded(FeatureOn()); }; _harmony = new Harmony("com.frizzqol.boatcontrol"); try { _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"FrizzQOL Boat Control 0.2.0 loaded"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Harmony patch failed: " + ex.Message)); } } internal static bool FeatureOn() { if (HammerDeconstruct != null) { return HammerDeconstruct.Value; } return false; } internal static void LogInfo(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)message); } } internal static void LogWarning(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)message); } } internal static void LogError(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogError((object)message); } } } internal static class StorageApplier { private static readonly FieldInfo ContainerWidth = AccessTools.Field(typeof(Container), "m_width"); private static readonly FieldInfo ContainerHeight = AccessTools.Field(typeof(Container), "m_height"); private static readonly FieldInfo InventoryWidth = AccessTools.Field(typeof(Inventory), "m_width"); private static readonly FieldInfo InventoryHeight = AccessTools.Field(typeof(Inventory), "m_height"); public static void Apply(Container container) { if ((Object)(object)container == (Object)null) { return; } BoatCatalog.BoatConfig boatConfig = BoatCatalog.BindFromContainer(container); if (boatConfig == null) { return; } int num = StorageRules.ResolveWidth(boatConfig.Width.Value, boatConfig.VanillaWidth); int num2 = StorageRules.ResolveHeight(boatConfig.Height.Value, boatConfig.VanillaHeight); if (ContainerWidth != null) { ContainerWidth.SetValue(container, num); } if (ContainerHeight != null) { ContainerHeight.SetValue(container, num2); } Inventory inventory = container.GetInventory(); if (inventory != null) { if (InventoryWidth != null) { InventoryWidth.SetValue(inventory, num); } inventory.SetHeight(num2); if (InventoryHeight != null) { InventoryHeight.SetValue(inventory, num2); } Ship val = BoatCatalog.FindShip(container); string prefabName = (((Object)(object)val != (Object)null) ? BoatCatalog.GetPrefabName(((Component)val).gameObject) : null); BoatCatalog.RegisterInventory(inventory, prefabName); } } public static void ApplyAllLoaded() { Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); if (array == null) { return; } Container[] array2 = array; foreach (Container container in array2) { if ((Object)(object)BoatCatalog.FindShip(container) != (Object)null) { Apply(container); } } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakePatch { private static void Postfix(ZNetScene __instance) { BoatCatalog.DiscoverFromScene(__instance); DeconstructApplier.ApplyPrefabs(__instance, Plugin.FeatureOn()); } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { StorageApplier.Apply(__instance); } } [HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[] { typeof(ItemData), typeof(int) })] internal static class CanAddItemPatch { private static bool Prefix(Inventory __instance, ItemData __0, int __1, ref bool __result) { if (WeightGate.AllowsWeight(__instance, WeightGate.ItemWeight(__0, __1))) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class AddItemPatch { private static bool Prefix(Inventory __instance, ItemData __0) { return WeightGate.AllowsWeight(__instance, WeightGate.ItemWeight(__0, __0?.m_stack ?? 0)); } } internal static class WeightGate { public static bool AllowsWeight(Inventory inventory, float itemWeight) { BoatCatalog.BoatConfig forInventory = BoatCatalog.GetForInventory(inventory); if (forInventory == null) { return true; } return StorageRules.CanAddWeight(inventory.GetTotalWeight(), itemWeight, forInventory.MaxWeight.Value); } public static float ItemWeight(ItemData item, int stack) { if (item == null || item.m_shared == null) { return 0f; } int num = ((stack <= 0) ? 1 : stack); return item.m_shared.m_weight * (float)num; } } }