using 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 GameNetcodeStuff; using HarmonyLib; using ImprovedTabSlot.Networking; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; 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("Iron.ImprovedTABSlot")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.0.0.0")] [assembly: AssemblyInformationalVersion("2.0.0+a1a0ae3f460207ca065f3f4b61d85da3d504388e")] [assembly: AssemblyProduct("ImprovedTABSlot")] [assembly: AssemblyTitle("Iron.ImprovedTABSlot")] [assembly: AssemblyVersion("2.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 ImprovedTabSlot { internal static class ItemIdentity { internal const int ManeaterItemIdA = 123984; internal const int ManeaterItemIdB = 819501; public static bool IsManeater(GrabbableObject o) { Item val = o?.itemProperties; if ((Object)(object)val != (Object)null) { if (val.itemId != 123984) { return val.itemId == 819501; } return true; } return false; } public static string Name(GrabbableObject o) { if ((Object)(object)o == (Object)null) { return ""; } if ((Object)(object)o.itemProperties != (Object)null && !string.IsNullOrEmpty(o.itemProperties.itemName)) { return o.itemProperties.itemName; } return ((object)o).GetType().Name; } public static bool IsAllowed(GrabbableObject o) { if ((Object)(object)o == (Object)null) { return false; } PluginConfig cfg = Plugin.Cfg; string text = (((Object)(object)o.itemProperties != (Object)null && o.itemProperties.itemName != null) ? o.itemProperties.itemName.Trim().ToLowerInvariant() : ""); if (cfg.AllowShovel.Value) { if (!HasClass(o, "Shovel")) { switch (text) { case "shovel": case "stop sign": case "yield sign": break; default: goto IL_008c; } } return true; } goto IL_008c; IL_008c: if (cfg.AllowShotgun.Value && (HasClass(o, "ShotgunItem") || text == "shotgun")) { return true; } if (cfg.AllowKnife.Value && (HasClass(o, "KnifeItem") || text.Contains("knife"))) { return true; } if (cfg.AllowKey.Value && (HasClass(o, "KeyItem") || text == "key")) { return true; } if (cfg.AllowClipboard.Value && text == "clipboard") { return true; } if (cfg.AllowStickyNote.Value && text == "sticky note") { return true; } if (cfg.AllowAmmo.Value && (text == "ammo" || text.Contains("shell"))) { return true; } if (cfg.AllowManeater.Value && IsManeater(o)) { return true; } return false; } private static bool HasClass(GrabbableObject o, string simpleName) { Type type = ((object)o).GetType(); while (type != null && type != typeof(GrabbableObject)) { if (type.Name == simpleName) { return true; } type = type.BaseType; } return false; } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class UtilitySlotPatch { internal const int UtilitySlotIndex = 50; [HarmonyPrefix] [HarmonyPatch("FirstEmptyItemSlot", new Type[] { typeof(GrabbableObject) })] private static bool FirstEmptyItemSlot_Prefix(PlayerControllerB __instance, GrabbableObject attemptingGrab, ref int __result) { try { if (!HostSync.Enabled) { return true; } if (!Plugin.Cfg.Enabled.Value) { return true; } if ((Object)(object)attemptingGrab == (Object)null) { return true; } if ((Object)(object)__instance.ItemOnlySlot != (Object)null) { return true; } if (!ItemIdentity.IsAllowed(attemptingGrab)) { return true; } __result = 50; if (Plugin.Cfg.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Routing '" + ItemIdentity.Name(attemptingGrab) + "' to the utility slot.")); } return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"FirstEmptyItemSlot_Prefix error: {arg}"); return true; } } } [BepInPlugin("Iron.ImprovedTABSlot", "ImprovedTABSlot", "2.0.0")] public class Plugin : BaseUnityPlugin { public const string PLUGIN_GUID = "Iron.ImprovedTABSlot"; public const string PLUGIN_NAME = "ImprovedTABSlot"; public const string PLUGIN_VERSION = "2.0.0"; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static PluginConfig Cfg { get; private set; } private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Cfg = new PluginConfig(((BaseUnityPlugin)this).Config); _harmony = new Harmony("Iron.ImprovedTABSlot"); try { _harmony.PatchAll(typeof(Plugin).Assembly); VerifyPatched(); } catch (Exception arg) { Log.LogError((object)$"Harmony patching failed; the mod is inactive: {arg}"); } Log.LogInfo((object)"ImprovedTABSlot v2.0.0 loaded."); } private void VerifyPatched() { MethodInfo methodInfo = AccessTools.Method(typeof(PlayerControllerB), "FirstEmptyItemSlot", new Type[1] { typeof(GrabbableObject) }, (Type[])null); if (methodInfo == null) { Log.LogError((object)"PlayerControllerB.FirstEmptyItemSlot(GrabbableObject) was not found. The game version may have changed it; the utility-slot patch is INACTIVE."); return; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); bool flag = patchInfo?.Prefixes != null && patchInfo.Prefixes.Any((Patch p) => p.owner == "Iron.ImprovedTABSlot"); Log.LogInfo((object)(flag ? "Utility-slot patch active on PlayerControllerB.FirstEmptyItemSlot." : "WARNING: utility-slot Prefix did not attach — mod is loaded but inactive.")); } } public class PluginConfig { public readonly ConfigEntry Enabled; public readonly ConfigEntry AllowShovel; public readonly ConfigEntry AllowAmmo; public readonly ConfigEntry AllowClipboard; public readonly ConfigEntry AllowStickyNote; public readonly ConfigEntry AllowKey; public readonly ConfigEntry AllowShotgun; public readonly ConfigEntry AllowKnife; public readonly ConfigEntry AllowManeater; public readonly ConfigEntry VerboseLogging; public PluginConfig(ConfigFile file) { Enabled = file.Bind("General", "Enabled", true, "Master switch. When false the mod does nothing and the vanilla utility-slot blacklist is left untouched."); AllowShovel = file.Bind("Items", "Shovel", true, "Allow the Shovel into the utility slot. (Stop sign / Yield sign reuse the Shovel class and are included.)"); AllowAmmo = file.Bind("Items", "Ammo", true, "Allow Ammo (shotgun shells) into the utility slot."); AllowClipboard = file.Bind("Items", "Clipboard", true, "Allow the Clipboard into the utility slot."); AllowStickyNote = file.Bind("Items", "StickyNote", true, "Allow the Sticky note into the utility slot."); AllowKey = file.Bind("Items", "Key", true, "Allow Keys into the utility slot."); AllowShotgun = file.Bind("Items", "Shotgun", true, "Allow the Shotgun into the utility slot. NOTE: the Shotgun is two-handed, so vanilla's two-handed switching lock still applies once it is in your hands (see README)."); AllowKnife = file.Bind("Items", "Knife", true, "Allow the Kitchen knife into the utility slot."); AllowManeater = file.Bind("Items", "Maneater", false, "Allow the Maneater (baby cave dweller) into the utility (Tab) slot. The utility slot uses the real grab path (EquipItem) which freezes the creature correctly. WARNING: off by design — it's a balance cheese. Set true only if you specifically want it."); VerboseLogging = file.Bind("Debug", "VerboseLogging", false, "Log each time an item is routed to the utility slot, and networking state changes."); } } } namespace ImprovedTabSlot.Patches { [HarmonyPatch] internal static class NetworkPatches { [HarmonyPostfix] [HarmonyPatch(typeof(NetworkManager), "Initialize")] private static void AfterNetworkInitialize() { HostSync.Register(); } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "SetInstanceValuesBackToDefault")] private static void OnNetworkTearDown() { HostSync.Unregister(); } [HarmonyPrefix] [HarmonyPatch(typeof(StartOfRound), "Start")] private static void OnLobbyStart(StartOfRound __instance) { if (((NetworkBehaviour)__instance).IsServer) { HostSync.EnableAsHost(); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "OnClientConnect")] private static void OnClientConnected(StartOfRound __instance, ulong __0) { if (((NetworkBehaviour)__instance).IsServer) { HostSync.SendHostPresent(new ulong[1] { __0 }); } } [HarmonyPrefix] [HarmonyPatch(typeof(MenuManager), "Start")] private static void OnMainMenu() { HostSync.Reset(); } } } namespace ImprovedTabSlot.Networking { internal static class HostSync { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnHostPresent; } internal static bool Enabled; private static readonly string HostPresentMessage = "Iron.ImprovedTABSlot|HostPresent"; internal static void Register() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) != null) { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; string hostPresentMessage = HostPresentMessage; object obj = <>O.<0>__OnHostPresent; if (obj == null) { HandleNamedMessageDelegate val = OnHostPresent; <>O.<0>__OnHostPresent = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler(hostPresentMessage, (HandleNamedMessageDelegate)obj); } } internal static void Unregister() { NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) != null) { singleton.CustomMessagingManager.UnregisterNamedMessageHandler(HostPresentMessage); } } internal static void EnableAsHost() { Enabled = true; if (Plugin.Cfg.VerboseLogging.Value) { Plugin.Log.LogInfo((object)"Host has the mod — utility-slot feature enabled (host)."); } } internal static void Reset() { Enabled = false; } internal static void SendHostPresent(IReadOnlyList targets = null) { //IL_004c: 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) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && singleton.CustomMessagingManager != null) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(0, (Allocator)2, -1); if (targets == null) { singleton.CustomMessagingManager.SendNamedMessageToAll(HostPresentMessage, val, (NetworkDelivery)3); } else { singleton.CustomMessagingManager.SendNamedMessage(HostPresentMessage, targets, val, (NetworkDelivery)3); } } } private static void OnHostPresent(ulong senderId, FastBufferReader reader) { if (senderId == 0L) { Enabled = true; if (Plugin.Cfg.VerboseLogging.Value) { Plugin.Log.LogInfo((object)"Host has the mod — utility-slot feature enabled (client)."); } } } } }