Decompiled source of OneHandNeededHostversion v3.0.0

OneHandNeeded.dll

Decompiled 13 hours ago
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;
	}
}