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 OneHandNeededHostversion v3.0.0
OneHandNeeded.dll
Decompiled 5 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("OneHandNeeded")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("OneHandNeeded")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8fd9cbdb-051b-435f-b586-1a9ed221ccda")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace HoppinHauler.OneHandNeeded; [BepInPlugin("HoppinHauler.OneHandNeeded", "One Hand Needed (Host Required)", "3.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "HoppinHauler.OneHandNeeded"; public const string PluginName = "One Hand Needed (Host Required)"; public const string PluginVersion = "3.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<int> Host_TwoHandedBeforeHandsFull; internal static ConfigEntry<bool> Host_SyncConfigToClients; internal static ConfigEntry<int> Client_TwoHandedBeforeHandsFull; internal static volatile bool Runtime_ModActive; internal static volatile int Runtime_TwoHandedBeforeHandsFull; internal static volatile bool Runtime_UsingHostSync; private Harmony _harmony; private HostHandshake _handshake; private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Host_TwoHandedBeforeHandsFull = ((BaseUnityPlugin)this).Config.Bind<int>("Host Settings", "TwoHandedItemsBeforeHandsFull", 0, new ConfigDescription("HOST ONLY. Threshold used by the host.\nMeaning: how many two-handed items you can already have BEFORE picking another two-handed item causes 'Hands Full'.\nExample: value = 2 means you can end up carrying 3 two-handed items (the 3rd is what triggers the check).\nSet to 0 for unlimited (never block due to two-handed items).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); Host_SyncConfigToClients = ((BaseUnityPlugin)this).Config.Bind<bool>("Host Settings", "SyncConfigToClients", true, new ConfigDescription("HOST ONLY. If enabled, the host will push its settings to all clients.\nClients will use host settings at runtime regardless of their local config.\nRecommended: ON for consistent multiplayer behavior.", (AcceptableValueBase)null, Array.Empty<object>())); Client_TwoHandedBeforeHandsFull = ((BaseUnityPlugin)this).Config.Bind<int>("Client Settings", "TwoHandedItemsBeforeHandsFull", 0, new ConfigDescription("CLIENT ONLY fallback. Used only if the host disables SyncConfigToClients.\nIMPORTANT: This mod still requires the host to have it installed.\nSame meaning as host setting: 'how many you can have BEFORE the next pickup causes Hands Full'.\n0 = unlimited.", (AcceptableValueBase)null, Array.Empty<object>())); Runtime_ModActive = false; Runtime_UsingHostSync = false; Runtime_TwoHandedBeforeHandsFull = 0; _harmony = new Harmony("HoppinHauler.OneHandNeeded"); _harmony.PatchAll(); _handshake = ((Component)this).gameObject.AddComponent<HostHandshake>(); Log.LogInfo((object)"One Hand Needed (Host Required) v3.0.0 loaded (host-required)."); } private void OnDestroy() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"UnpatchSelf error: {arg}"); } } } internal static void ApplyHostSettings(int twoHandedBeforeHandsFull, bool usingHostSync) { Runtime_ModActive = true; Runtime_UsingHostSync = usingHostSync; Runtime_TwoHandedBeforeHandsFull = Clamp(twoHandedBeforeHandsFull, 0, 10); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"Handshake OK. Mod active. UsingHostSync={Runtime_UsingHostSync}, TwoHandedBeforeHandsFull={Runtime_TwoHandedBeforeHandsFull}"); } } internal static void ApplyClientFallback() { Runtime_ModActive = true; Runtime_UsingHostSync = false; Runtime_TwoHandedBeforeHandsFull = Clamp(Client_TwoHandedBeforeHandsFull.Value, 0, 10); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"Handshake OK. Host sync OFF. Using client fallback: TwoHandedBeforeHandsFull={Runtime_TwoHandedBeforeHandsFull}"); } } internal static void DisableForSession(string reason) { Runtime_ModActive = false; Runtime_UsingHostSync = false; ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Mod disabled for this session: " + reason)); } } private static int Clamp(int v, int min, int max) { if (v < min) { return min; } if (v > max) { return max; } return v; } } internal sealed class HostHandshake : MonoBehaviour { [CompilerGenerated] private sealed class <ClientRequestAndWait>d__9 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public HostHandshake <>4__this; private NetworkManager <nm>5__1; private float <wait>5__2; private FastBufferWriter <writer>5__3; private Exception <e>5__4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ClientRequestAndWait>d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <nm>5__1 = null; <e>5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_00a8: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <nm>5__1 = NetworkManager.Singleton; if ((Object)(object)<nm>5__1 == (Object)null || <nm>5__1.CustomMessagingManager == null) { Plugin.DisableForSession("CustomMessagingManager not available."); return false; } <>4__this._clientReceivedResponse = false; try { <writer>5__3 = new FastBufferWriter(4, (Allocator)2, -1); try { ref FastBufferWriter reference = ref <writer>5__3; int num = 0; ((FastBufferWriter)(ref reference)).WriteValueSafe<int>(ref num, default(ForPrimitives)); <nm>5__1.CustomMessagingManager.SendNamedMessage("HoppinHauler.OneHandNeeded/ConfigRequest", 0uL, <writer>5__3, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref <writer>5__3)).Dispose(); } } catch (Exception ex) { <e>5__4 = ex; Plugin.DisableForSession("Failed to send config request: " + <e>5__4.GetType().Name); return false; } <wait>5__2 = 3.5f; break; case 1: <>1__state = -1; break; } if (<wait>5__2 > 0f && !<>4__this._clientReceivedResponse) { <wait>5__2 -= Time.unscaledDeltaTime; <>2__current = null; <>1__state = 1; return true; } if (!<>4__this._clientReceivedResponse) { Plugin.DisableForSession("Host did not respond (host likely does not have the mod)."); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <InitRoutine>d__5 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public HostHandshake <>4__this; private float <timeout>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InitRoutine>d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <timeout>5__1 = 12f; goto IL_0066; case 1: <>1__state = -1; goto IL_0066; case 2: { <>1__state = -1; break; } IL_0066: if ((Object)(object)NetworkManager.Singleton == (Object)null && <timeout>5__1 > 0f) { <timeout>5__1 -= Time.unscaledDeltaTime; <>2__current = null; <>1__state = 1; return true; } if ((Object)(object)NetworkManager.Singleton == (Object)null) { Plugin.DisableForSession("NetworkManager not found."); return false; } <>4__this.TryRegisterHandlers(); if (NetworkManager.Singleton.IsServer) { Plugin.ApplyHostSettings(Plugin.Host_TwoHandedBeforeHandsFull.Value, Plugin.Host_SyncConfigToClients.Value); break; } <>2__current = ((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.ClientRequestAndWait()); <>1__state = 2; return true; } NetworkManager.Singleton.OnClientConnectedCallback += <>4__this.OnClientConnected; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string Msg_Request = "HoppinHauler.OneHandNeeded/ConfigRequest"; private const string Msg_Response = "HoppinHauler.OneHandNeeded/ConfigResponse"; private bool _started; private bool _clientReceivedResponse; private void Start() { if (!_started) { _started = true; ((MonoBehaviour)this).StartCoroutine(InitRoutine()); } } [IteratorStateMachine(typeof(<InitRoutine>d__5))] private IEnumerator InitRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <InitRoutine>d__5(0) { <>4__this = this }; } private void OnDestroy() { try { if ((Object)(object)NetworkManager.Singleton != (Object)null) { NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnected; } } catch { } } private void OnClientConnected(ulong clientId) { TryRegisterHandlers(); } private void TryRegisterHandlers() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null) { return; } if (singleton.IsServer) { try { singleton.CustomMessagingManager.RegisterNamedMessageHandler("HoppinHauler.OneHandNeeded/ConfigRequest", new HandleNamedMessageDelegate(OnHostReceivedRequest)); } catch { } } if (singleton.IsServer) { return; } try { singleton.CustomMessagingManager.RegisterNamedMessageHandler("HoppinHauler.OneHandNeeded/ConfigResponse", new HandleNamedMessageDelegate(OnClientReceivedResponse)); } catch { } } [IteratorStateMachine(typeof(<ClientRequestAndWait>d__9))] private IEnumerator ClientRequestAndWait() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ClientRequestAndWait>d__9(0) { <>4__this = this }; } private void OnHostReceivedRequest(ulong clientId, FastBufferReader reader) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0082: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer) { return; } try { int value = Plugin.Host_TwoHandedBeforeHandsFull.Value; bool value2 = Plugin.Host_SyncConfigToClients.Value; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(5, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref value, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref value2, default(ForPrimitives)); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("HoppinHauler.OneHandNeeded/ConfigResponse", clientId, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Failed to send config response to client {clientId}: {arg}"); } } } private void OnClientReceivedResponse(ulong serverClientId, FastBufferReader reader) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { int twoHandedBeforeHandsFull = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref twoHandedBeforeHandsFull, default(ForPrimitives)); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag, default(ForPrimitives)); _clientReceivedResponse = true; if (flag) { Plugin.ApplyHostSettings(twoHandedBeforeHandsFull, usingHostSync: true); } else { Plugin.ApplyClientFallback(); } } catch (Exception ex) { _clientReceivedResponse = true; Plugin.DisableForSession("Failed to read host config: " + ex.GetType().Name); } } } [HarmonyPatch] internal static class PlayerPatches { [HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")] [HarmonyPostfix] private static void BeginGrabObject_Postfix(PlayerControllerB __instance) { CancelTwoHanded(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "SwitchToItemSlot")] [HarmonyPostfix] private static void SwitchToItemSlot_Postfix(PlayerControllerB __instance) { CancelTwoHanded(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "GrabObjectClientRpc")] [HarmonyPostfix] private static void GrabObjectClientRpc_Postfix(PlayerControllerB __instance) { CancelTwoHanded(__instance); } private static void CancelTwoHanded(PlayerControllerB player) { if (!Plugin.Runtime_ModActive) { return; } try { if ((Object)(object)player == (Object)null || !player.twoHanded || IsHoldingLiveEnemy(player)) { return; } int runtime_TwoHandedBeforeHandsFull = Plugin.Runtime_TwoHandedBeforeHandsFull; if (runtime_TwoHandedBeforeHandsFull == 0) { ForceNotTwoHanded(player); return; } int num = CountTwoHandedItemsInInventory(player); if (num < runtime_TwoHandedBeforeHandsFull) { ForceNotTwoHanded(player); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"CancelTwoHanded exception: {arg}"); } } } private static void ForceNotTwoHanded(PlayerControllerB player) { player.twoHanded = false; if (!((NetworkBehaviour)player).IsOwner) { return; } HUDManager instance = HUDManager.Instance; if (!((Object)(object)instance == (Object)null)) { TextMeshProUGUI holdingTwoHandedItem = instance.holdingTwoHandedItem; if (!((Object)(object)holdingTwoHandedItem == (Object)null)) { ((Behaviour)holdingTwoHandedItem).enabled = false; } } } private static bool IsHoldingLiveEnemy(PlayerControllerB player) { GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer; if ((Object)(object)currentlyHeldObjectServer == (Object)null) { return false; } Component val = (Component)(object)currentlyHeldObjectServer; if ((Object)(object)val == (Object)null) { return false; } EnemyAI component = val.GetComponent<EnemyAI>(); if ((Object)(object)component == (Object)null) { return false; } return !component.isEnemyDead; } private static int CountTwoHandedItemsInInventory(PlayerControllerB player) { int num = 0; GrabbableObject[] itemSlots = player.ItemSlots; if (itemSlots == null) { return 0; } foreach (GrabbableObject val in itemSlots) { if (!((Object)(object)val == (Object)null)) { Item itemProperties = val.itemProperties; if (!((Object)(object)itemProperties == (Object)null) && itemProperties.twoHanded) { num++; } } } return num; } }