using 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(true) != (Object)null) { return false; } if (StickyGunPlugin.IncludeModdedGuns.Value && (Object)(object)((Component)obj).GetComponentInChildren(true) != (Object)null) { return true; } ItemAttributes component = ((Component)obj).GetComponent(); 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 ""; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(((Object)obj).name); ItemAttributes component = ((Component)obj).GetComponent(); 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(true) != (Object)null); return stringBuilder.ToString(); } internal static string ComponentList(PhysGrabObject obj) { if ((Object)(object)obj == (Object)null) { return ""; } StringBuilder stringBuilder = new StringBuilder(); Component[] components = ((Component)obj).GetComponents(); 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 Enabled; internal static ConfigEntry IncludeModdedGuns; internal static ConfigEntry DebugLogging; internal static ConfigEntry 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("General", "Enabled", true, "Master on/off. When false StickyGun does nothing and the game behaves exactly as vanilla."); IncludeModdedGuns = val.Bind("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("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("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 = ""; bool flag = false; try { text = ((Object)watchedGun).name; ItemEquippable component = ((Component)watchedGun).GetComponent(); 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() : ""; } catch (Exception ex) { return ""; } } } [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 : "") + "' - 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 alreadyDescribed = new HashSet(); 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(); 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(); 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(); 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) { } } }