using 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;
}
}