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 SprintUpgradesLimiter v1.0.1
SpeedLimit.dll
Decompiled 9 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; 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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("Igor_Does_Nothing")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SpeedLimit")] [assembly: AssemblyTitle("SpeedLimit")] [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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace speedLimit { [BepInPlugin("Igor_does_nothing.NoMoreSpeed", "Shop Wiper (Update-based Sprint Remover)", "1.5.0")] public sealed class ShopWiperUpdatePlugin : BaseUnityPlugin { internal static ManualLogSource Log = null; private Harmony _harmony; private static GameObject _runnerGO; internal static ConfigEntry<float> ScanEverySeconds = null; internal static ConfigEntry<bool> DestroyInsteadOfDisable = null; internal static ConfigEntry<string> TargetNamesCsv = null; internal static ConfigEntry<bool> Verbose = null; internal static ConfigEntry<bool> Heartbeat = null; internal static ConfigEntry<float> HeartbeatEverySeconds = null; internal static ConfigEntry<string> RootNameHintsCsv = null; internal static ConfigEntry<bool> SpawnCarOnRemoval = null; internal static ConfigEntry<bool> SpawnOnlyIfMasterOrSingle = null; internal static ConfigEntry<string> ValuableName = null; internal static ConfigEntry<int> ValuableId = null; internal static ConfigEntry<string> CarPrefabPath = null; internal static ConfigEntry<float> CarSpawnYOffset = null; internal static ConfigEntry<float> CarSpawnForwardOffset = null; internal static Type SprintUpgradeType; internal static readonly string[] Defaults = new string[1] { "Item Upgrade Player Sprint Speed" }; private void Awake() { //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ScanEverySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Wiper", "ScanEverySeconds", 5f, "How often to scan & wipe (seconds)."); DestroyInsteadOfDisable = ((BaseUnityPlugin)this).Config.Bind<bool>("Wiper", "DestroyInsteadOfDisable", true, "Destroy() instead of SetActive(false)."); TargetNamesCsv = ((BaseUnityPlugin)this).Config.Bind<string>("Wiper", "TargetNamesCsv", string.Join(",", Defaults), "Target names (comma-separated). “(Clone)” is ignored."); Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Wiper", "VerboseLogs", true, "More logging."); Heartbeat = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableHeartbeatLogs", true, "Log a heartbeat to confirm Update() is running."); HeartbeatEverySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Debug", "HeartbeatEverySeconds", 2f, "Heartbeat period in seconds."); RootNameHintsCsv = ((BaseUnityPlugin)this).Config.Bind<string>("Perf", "RootNameHintsCsv", "Shop,Store,Level Generator,Map Controller,Items,Over Layer", "Scan only under scene roots whose names contain any of these tokens; if none found, fall back to scanning all active scene objects."); SpawnCarOnRemoval = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "SpawnCarOnRemoval", true, "After removing a sprint upgrade, try to spawn a car."); SpawnOnlyIfMasterOrSingle = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "SpawnOnlyIfMasterOrSingle", true, "Spawn only if host/singleplayer to avoid duplicates."); ValuableName = ((BaseUnityPlugin)this).Config.Bind<string>("Spawn", "ValuableName", "Valuable Car", "Valuable name (for ValuableDirector/SemiFunc)."); ValuableId = ((BaseUnityPlugin)this).Config.Bind<int>("Spawn", "ValuableId", 0, "Valuable id (if the API needs it)."); CarPrefabPath = ((BaseUnityPlugin)this).Config.Bind<string>("Spawn", "CarPrefabPath", "valuables/02 small/Valuable Car", "Photon/Resources path (no .prefab)."); CarSpawnYOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "CarSpawnYOffset", 0.15f, "Raise the spawn point by Y."); CarSpawnForwardOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "CarSpawnForwardOffset", 0.25f, "Spawn a bit forward from the removed object."); SprintUpgradeType = AccessTools.TypeByName("ItemUpgradePlayerSprintSpeed"); _harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); TryPatchPurchaseBlock(); TrySpawnRunner(); Log.LogInfo((object)$"[ShopWiper] Awake OK. Scan={ScanEverySeconds.Value}s, Destroy={DestroyInsteadOfDisable.Value}"); Log.LogInfo((object)("[ShopWiper] Targets: " + string.Join(", ", GetTargetsList()))); Log.LogInfo((object)("[ShopWiper] Roots: " + RootNameHintsCsv.Value)); Log.LogInfo((object)$"[ShopWiper] Spawn: Enabled={SpawnCarOnRemoval.Value}, OnlyMaster={SpawnOnlyIfMasterOrSingle.Value}, Name={ValuableName.Value}, Id={ValuableId.Value}, PrefabPath={CarPrefabPath.Value}"); } private void OnEnable() { Log.LogInfo((object)"[ShopWiper] OnEnable()"); } private void OnDisable() { Log.LogInfo((object)"[ShopWiper] OnDisable() (runner stays alive)"); } private void TryPatchPurchaseBlock() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown Type type = SprintUpgradeType ?? AccessTools.TypeByName("ItemUpgradePlayerSprintSpeed"); if (type == null) { Log.LogInfo((object)"[ShopWiper] No ItemUpgradePlayerSprintSpeed — patch not needed."); return; } MethodInfo methodInfo = AccessTools.Method(type, "Upgrade", (Type[])null, (Type[])null); if (methodInfo == null) { Log.LogInfo((object)"[ShopWiper] No Upgrade() on ItemUpgradePlayerSprintSpeed — ok."); return; } _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ShopWiperUpdatePlugin), "BlockPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[ShopWiper] Patched ItemUpgradePlayerSprintSpeed.Upgrade (failsafe)."); } private static bool BlockPrefix(object __instance) { try { object obj = ((__instance is Component) ? __instance : null); GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if ((Object)(object)val != (Object)null) { val.SetActive(false); if (Verbose.Value) { Log.LogInfo((object)("[ShopWiper] Failsafe: hid " + HierarchyPath(val))); } } } catch { } return false; } private static void TrySpawnRunner() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_runnerGO != (Object)null)) { _runnerGO = new GameObject("ShopWiperRunner"); ((Object)_runnerGO).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)_runnerGO); _runnerGO.transform.parent = null; _runnerGO.AddComponent<ShopWiperRunner>(); Log.LogInfo((object)"[Runner] Spawned persistent runner GameObject."); } } internal static IReadOnlyList<string> GetTargetsList() { List<string> list = new List<string>(); string text = TargetNamesCsv?.Value ?? string.Empty; if (!string.IsNullOrWhiteSpace(text)) { list.AddRange(from x in text.Split(new char[1] { ',' }) select x.Trim() into x where x.Length > 0 select x); } if (list.Count == 0) { list.AddRange(Defaults); } return list; } internal static IReadOnlyList<string> GetRootHints() { string text = RootNameHintsCsv?.Value ?? ""; return (from s in text.Split(new char[1] { ',' }) select s.Trim() into s where s.Length > 0 select s).ToArray(); } internal static string Normalize(string s) { if (string.IsNullOrEmpty(s)) { return string.Empty; } return StripCloneSuffix(s).Trim(); } internal static string StripCloneSuffix(string s) { if (string.IsNullOrEmpty(s)) { return string.Empty; } int num = s.LastIndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0 && num + "(Clone)".Length == s.Length) { return s.Substring(0, num).TrimEnd(Array.Empty<char>()); } return s; } internal static string HierarchyPath(GameObject go) { string text = ((Object)go).name; Transform val = go.transform; while (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.parent)) { val = val.parent; text = ((Object)val).name + "/" + text; } return text; } internal static bool IsMasterOrSingleplayer() { try { Type type = AccessTools.TypeByName("SemiFunc"); MethodInfo methodInfo = AccessTools.Method(type, "IsMasterClientOrSingleplayer", (Type[])null, (Type[])null); if (methodInfo != null) { return (bool)(methodInfo.Invoke(null, null) ?? ((object)false)); } } catch { } if (!PhotonNetwork.OfflineMode) { return PhotonNetwork.IsMasterClient; } return true; } } internal sealed class ShopWiperRunner : MonoBehaviour { private float _scanTimer; private float _beatTimer; private void Update() { _scanTimer += Time.deltaTime; if (_scanTimer >= Mathf.Max(0.05f, ShopWiperUpdatePlugin.ScanEverySeconds.Value)) { _scanTimer = 0f; int num = ScanAndWipe(); if (ShopWiperUpdatePlugin.Verbose.Value && num > 0) { ShopWiperUpdatePlugin.Log.LogInfo((object)$"[Runner] Scan removed {num} sprint-upgrade objects."); } } if (ShopWiperUpdatePlugin.Heartbeat.Value) { _beatTimer += Time.deltaTime; if (_beatTimer >= Mathf.Max(0.5f, ShopWiperUpdatePlugin.HeartbeatEverySeconds.Value)) { _beatTimer = 0f; ShopWiperUpdatePlugin.Log.LogInfo((object)"[Runner] Heartbeat (Update is running)."); } } } private int ScanAndWipe() { int num = 0; IReadOnlyList<string> targetsList = ShopWiperUpdatePlugin.GetTargetsList(); List<Transform> candidateRoots = GetCandidateRoots(); if (candidateRoots.Count > 0) { foreach (Transform item in candidateRoots) { num += ScanUnder(item, targetsList); } } else { GameObject[] array = Object.FindObjectsOfType<GameObject>(); GameObject[] array2 = array; foreach (GameObject val in array2) { if (Object.op_Implicit((Object)(object)val) && MatchesName(((Object)val).name, targetsList)) { num += RemoveOne(val); } } } return num; } private static List<Transform> GetCandidateRoots() { IReadOnlyList<string> rootHints = ShopWiperUpdatePlugin.GetRootHints(); List<Transform> list = new List<Transform>(); GameObject[] array = Object.FindObjectsOfType<GameObject>(); foreach (GameObject val in array) { if (!Object.op_Implicit((Object)(object)val)) { continue; } Transform transform = val.transform; if (!((Object)(object)transform.parent != (Object)null)) { string name = ((Object)val).name ?? ""; if (rootHints.Any((string tok) => name.IndexOf(tok, StringComparison.OrdinalIgnoreCase) >= 0)) { list.Add(transform); } } } return list; } private int ScanUnder(Transform root, IReadOnlyList<string> targets) { int num = 0; Stack<Transform> stack = new Stack<Transform>(); stack.Push(root); while (stack.Count > 0) { Transform val = stack.Pop(); if (Object.op_Implicit((Object)(object)val)) { GameObject gameObject = ((Component)val).gameObject; if (Object.op_Implicit((Object)(object)gameObject) && MatchesName(((Object)gameObject).name, targets)) { num += RemoveOne(gameObject); } for (int i = 0; i < val.childCount; i++) { stack.Push(val.GetChild(i)); } } } return num; } private static bool MatchesName(string goName, IReadOnlyList<string> targets) { if (string.IsNullOrEmpty(goName)) { return false; } string text = ShopWiperUpdatePlugin.Normalize(goName); foreach (string target in targets) { string text2 = ShopWiperUpdatePlugin.Normalize(target); if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase)) { return true; } if (text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { return true; } if (text.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private int RemoveOne(GameObject go) { if (ShopWiperUpdatePlugin.SpawnCarOnRemoval.Value) { TrySpawnCarNear(go); } TryDestroyEverywhere(go); return 1; } private void TryDestroyEverywhere(GameObject go) { try { PhotonView component = go.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && ShopWiperUpdatePlugin.IsMasterOrSingleplayer()) { if (ShopWiperUpdatePlugin.Verbose.Value) { ShopWiperUpdatePlugin.Log.LogInfo((object)$"[ShopWiper] Photon destroy: {ShopWiperUpdatePlugin.HierarchyPath(go)} (viewID {component.ViewID})"); } PhotonNetwork.Destroy(go); } else if (ShopWiperUpdatePlugin.DestroyInsteadOfDisable.Value) { if (ShopWiperUpdatePlugin.Verbose.Value) { ShopWiperUpdatePlugin.Log.LogInfo((object)("[ShopWiper] Destroy (local): " + ShopWiperUpdatePlugin.HierarchyPath(go))); } Object.Destroy((Object)(object)go); } else { if (ShopWiperUpdatePlugin.Verbose.Value) { ShopWiperUpdatePlugin.Log.LogInfo((object)("[ShopWiper] Disable (local): " + ShopWiperUpdatePlugin.HierarchyPath(go))); } go.SetActive(false); } } catch (Exception ex) { ShopWiperUpdatePlugin.Log.LogWarning((object)("[ShopWiper] Destroy failed: " + ex.Message)); try { go.SetActive(false); } catch { } } } private void TrySpawnCarNear(GameObject go) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: 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) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) try { if (ShopWiperUpdatePlugin.SpawnOnlyIfMasterOrSingle.Value && !ShopWiperUpdatePlugin.IsMasterOrSingleplayer()) { return; } Vector3 val = go.transform.position + Vector3.up * ShopWiperUpdatePlugin.CarSpawnYOffset.Value + go.transform.forward * ShopWiperUpdatePlugin.CarSpawnForwardOffset.Value; Quaternion rotation = go.transform.rotation; if (TrySpawnViaValuableDirector(val, rotation) || TrySpawnViaSemiFunc(val, rotation)) { return; } if (PhotonNetwork.InRoom) { string text = ShopWiperUpdatePlugin.CarPrefabPath.Value ?? ""; if (!string.IsNullOrWhiteSpace(text)) { GameObject val2 = PhotonNetwork.Instantiate(text, val, rotation, (byte)0, (object[])null); if ((Object)(object)val2 != (Object)null) { return; } } } GameObject val3 = Resources.Load<GameObject>(ShopWiperUpdatePlugin.CarPrefabPath.Value) ?? Resources.Load<GameObject>(Path.GetFileName(ShopWiperUpdatePlugin.CarPrefabPath.Value)); if ((Object)(object)val3 != (Object)null) { Object.Instantiate<GameObject>(val3, val, rotation); } } catch (Exception ex) { ShopWiperUpdatePlugin.Log.LogDebug((object)("[ShopWiper] Spawn failed: " + ex.Message)); } } private static bool TrySpawnViaValuableDirector(Vector3 pos, Quaternion rot) { //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) try { Type type = AccessTools.TypeByName("ValuableDirector"); if (type == null) { return false; } object obj = AccessTools.Field(type, "instance")?.GetValue(null) ?? AccessTools.Field(type, "Instance")?.GetValue(null); if (obj == null) { return false; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { string text = methodInfo.Name.ToLowerInvariant(); if (!text.Contains("spawn") || !text.Contains("valuable")) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length >= 3 && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(Quaternion)) { if (parameters[0].ParameterType == typeof(string)) { methodInfo.Invoke(obj, new object[3] { ShopWiperUpdatePlugin.ValuableName.Value, pos, rot }); return true; } if (parameters[0].ParameterType == typeof(int)) { methodInfo.Invoke(obj, new object[3] { ShopWiperUpdatePlugin.ValuableId.Value, pos, rot }); return true; } } if (parameters.Length >= 2 && parameters[1].ParameterType == typeof(Vector3)) { if (parameters[0].ParameterType == typeof(string)) { methodInfo.Invoke(obj, new object[2] { ShopWiperUpdatePlugin.ValuableName.Value, pos }); return true; } if (parameters[0].ParameterType == typeof(int)) { methodInfo.Invoke(obj, new object[2] { ShopWiperUpdatePlugin.ValuableId.Value, pos }); return true; } } } } catch { } return false; } private static bool TrySpawnViaSemiFunc(Vector3 pos, Quaternion rot) { //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) try { Type type = AccessTools.TypeByName("SemiFunc"); if (type == null) { return false; } MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { string text = methodInfo.Name.ToLowerInvariant(); if (!text.Contains("spawn") || !text.Contains("valuable")) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length >= 3 && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(Quaternion)) { if (parameters[0].ParameterType == typeof(string)) { methodInfo.Invoke(null, new object[3] { ShopWiperUpdatePlugin.ValuableName.Value, pos, rot }); return true; } if (parameters[0].ParameterType == typeof(int)) { methodInfo.Invoke(null, new object[3] { ShopWiperUpdatePlugin.ValuableId.Value, pos, rot }); return true; } } if (parameters.Length >= 2 && parameters[1].ParameterType == typeof(Vector3)) { if (parameters[0].ParameterType == typeof(string)) { methodInfo.Invoke(null, new object[2] { ShopWiperUpdatePlugin.ValuableName.Value, pos }); return true; } if (parameters[0].ParameterType == typeof(int)) { methodInfo.Invoke(null, new object[2] { ShopWiperUpdatePlugin.ValuableId.Value, pos }); return true; } } } } catch { } return false; } } }