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 StickyGun v1.0.0
BepInEx/plugins/StickyGun/StickyGun.dll
Decompiled 9 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using StickyGun.Patches; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("Autodesk.Fbx")] [assembly: IgnoresAccessChecksTo("Discord.Sdk")] [assembly: IgnoresAccessChecksTo("Domain_Reload")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("FbxBuildTestAssets")] [assembly: IgnoresAccessChecksTo("Klattersynth")] [assembly: IgnoresAccessChecksTo("Photon3Unity3D")] [assembly: IgnoresAccessChecksTo("PhotonChat")] [assembly: IgnoresAccessChecksTo("PhotonRealtime")] [assembly: IgnoresAccessChecksTo("PhotonUnityNetworking")] [assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Utilities")] [assembly: IgnoresAccessChecksTo("PhotonVoice.API")] [assembly: IgnoresAccessChecksTo("PhotonVoice")] [assembly: IgnoresAccessChecksTo("PhotonVoice.PUN")] [assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime.Public")] [assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Attributes")] [assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Modules.Unity.Addressables")] [assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Modules.UnityLocalization")] [assembly: IgnoresAccessChecksTo("Sirenix.Serialization.Config")] [assembly: IgnoresAccessChecksTo("Sirenix.Serialization")] [assembly: IgnoresAccessChecksTo("Sirenix.Utilities")] [assembly: IgnoresAccessChecksTo("Unity.Addressables")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.Formats.Fbx.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.InternalAPIEngineBridge.013")] [assembly: IgnoresAccessChecksTo("Unity.Localization")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.MemoryProfiler")] [assembly: IgnoresAccessChecksTo("Unity.Postprocessing.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.ResourceManager")] [assembly: IgnoresAccessChecksTo("Unity.ScriptableBuildPipeline")] [assembly: IgnoresAccessChecksTo("Unity.Splines")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Antlr3.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Core")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Flow")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.State")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("websocket-sharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("StickyGun")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+22f0d653d9a051d42d5a5e40d246ad57a90d0539")] [assembly: AssemblyProduct("StickyGun")] [assembly: AssemblyTitle("StickyGun")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 StickyGun { internal static class GunCheck { internal static bool IsProtectedGun(PhysGrabObject obj) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 if ((Object)(object)obj == (Object)null) { return false; } if ((Object)(object)((Component)obj).GetComponentInChildren<ItemCartCannonMain>(true) != (Object)null) { return false; } if (StickyGunPlugin.IncludeModdedGuns.Value && (Object)(object)((Component)obj).GetComponentInChildren<ItemGun>(true) != (Object)null) { return true; } ItemAttributes component = ((Component)obj).GetComponent<ItemAttributes>(); if ((Object)(object)component != (Object)null && (int)component.itemType == 9) { return true; } return false; } internal static string Describe(PhysGrabObject obj) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)obj == (Object)null) { return "<null>"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(((Object)obj).name); ItemAttributes component = ((Component)obj).GetComponent<ItemAttributes>(); if ((Object)(object)component != (Object)null) { stringBuilder.Append(" | itemType=").Append(component.itemType); if (!string.IsNullOrEmpty(component.instanceName)) { stringBuilder.Append(" | instance=").Append(component.instanceName); } } else { stringBuilder.Append(" | no ItemAttributes"); } stringBuilder.Append(" | ItemGun=").Append((Object)(object)((Component)obj).GetComponentInChildren<ItemGun>(true) != (Object)null); return stringBuilder.ToString(); } internal static string ComponentList(PhysGrabObject obj) { if ((Object)(object)obj == (Object)null) { return "<null>"; } StringBuilder stringBuilder = new StringBuilder(); Component[] components = ((Component)obj).GetComponents<Component>(); for (int i = 0; i < components.Length; i++) { if (!((Object)(object)components[i] == (Object)null)) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(((object)components[i]).GetType().Name); } } return stringBuilder.ToString(); } } [BepInPlugin("com.sinan.stickygun", "StickyGun", "1.0.0")] public class StickyGunPlugin : BaseUnityPlugin { public const string PluginGuid = "com.sinan.stickygun"; public const string PluginName = "StickyGun"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> IncludeModdedGuns; internal static ConfigEntry<bool> DebugLogging; internal static ConfigEntry<bool> DiagnosticMode; public const string ConfigFileName = "StickyGun.cfg"; private PhysGrabObject watchedGun; internal static bool Diagnostics { get { if (DiagnosticMode == null || !DiagnosticMode.Value) { if (DebugLogging != null) { return DebugLogging.Value; } return false; } return true; } } private void Awake() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "StickyGun.cfg"), true, ((BaseUnityPlugin)this).Info.Metadata); Enabled = val.Bind<bool>("General", "Enabled", true, "Master on/off. When false StickyGun does nothing and the game behaves exactly as vanilla."); IncludeModdedGuns = val.Bind<bool>("General", "IncludeModdedGuns", true, "When true, anything carrying the game's ItemGun component counts as a gun - this is what makes guns from other mods work too. When false, only items the game itself labels as item type 'gun' are protected."); DebugLogging = val.Bind<bool>("General", "DebugLogging", false, "Logs every forced release involving a gun, and why StickyGun did or did not step in. Useful while testing, noisy during normal play."); DiagnosticMode = val.Bind<bool>("General", "DiagnosticMode", false, "Logs a line each time StickyGun stops a gun being knocked out of your hands. Handy if you want to confirm the mod is doing something; off by default so your log stays clean."); try { new Harmony("com.sinan.stickygun").PatchAll(); Log.LogInfo((object)"[StickyGun] loaded"); Log.LogInfo((object)("[StickyGun] settings: Enabled=" + Enabled.Value + " IncludeModdedGuns=" + IncludeModdedGuns.Value + " DiagnosticMode=" + DiagnosticMode.Value + " DebugLogging=" + DebugLogging.Value)); } catch (Exception ex) { Log.LogError((object)("[StickyGun] failed to apply patches, mod is inactive: " + ex)); } } private void Update() { if (Log == null || DebugLogging == null || !DebugLogging.Value) { watchedGun = null; return; } try { PhysGrabber instance = PhysGrabber.instance; if ((Object)(object)instance == (Object)null || !instance.isLocal) { return; } PhysGrabObject val = (instance.grabbed ? instance.grabbedPhysGrabObject : null); if ((Object)(object)val != (Object)null && GunCheck.IsProtectedGun(val)) { if ((Object)(object)watchedGun != (Object)(object)val) { watchedGun = val; Log.LogInfo((object)("[StickyGun][diag] now holding gun on beam: '" + ((Object)val).name + "' | moonLevel=" + SafeMoonLevel())); } } else if ((Object)(object)watchedGun != (Object)null) { string text = "<destroyed>"; bool flag = false; try { text = ((Object)watchedGun).name; ItemEquippable component = ((Component)watchedGun).GetComponent<ItemEquippable>(); flag = (Object)(object)component != (Object)null && component.IsEquipped(); } catch { } bool flag2 = PhysGrabberReleasePatch.lastGunReleaseFrame >= Time.frameCount - 1; Log.LogWarning((object)("[StickyGun][diag] gun '" + text + "' LEFT THE BEAM | viaReleaseObject=" + flag2 + " | wentIntoASlot=" + flag + " | moonLevel=" + SafeMoonLevel())); if (!flag2 && !flag) { Log.LogWarning((object)"[StickyGun][diag] ^^ NOT via ReleaseObject and NOT equipped. The game used a route we have not patched. Send me this log."); } watchedGun = null; } } catch (Exception ex) { Log.LogError((object)("[StickyGun][diag] watcher error: " + ex)); } } private static int SafeMoonLevel() { try { return SemiFunc.MoonLevel(); } catch { return -1; } } } } namespace StickyGun.Patches { internal static class Diag { internal static string CallChain(int maxFrames = 8) { try { StackTrace stackTrace = new StackTrace(fNeedFileInfo: false); StringBuilder stringBuilder = new StringBuilder(); int num = 0; for (int i = 2; i < stackTrace.FrameCount; i++) { if (num >= maxFrames) { break; } MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod(); if (methodBase == null) { continue; } string text = ((methodBase.DeclaringType != null) ? methodBase.DeclaringType.Name : "?"); if (!text.Contains("Harmony") && !text.StartsWith("StickyGun")) { if (num > 0) { stringBuilder.Append(" <- "); } stringBuilder.Append(text).Append('.').Append(methodBase.Name); num++; } } return (num > 0) ? stringBuilder.ToString() : "<no game frames found>"; } catch (Exception ex) { return "<could not read call chain: " + ex.Message + ">"; } } } [HarmonyPatch(typeof(Inventory), "ForceUnequip")] internal static class InventoryForceUnequipDiagnostic { private static void Prefix() { try { if (StickyGunPlugin.DebugLogging.Value) { StickyGunPlugin.Log.LogWarning((object)("[StickyGun][diag] Inventory.ForceUnequip fired - called by: " + Diag.CallChain())); } } catch (Exception ex) { StickyGunPlugin.Log.LogError((object)("[StickyGun][diag] " + ex)); } } } [HarmonyPatch(typeof(ItemEquippable), "ForceUnequip")] internal static class ItemForceUnequipDiagnostic { private static void Prefix(ItemEquippable __instance) { try { if (StickyGunPlugin.DebugLogging.Value) { StickyGunPlugin.Log.LogWarning((object)("[StickyGun][diag] ItemEquippable.ForceUnequip on '" + (((Object)(object)__instance != (Object)null) ? ((Object)__instance).name : "<null>") + "' - called by: " + Diag.CallChain())); } } catch (Exception ex) { StickyGunPlugin.Log.LogError((object)("[StickyGun][diag] " + ex)); } } } [HarmonyPatch(typeof(HurtCollider), "PhysObjectHurt")] internal static class HurtColliderPhysObjectHurtPatch { private static int lastLoggedId; private static float lastLoggedTime; private static void Prefix(PhysGrabObject physGrabObject, float hitForce) { try { if (!StickyGunPlugin.Enabled.Value || (Object)(object)physGrabObject == (Object)null || physGrabObject.playerGrabbing == null || physGrabObject.playerGrabbing.Count == 0 || !GunCheck.IsProtectedGun(physGrabObject)) { return; } physGrabObject.OverrideKnockOutOfGrabDisable(0.1f); if (StickyGunPlugin.Diagnostics) { int instanceID = ((Object)physGrabObject).GetInstanceID(); if (instanceID != lastLoggedId || Time.time - lastLoggedTime > 1f) { lastLoggedId = instanceID; lastLoggedTime = Time.time; StickyGunPlugin.Log.LogInfo((object)("[StickyGun] PROTECTED '" + ((Object)physGrabObject).name + "' from being knocked out of your hands (hitForce=" + hitForce + ")")); } } } catch (Exception ex) { StickyGunPlugin.Log.LogError((object)("[StickyGun] knock-out patch error: " + ex)); } } } [HarmonyPatch(typeof(PhysGrabber), "ReleaseObject", new Type[] { typeof(int), typeof(float) })] internal static class PhysGrabberReleasePatch { private static readonly HashSet<int> alreadyDescribed = new HashSet<int>(); internal static int lastGunReleaseFrame = -1; private static bool Prefix(PhysGrabber __instance, int _releaseObjectViewID, float _disableTimer) { try { PhysGrabObject val = (((Object)(object)__instance != (Object)null && __instance.grabbed) ? __instance.grabbedPhysGrabObject : null); bool flag = (Object)(object)__instance != (Object)null && __instance.isLocal && (Object)(object)val != (Object)null && GunCheck.IsProtectedGun(val); if (flag && StickyGunPlugin.DebugLogging.Value) { lastGunReleaseFrame = Time.frameCount; if (alreadyDescribed.Add(((Object)val).GetInstanceID())) { StickyGunPlugin.Log.LogInfo((object)("[StickyGun] gun detected: " + GunCheck.Describe(val))); StickyGunPlugin.Log.LogInfo((object)("[StickyGun] components: " + GunCheck.ComponentList(val))); } ItemEquippable component = ((Component)val).GetComponent<ItemEquippable>(); StickyGunPlugin.Log.LogWarning((object)("[StickyGun][diag] ReleaseObject on gun '" + ((Object)val).name + "' | releaseViewID=" + _releaseObjectViewID + " | disableTimer=" + _disableTimer + " | equipped=" + ((Object)(object)component != (Object)null && component.IsEquipped()) + " | equipping=" + ((Object)(object)component != (Object)null && component.isEquipping) + " | grabbers=" + ((val.playerGrabbing != null) ? val.playerGrabbing.Count : (-1)) + " | moonLevel=" + SafeMoonLevel() + " | called by: " + Diag.CallChain())); } if (!StickyGunPlugin.Enabled.Value) { return true; } if (_releaseObjectViewID == -1) { return true; } if (!flag) { return true; } ItemEquippable component2 = ((Component)val).GetComponent<ItemEquippable>(); if ((Object)(object)component2 != (Object)null && (component2.IsEquipped() || component2.isEquipping)) { return true; } if (val.playerGrabbing != null && val.playerGrabbing.Count > 1) { return true; } if (StickyGunPlugin.Diagnostics) { StickyGunPlugin.Log.LogInfo((object)("[StickyGun] BLOCKED forced drop of " + ((Object)val).name + " (releaseViewID=" + _releaseObjectViewID + ")")); } return false; } catch (Exception ex) { StickyGunPlugin.Log.LogError((object)("[StickyGun] patch error, letting the game continue: " + ex)); return true; } } private static int SafeMoonLevel() { try { return SemiFunc.MoonLevel(); } catch { return -1; } } } [HarmonyPatch(typeof(PhysGrabber), "ReleaseObjectRPC")] internal static class PhysGrabberReleaseRpcPatch { private static bool Prefix(PhysGrabber __instance, int _releaseObjectViewID) { try { if (!StickyGunPlugin.Enabled.Value) { return true; } if (_releaseObjectViewID != -1) { return true; } if ((Object)(object)__instance == (Object)null || !__instance.isLocal || !__instance.grabbed) { return true; } PhysGrabObject grabbedPhysGrabObject = __instance.grabbedPhysGrabObject; if ((Object)(object)grabbedPhysGrabObject == (Object)null || !GunCheck.IsProtectedGun(grabbedPhysGrabObject)) { return true; } ItemEquippable component = ((Component)grabbedPhysGrabObject).GetComponent<ItemEquippable>(); if ((Object)(object)component != (Object)null && (component.IsEquipped() || component.isEquipping)) { return true; } if (StickyGunPlugin.Diagnostics) { StickyGunPlugin.Log.LogInfo((object)("[StickyGun] PROTECTED '" + ((Object)grabbedPhysGrabObject).name + "' - declined a networked drop order.")); } return false; } catch (Exception ex) { StickyGunPlugin.Log.LogError((object)("[StickyGun] RPC patch error, letting the game continue: " + ex)); return true; } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }