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