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 ShopGuardian v1.0.2
ShopGuardian.dll
Decompiled 8 months agousing System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ShopGuardian")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ShopGuardian")] [assembly: AssemblyTitle("ShopGuardian")] [assembly: AssemblyVersion("1.0.0.0")] [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; } } } namespace ShopGuardian { [BepInPlugin("pl.kuba.shopguardian", "Shop Guardian", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ShopGuardianPlugin : BaseUnityPlugin { public const string PluginGuid = "pl.kuba.shopguardian"; public const string PluginName = "Shop Guardian"; public const string PluginVersion = "1.0.1"; private Harmony _harmony; internal static ManualLogSource LogS; private ConfigEntry<bool> _blockArcadia; private ConfigEntry<bool> _blockAllThirdParty; private ConfigEntry<bool> _forceDisableRandomSelection; private ConfigEntry<bool> _writeArcadiaCfg; private static ShopGuardianPlugin _instance; internal static ShopGuardianPlugin Instance => _instance; private void Awake() { _instance = this; LogS = ((BaseUnityPlugin)this).Logger; _blockArcadia = ((BaseUnityPlugin)this).Config.Bind<bool>("Blockers", "BlockArcadiaBetterShop", true, "Block shop registrations coming specifically from ArcadiaBetterShop/KagomesMod."); _blockAllThirdParty = ((BaseUnityPlugin)this).Config.Bind<bool>("Blockers", "BlockAllThirdPartyShopOverrides", false, "Block ANY third-party shop registrations (allows only vanilla/REPOShopLib internal)."); _forceDisableRandomSelection = ((BaseUnityPlugin)this).Config.Bind<bool>("Toggles", "DisableRandomShopSelection", true, "Force-disable random/alt shop selection in REPOShopLib, when possible."); _writeArcadiaCfg = ((BaseUnityPlugin)this).Config.Bind<bool>("Toggles", "WriteArcadiaBetterShopConfigOverride", true, "On startup, write/update ArcadiaBetterShop config to disable its shop features."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Shop Guardian] Booting…"); TryWriteArcadiaConfig(); TryForceREPOShopLibFlags(); TryPatchREPOShopLib(); } private void TryWriteArcadiaConfig() { if (!_writeArcadiaCfg.Value) { return; } try { string text = Path.Combine(Paths.ConfigPath, "ArcadiaBetterShop.arcadiabettershop.cfg"); string[] array = new string[6] { "# Auto-generated by Shop Guardian to keep the vanilla shop intact.", "# You can set WriteArcadiaBetterShopConfigOverride=false in pl.kuba.shopguardian.cfg to stop this.", "", "[Shop]", "UseRandomShop = false", "EnableRandomShopSelection = false" }; if (!File.Exists(text)) { File.WriteAllLines(text, array); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Wrote " + text)); return; } string text2 = File.ReadAllText(text); if (!text2.Contains("UseRandomShop") || !text2.Contains("EnableRandomShopSelection")) { File.AppendAllText(text, Environment.NewLine + string.Join(Environment.NewLine, array.Skip(3))); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Updated " + text + " with missing keys.")); return; } text2 = Regex.Replace(text2, "UseRandomShop\\s*=\\s*\\w+", "UseRandomShop = false"); text2 = Regex.Replace(text2, "EnableRandomShopSelection\\s*=\\s*\\w+", "EnableRandomShopSelection = false"); File.WriteAllText(text, text2); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Ensured disabling flags in " + text + ".")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Arcadia config override failed: " + ex.Message)); } } private void TryForceREPOShopLibFlags() { if (!_forceDisableRandomSelection.Value) { return; } try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name.Contains("REPOShopLib")); if (assembly == null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib not loaded yet; skipping flag force."); return; } Type type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "ShopUtils"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib.ShopUtils not found; skipping flag force."); return; } string[] array = new string[2] { "EnableRandomShopSelection", "UseRandomShop" }; foreach (string text in array) { PropertyInfo property = type.GetProperty(text, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { try { property.SetValue(null, false); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Set REPOShopLib.ShopUtils." + text + " = false")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to set ShopUtils." + text + ": " + ex.Message)); } } } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Error while forcing REPOShopLib flags: " + ex2.Message)); } } private void TryPatchREPOShopLib() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Expected O, but got Unknown try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name.Contains("REPOShopLib")); if (assembly == null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib not loaded; will rely on config override."); return; } Type type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "ShopUtils"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib.ShopUtils not found; cannot patch."); return; } _harmony = new Harmony("pl.kuba.shopguardian"); string[] array = new string[2] { "RegisterNamedShop", "RegisterShopRoom" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo != null) { HarmonyMethod val = new HarmonyMethod(typeof(ShopGuardianPlugin).GetMethod("GenericShopGatePrefix", BindingFlags.Static | BindingFlags.NonPublic)); _harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched ShopUtils." + text)); } } MethodInfo methodInfo2 = AccessTools.Method(type, "EnableRandomShopSelection", (Type[])null, (Type[])null); if (methodInfo2 != null) { HarmonyMethod val2 = new HarmonyMethod(typeof(ShopGuardianPlugin).GetMethod("ForceDisableRandomSelectionPrefix", BindingFlags.Static | BindingFlags.NonPublic)); _harmony.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched ShopUtils.EnableRandomShopSelection"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Harmony patching failed: " + ex.Message)); } } private static bool GenericShopGatePrefix(MethodBase __originalMethod) { try { if ((Object)(object)Instance != (Object)null && Instance._blockAllThirdParty.Value) { LogS.LogDebug((object)("Blocked " + __originalMethod?.Name + " (all third-party).")); return false; } if ((Object)(object)Instance != (Object)null && Instance._blockArcadia.Value && IsCallFromAssemblyMatching("ArcadiaBetterShop", "KagomesMod")) { LogS.LogInfo((object)("Blocked " + __originalMethod?.Name + " from ArcadiaBetterShop/KagomesMod.")); return false; } } catch (Exception ex) { LogS.LogWarning((object)("GenericShopGatePrefix error: " + ex.Message)); } return true; } private static bool ForceDisableRandomSelectionPrefix() { try { if ((Object)(object)Instance != (Object)null && Instance._forceDisableRandomSelection.Value) { LogS.LogInfo((object)"Prevented enabling random shop selection."); return false; } } catch (Exception ex) { LogS.LogWarning((object)("ForceDisableRandomSelectionPrefix error: " + ex.Message)); } return true; } private static bool IsCallFromAssemblyMatching(params string[] substrings) { StackFrame[] frames = new StackTrace(2, fNeedFileInfo: false).GetFrames(); foreach (StackFrame obj in frames) { Type type = obj.GetMethod()?.DeclaringType; if (!(type == null)) { string asmName = type.Assembly.GetName().Name; if (!asmName.Contains("ShopGuardian") && !asmName.Contains("REPOShopLib") && substrings.Any((string s) => asmName.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } } } return false; } } }