Decompiled source of RerollHostOnly v4.0.0

RerollHostOnly.dll

Decompiled a week ago
using System;
using System.Collections;
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 BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
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: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("REPOJP")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("zabuMod")]
[assembly: AssemblyTitle("zabuMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 REPOJP.RerollHostOnly
{
	[BepInPlugin("REPOJP.RerollHostOnly", "RerollHostOnly", "4.0.0")]
	public class RerollHostOnlyPlugin : BaseUnityPlugin
	{
		[HarmonyPatch]
		private static class UpgradeStandOnClickPatch
		{
			private static bool Prepare()
			{
				Type upgradeStandType = GetUpgradeStandType();
				if (upgradeStandType != null)
				{
					return AccessTools.Method(upgradeStandType, "OnClick", (Type[])null, (Type[])null) != null;
				}
				return false;
			}

			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(GetUpgradeStandType(), "OnClick", (Type[])null, (Type[])null);
			}

			private static bool Prefix(object __instance)
			{
				try
				{
					if (!IsEnabled())
					{
						return true;
					}
					if (IsLocalAllowedToReroll())
					{
						MarkStandAuthorized(__instance, 20f);
						return true;
					}
					if (ConfigBlockNonHostInteract != null && ConfigBlockNonHostInteract.Value)
					{
						if (ConfigShowBlockedMessage != null && ConfigShowBlockedMessage.Value && ConfigBlockedMessage != null)
						{
							SendPublicMessageWithCooldown(ConfigBlockedMessage.Value, "local_blocked", 1.5f);
						}
						return false;
					}
					return true;
				}
				catch (Exception ex)
				{
					LogFailure("UpgradeStand.OnClick", ex);
					return true;
				}
			}
		}

		[HarmonyPatch]
		private static class UpgradeStandStateSetPatch
		{
			private static bool Prepare()
			{
				Type upgradeStandType = GetUpgradeStandType();
				if (upgradeStandType != null)
				{
					return AccessTools.Method(upgradeStandType, "StateSet", (Type[])null, (Type[])null) != null;
				}
				return false;
			}

			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(GetUpgradeStandType(), "StateSet", (Type[])null, (Type[])null);
			}

			private static bool Prefix(object __instance, object[] __args)
			{
				try
				{
					if (!IsEnabled())
					{
						return true;
					}
					if (__args == null || __args.Length == 0)
					{
						return true;
					}
					if (!StateArgumentIs(__args[0], "Press"))
					{
						return true;
					}
					if (!SemiFunc.IsMasterClientOrSingleplayer())
					{
						return true;
					}
					if (IsStandAuthorized(__instance))
					{
						return true;
					}
					if (StandHasHostGrabber(__instance))
					{
						MarkStandAuthorized(__instance, 20f);
						return true;
					}
					if (StandHasOnlyNonHostGrabber(__instance))
					{
						string message = ((ConfigBlockedMessage != null) ? ConfigBlockedMessage.Value : "Only the host can use the Upgrade Reroller.");
						SendPublicMessageWithCooldown(message, "state_blocked_" + GetStandKey(__instance), 1.5f);
						DebugLog("Blocked unauthorized UpgradeStand.State.Press.");
						return false;
					}
					return true;
				}
				catch (Exception ex)
				{
					LogFailure("UpgradeStand.StateSet", ex);
					return true;
				}
			}

			private static void Postfix(object __instance, object[] __args)
			{
				try
				{
					if (__args != null && __args.Length != 0 && (StateArgumentIs(__args[0], "Idle") || StateArgumentIs(__args[0], "Broken")))
					{
						ClearStandAuthorization(__instance);
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPatch]
		private static class UpgradeStandHandleCostDisplayPatch
		{
			private static bool Prepare()
			{
				Type upgradeStandType = GetUpgradeStandType();
				if (upgradeStandType != null)
				{
					return AccessTools.Method(upgradeStandType, "HandleCostDisplay", (Type[])null, (Type[])null) != null;
				}
				return false;
			}

			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(GetUpgradeStandType(), "HandleCostDisplay", (Type[])null, (Type[])null);
			}

			private static void Postfix(object __instance)
			{
				try
				{
					if (IsEnabled() && ConfigShowHoverTextForNonHost != null && ConfigShowHoverTextForNonHost.Value && !IsLocalAllowedToReroll() && !StandStateIs(__instance, "Broken"))
					{
						object fieldValue = GetFieldValue(__instance, "buttonGrabObject");
						if (fieldValue != null)
						{
							string value = ((ConfigNonHostHoverText != null) ? ConfigNonHostHoverText.Value : "HOST ONLY REROLL");
							SetFieldValue(fieldValue, "hoverText", value);
						}
					}
				}
				catch (Exception ex)
				{
					LogFailure("UpgradeStand.HandleCostDisplay", ex);
				}
			}
		}

		[HarmonyPatch]
		private static class StaticGrabObjectGrabStartedRpcPatch
		{
			private static bool Prepare()
			{
				Type type = AccessTools.TypeByName("StaticGrabObject");
				if (type != null)
				{
					return AccessTools.Method(type, "GrabStartedRPC", (Type[])null, (Type[])null) != null;
				}
				return false;
			}

			private static MethodBase TargetMethod()
			{
				Type type = AccessTools.TypeByName("StaticGrabObject");
				return AccessTools.Method(type, "GrabStartedRPC", (Type[])null, (Type[])null);
			}

			private static bool Prefix(object __instance, int playerPhotonID)
			{
				try
				{
					if (!IsEnabled())
					{
						return true;
					}
					if (!TryGetUpgradeStandFromStaticGrabObject(__instance, out var stand))
					{
						return true;
					}
					if (!SemiFunc.IsMasterClientOrSingleplayer())
					{
						return true;
					}
					return HandleNonHostGrabAttempt(stand, playerPhotonID);
				}
				catch (Exception ex)
				{
					LogFailure("StaticGrabObject.GrabStartedRPC", ex);
					return true;
				}
			}
		}

		public const string PluginGuid = "REPOJP.RerollHostOnly";

		public const string PluginName = "RerollHostOnly";

		public const string PluginVersion = "4.0.0";

		private static ManualLogSource Log;

		private static Harmony harmony;

		private static ConfigEntry<bool> ConfigEnable;

		private static ConfigEntry<bool> ConfigShowDebugLog;

		private static ConfigEntry<bool> ConfigHostOnlyReroll;

		private static ConfigEntry<bool> ConfigAllowSinglePlayer;

		private static ConfigEntry<bool> ConfigBlockNonHostInteract;

		private static ConfigEntry<bool> ConfigShowBlockedMessage;

		private static ConfigEntry<string> ConfigBlockedMessage;

		private static ConfigEntry<bool> ConfigShowHoverTextForNonHost;

		private static ConfigEntry<string> ConfigNonHostHoverText;

		private static Type upgradeStandType;

		private static Type upgradeStandStateType;

		private static Type physGrabberType;

		private static Type playerAvatarType;

		private static readonly Dictionary<int, float> AuthorizedStandUntil = new Dictionary<int, float>();

		private static readonly Dictionary<string, float> MessageCooldownUntil = new Dictionary<string, float>();

		private void Awake()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			try
			{
				((Component)this).transform.parent = null;
				((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				Log = ((BaseUnityPlugin)this).Logger;
				BindConfig();
				CacheTypes();
				harmony = new Harmony("REPOJP.RerollHostOnly");
				harmony.PatchAll(typeof(RerollHostOnlyPlugin).Assembly);
				Log.LogInfo((object)"RerollHostOnly loaded. Version 4.0.0");
			}
			catch (Exception ex)
			{
				if (((BaseUnityPlugin)this).Logger != null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Awake\n" + ex));
				}
			}
		}

		private void OnDestroy()
		{
			try
			{
				if (harmony != null)
				{
					harmony.UnpatchSelf();
					harmony = null;
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogError((object)("Failure: OnDestroy\n" + ex));
				}
			}
		}

		private void BindConfig()
		{
			ConfigEnable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable", true, "Enable this mod.このMODの有効化");
			ConfigShowDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowDebugLog", false, "Show debug logs.デバッグログの表示");
			ConfigHostOnlyReroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Permission", "HostOnlyReroll", true, "Allow only the host to start the Upgrade Reroller.Upgrade Rerollerの開始をホストのみに制限");
			ConfigAllowSinglePlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Permission", "AllowSinglePlayer", true, "Allow normal rerolling in single player.シングルプレイでの通常使用許可");
			ConfigBlockNonHostInteract = ((BaseUnityPlugin)this).Config.Bind<bool>("Permission", "BlockNonHostInteract", true, "Block non-host interactions before reroll starts.非ホスト操作の開始前ブロック");
			ConfigShowBlockedMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("Message", "ShowBlockedMessage", true, "Show a message when a non-host tries to use the reroller.非ホスト操作時メッセージの表示");
			ConfigBlockedMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Message", "BlockedMessage", "Only the host can use the Upgrade Reroller.", "Blocked message text.ブロック時メッセージ");
			ConfigShowHoverTextForNonHost = ((BaseUnityPlugin)this).Config.Bind<bool>("Message", "ShowHoverTextForNonHost", true, "Show host-only hover text for non-host clients that also have this mod.このMODを導入した非ホスト向けHover表示");
			ConfigNonHostHoverText = ((BaseUnityPlugin)this).Config.Bind<string>("Message", "NonHostHoverText", "HOST ONLY REROLL", "Non-host hover text.非ホスト向けHover表示");
		}

		private static void CacheTypes()
		{
			upgradeStandType = AccessTools.TypeByName("UpgradeStand");
			physGrabberType = AccessTools.TypeByName("PhysGrabber");
			playerAvatarType = AccessTools.TypeByName("PlayerAvatar");
			if (upgradeStandType != null)
			{
				upgradeStandStateType = AccessTools.Inner(upgradeStandType, "State");
			}
		}

		private static Type GetUpgradeStandType()
		{
			if (upgradeStandType == null)
			{
				upgradeStandType = AccessTools.TypeByName("UpgradeStand");
			}
			return upgradeStandType;
		}

		private static Type GetUpgradeStandStateType()
		{
			if (upgradeStandStateType == null && GetUpgradeStandType() != null)
			{
				upgradeStandStateType = AccessTools.Inner(upgradeStandType, "State");
			}
			return upgradeStandStateType;
		}

		private static Type GetPhysGrabberType()
		{
			if (physGrabberType == null)
			{
				physGrabberType = AccessTools.TypeByName("PhysGrabber");
			}
			return physGrabberType;
		}

		private static bool IsEnabled()
		{
			if (ConfigEnable != null && ConfigEnable.Value && ConfigHostOnlyReroll != null)
			{
				return ConfigHostOnlyReroll.Value;
			}
			return false;
		}

		private static bool IsLocalAllowedToReroll()
		{
			try
			{
				if (!SemiFunc.IsMultiplayer())
				{
					return ConfigAllowSinglePlayer == null || ConfigAllowSinglePlayer.Value;
				}
				return SemiFunc.IsMasterClientOrSingleplayer();
			}
			catch
			{
				return false;
			}
		}

		private static object GetStandState(string stateName)
		{
			try
			{
				Type type = GetUpgradeStandStateType();
				if (type == null)
				{
					return null;
				}
				return Enum.Parse(type, stateName);
			}
			catch
			{
				return null;
			}
		}

		private static string GetStateName(object state)
		{
			try
			{
				if (state == null)
				{
					return string.Empty;
				}
				if (state.GetType().IsEnum)
				{
					return Enum.GetName(state.GetType(), state);
				}
				return Convert.ToInt32(state) switch
				{
					0 => "Idle", 
					1 => "Press", 
					2 => "PressFail", 
					3 => "PressSucceed", 
					4 => "Charging", 
					5 => "ChargingRollback", 
					6 => "RerollCloseHatch", 
					7 => "RerollRollStart", 
					8 => "RerollRolling", 
					9 => "RerollRollEnd", 
					10 => "RerollOpenHatch", 
					11 => "Broken", 
					_ => string.Empty, 
				};
			}
			catch
			{
				return string.Empty;
			}
		}

		private static bool StateArgumentIs(object state, string stateName)
		{
			return string.Equals(GetStateName(state), stateName, StringComparison.Ordinal);
		}

		private static bool StandStateIs(object stand, string stateName)
		{
			try
			{
				if (stand == null)
				{
					return false;
				}
				object standState = GetStandState(stateName);
				if (standState != null)
				{
					MethodInfo methodInfo = AccessTools.Method(GetUpgradeStandType(), "StateIs", (Type[])null, (Type[])null);
					if (methodInfo != null && methodInfo.Invoke(stand, new object[1] { standState }) is bool result)
					{
						return result;
					}
				}
				object fieldValue = GetFieldValue(stand, "currentState");
				return string.Equals(GetStateName(fieldValue), stateName, StringComparison.Ordinal);
			}
			catch
			{
				return false;
			}
		}

		private static object GetFieldValue(object instance, string fieldName)
		{
			try
			{
				if (instance == null)
				{
					return null;
				}
				FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), fieldName);
				if (fieldInfo == null)
				{
					return null;
				}
				return fieldInfo.GetValue(instance);
			}
			catch
			{
				return null;
			}
		}

		private static void SetFieldValue(object instance, string fieldName, object value)
		{
			try
			{
				if (instance != null)
				{
					FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), fieldName);
					if (!(fieldInfo == null))
					{
						fieldInfo.SetValue(instance, value);
					}
				}
			}
			catch (Exception ex)
			{
				LogFailure("SetFieldValue", ex);
			}
		}

		private static bool TryGetUpgradeStandFromStaticGrabObject(object staticGrabObject, out object stand)
		{
			stand = null;
			try
			{
				if (staticGrabObject == null)
				{
					return false;
				}
				Type type = GetUpgradeStandType();
				if (type == null)
				{
					return false;
				}
				Component val = (Component)((staticGrabObject is Component) ? staticGrabObject : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				stand = val.GetComponentInParent(type);
				if (stand == null)
				{
					return false;
				}
				object fieldValue = GetFieldValue(stand, "buttonGrabObject");
				if (fieldValue == null)
				{
					return false;
				}
				return fieldValue == staticGrabObject;
			}
			catch
			{
				stand = null;
				return false;
			}
		}

		private static int GetStandKey(object stand)
		{
			try
			{
				if (stand == null)
				{
					return 0;
				}
				Component val = (Component)((stand is Component) ? stand : null);
				if ((Object)(object)val != (Object)null)
				{
					PhotonView component = val.GetComponent<PhotonView>();
					if ((Object)(object)component != (Object)null && component.ViewID != 0)
					{
						return component.ViewID;
					}
					return ((Object)val).GetInstanceID();
				}
				return stand.GetHashCode();
			}
			catch
			{
				return 0;
			}
		}

		private static void MarkStandAuthorized(object stand, float seconds)
		{
			try
			{
				int standKey = GetStandKey(stand);
				if (standKey != 0)
				{
					AuthorizedStandUntil[standKey] = Time.time + Mathf.Max(1f, seconds);
				}
			}
			catch (Exception ex)
			{
				LogFailure("MarkStandAuthorized", ex);
			}
		}

		private static bool IsStandAuthorized(object stand)
		{
			try
			{
				int standKey = GetStandKey(stand);
				if (standKey == 0)
				{
					return false;
				}
				if (!AuthorizedStandUntil.TryGetValue(standKey, out var value))
				{
					return false;
				}
				if (Time.time <= value)
				{
					return true;
				}
				AuthorizedStandUntil.Remove(standKey);
				return false;
			}
			catch
			{
				return false;
			}
		}

		private static void ClearStandAuthorization(object stand)
		{
			try
			{
				int standKey = GetStandKey(stand);
				if (standKey != 0 && AuthorizedStandUntil.ContainsKey(standKey))
				{
					AuthorizedStandUntil.Remove(standKey);
				}
			}
			catch
			{
			}
		}

		private static bool IsGrabberHost(object grabber)
		{
			try
			{
				if (!SemiFunc.IsMultiplayer())
				{
					return true;
				}
				if (grabber == null)
				{
					return false;
				}
				object fieldValue = GetFieldValue(grabber, "photonView");
				PhotonView val = (PhotonView)((fieldValue is PhotonView) ? fieldValue : null);
				if ((Object)(object)val == (Object)null)
				{
					Component val2 = (Component)((grabber is Component) ? grabber : null);
					if ((Object)(object)val2 != (Object)null)
					{
						val = val2.GetComponent<PhotonView>();
					}
				}
				if ((Object)(object)val == (Object)null || val.Owner == null)
				{
					return false;
				}
				return val.Owner.IsMasterClient;
			}
			catch
			{
				return false;
			}
		}

		private static bool IsPlayerPhotonIdHost(int playerPhotonId)
		{
			try
			{
				PhotonView val = PhotonView.Find(playerPhotonId);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				Type type = GetPhysGrabberType();
				object obj = null;
				if (type != null)
				{
					obj = ((Component)val).GetComponent(type);
				}
				if (obj == null)
				{
					obj = val;
				}
				return IsGrabberHost(obj);
			}
			catch
			{
				return false;
			}
		}

		private static bool StandHasHostGrabber(object stand)
		{
			try
			{
				object fieldValue = GetFieldValue(stand, "buttonGrabObject");
				if (!(GetFieldValue(fieldValue, "playerGrabbing") is IList list))
				{
					return false;
				}
				for (int i = 0; i < list.Count; i++)
				{
					object grabber = list[i];
					if (IsGrabberHost(grabber))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool StandHasOnlyNonHostGrabber(object stand)
		{
			try
			{
				object fieldValue = GetFieldValue(stand, "buttonGrabObject");
				if (!(GetFieldValue(fieldValue, "playerGrabbing") is IList list))
				{
					return false;
				}
				bool result = false;
				for (int i = 0; i < list.Count; i++)
				{
					object obj = list[i];
					if (obj != null)
					{
						result = true;
						if (IsGrabberHost(obj))
						{
							return false;
						}
					}
				}
				return result;
			}
			catch
			{
				return false;
			}
		}

		private static bool HandleNonHostGrabAttempt(object stand, int playerPhotonId)
		{
			try
			{
				if (!IsEnabled())
				{
					return true;
				}
				if (ConfigBlockNonHostInteract != null && !ConfigBlockNonHostInteract.Value)
				{
					return true;
				}
				if (IsPlayerPhotonIdHost(playerPhotonId))
				{
					MarkStandAuthorized(stand, 20f);
					return true;
				}
				SendBlockedMessage(playerPhotonId);
				return false;
			}
			catch (Exception ex)
			{
				LogFailure("HandleNonHostGrabAttempt", ex);
				return false;
			}
		}

		private static void SendBlockedMessage(int playerPhotonId)
		{
			try
			{
				if (ConfigShowBlockedMessage != null && ConfigShowBlockedMessage.Value)
				{
					string message = ((ConfigBlockedMessage != null) ? ConfigBlockedMessage.Value : "Only the host can use the Upgrade Reroller.");
					string key = "blocked_" + playerPhotonId;
					SendPublicMessageWithCooldown(message, key, 1.5f);
				}
			}
			catch
			{
			}
		}

		private static void SendPublicMessageWithCooldown(string message, string key, float cooldownSeconds)
		{
			try
			{
				if (!string.IsNullOrEmpty(message) && (!MessageCooldownUntil.TryGetValue(key, out var value) || !(Time.time < value)))
				{
					MessageCooldownUntil[key] = Time.time + Mathf.Max(0.1f, cooldownSeconds);
					if ((!SemiFunc.IsMasterClientOrSingleplayer() || !TrySendChatMessage(message)) && Log != null)
					{
						Log.LogInfo((object)message);
					}
				}
			}
			catch (Exception ex)
			{
				LogFailure("SendPublicMessage", ex);
			}
		}

		private static bool TrySendChatMessage(string message)
		{
			try
			{
				if (playerAvatarType == null)
				{
					playerAvatarType = AccessTools.TypeByName("PlayerAvatar");
				}
				if (playerAvatarType == null)
				{
					return false;
				}
				FieldInfo fieldInfo = AccessTools.Field(playerAvatarType, "instance");
				if (fieldInfo == null)
				{
					return false;
				}
				object value = fieldInfo.GetValue(null);
				if (value == null)
				{
					return false;
				}
				MethodInfo methodInfo = AccessTools.Method(playerAvatarType, "ChatMessageSend", new Type[1] { typeof(string) }, (Type[])null);
				if (methodInfo == null)
				{
					return false;
				}
				methodInfo.Invoke(value, new object[1] { message });
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void DebugLog(string message)
		{
			try
			{
				if (ConfigShowDebugLog != null && ConfigShowDebugLog.Value && Log != null)
				{
					Log.LogInfo((object)message);
				}
			}
			catch
			{
			}
		}

		private static void LogFailure(string name, Exception ex)
		{
			try
			{
				if (Log != null)
				{
					Log.LogError((object)("Failure: " + name + "\n" + ex));
				}
			}
			catch
			{
			}
		}
	}
}