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 SavingGrace v1.0.0
SavingGrace.dll
Decompiled 2 days 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.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SavingGrace")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SavingGrace")] [assembly: AssemblyTitle("SavingGrace")] [assembly: AssemblyVersion("1.0.0.0")] [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 SavingGrace { [BepInPlugin("yourname.savinggrace", "SavingGrace", "0.1.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "yourname.savinggrace"; public const string Name = "SavingGrace"; public const string Version = "0.1.0"; internal static ManualLogSource Log; internal static ConfigEntry<int> UsesPerRound; internal static ConfigEntry<float> StunSeconds; internal static ConfigEntry<int> PressesRequired; internal static ConfigEntry<float> DecayPerSecond; internal static ConfigEntry<float> SoundVolume; internal static ConfigEntry<bool> RequireModForAllPlayers; private void Awake() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PressesRequired = ((BaseUnityPlugin)this).Config.Bind<int>("General", "PressesRequired", 25, new ConfigDescription("Spacebar presses needed to break free from a Forest Keeper.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>())); UsesPerRound = ((BaseUnityPlugin)this).Config.Bind<int>("General", "UsesPerRound", 2, new ConfigDescription("How many times per round each player can escape. 0 = unlimited.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); StunSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "StunSeconds", 5f, new ConfigDescription("Seconds the Forest Keeper is stunned after you escape.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>())); DecayPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DecayPerSecond", 0f, new ConfigDescription("How fast the fill square drains per second (0-1 scale, 0 = no drain).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); RequireModForAllPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "RequireModForAllPlayers", true, "If true, a host refuses players who don't have this mod at the join screen, and you leave lobbies whose host doesn't have it."); SoundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SoundVolume", 0.7f, new ConfigDescription("Volume of the sound played on each spacebar press (0 = silent).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); Harmony val = new Harmony("yourname.savinggrace"); val.PatchAll(); SavingGraceJoinGate.Install(val); GameObject val2 = new GameObject("SavingGraceController") { hideFlags = (HideFlags)61 }; val2.AddComponent<SavingGraceController>(); SavingGraceAudio.Initialize(val2); Log.LogInfo((object)"SavingGrace 0.1.0 loaded."); } } [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] internal static class RegisterHandlersPatch { [HarmonyPostfix] private static void Postfix() { SavingGraceNet.RegisterHandlers(); } } [HarmonyPatch(typeof(RoundManager), "FinishGeneratingNewLevelClientRpc")] internal static class RoundResetPatch { [HarmonyPostfix] private static void Postfix() { SavingGraceNet.ResetRound(); } } internal static class SavingGraceAudio { private const string ResourceName = "SavingGrace.Sounds.press.wav"; private static AudioClip clip; private static AudioSource source; public static void Initialize(GameObject host) { source = host.AddComponent<AudioSource>(); source.playOnAwake = false; source.spatialBlend = 0f; clip = LoadEmbeddedWav("SavingGrace.Sounds.press.wav"); if ((Object)(object)clip == (Object)null) { Plugin.Log.LogWarning((object)"Saving Grace press sound could not be loaded; presses will be silent."); } } public static void PlayPress() { if (!((Object)(object)clip == (Object)null) && !((Object)(object)source == (Object)null)) { source.PlayOneShot(clip, Plugin.SoundVolume.Value); } } private static AudioClip LoadEmbeddedWav(string resourceName) { try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); using Stream stream = executingAssembly.GetManifestResourceStream(resourceName); if (stream == null) { Plugin.Log.LogWarning((object)("Embedded sound '" + resourceName + "' not found. Resources in this DLL: " + string.Join(", ", executingAssembly.GetManifestResourceNames()))); return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return ParseWav(memoryStream.ToArray(), "SavingGracePress"); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to load Saving Grace press sound: " + ex)); return null; } } private static AudioClip ParseWav(byte[] data, string clipName) { if (data.Length < 44 || Encoding.ASCII.GetString(data, 0, 4) != "RIFF" || Encoding.ASCII.GetString(data, 8, 4) != "WAVE") { throw new InvalidDataException("Not a WAV file."); } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = -1; int num6 = 0; int num7 = 12; while (num7 + 8 <= data.Length) { string text = Encoding.ASCII.GetString(data, num7, 4); int num8 = BitConverter.ToInt32(data, num7 + 4); int num9 = num7 + 8; if (text == "fmt ") { num = BitConverter.ToInt16(data, num9); num2 = BitConverter.ToInt16(data, num9 + 2); num3 = BitConverter.ToInt32(data, num9 + 4); num4 = BitConverter.ToInt16(data, num9 + 14); } else if (text == "data") { num5 = num9; num6 = Math.Min(num8, data.Length - num9); break; } num7 = num9 + num8 + (num8 & 1); } if (num != 1 || num4 != 16 || num2 < 1 || num5 < 0) { throw new InvalidDataException("Only 16-bit PCM WAV files are supported."); } int num10 = num6 / 2; float[] array = new float[num10]; for (int i = 0; i < num10; i++) { array[i] = (float)BitConverter.ToInt16(data, num5 + i * 2) / 32768f; } AudioClip val = AudioClip.Create(clipName, num10 / num2, num2, num3, false); val.SetData(array, 0); return val; } } internal class SavingGraceController : MonoBehaviour { private const float RequestTimeout = 1.5f; private readonly SavingGraceUI ui = new SavingGraceUI(); private ForestGiantAI grabber; private float progress; private bool requestSent; private float requestTimer; private void Update() { SavingGraceSync.Tick(); StartOfRound instance = StartOfRound.Instance; PlayerControllerB val = (((Object)(object)instance != (Object)null) ? instance.localPlayerController : null); if ((Object)(object)val == (Object)null || val.isPlayerDead) { ResetState(); return; } if ((Object)(object)grabber != (Object)null && (Object)(object)((EnemyAI)grabber).inSpecialAnimationWithPlayer != (Object)(object)val) { grabber = null; } if ((Object)(object)grabber == (Object)null) { grabber = SavingGraceNet.FindGrabbingGiant(val); } int playerId = (int)val.playerClientId; if ((Object)(object)grabber == (Object)null || !SavingGraceSync.IsActive || !SavingGraceNet.HasUseLeft(playerId)) { ResetState(); return; } ui.SetVisible(visible: true); Keyboard current = Keyboard.current; bool flag = current != null && ((ButtonControl)current.spaceKey).wasPressedThisFrame; if (flag) { SavingGraceAudio.PlayPress(); } ui.Tick(Time.deltaTime, flag); if (requestSent) { requestTimer -= Time.deltaTime; if (requestTimer <= 0f) { requestSent = false; progress = 0f; ui.SetFill(0f); } return; } progress -= SavingGraceSync.DecayPerSecond * Time.deltaTime; if (flag) { progress += 1f / (float)Mathf.Max(1, SavingGraceSync.PressesRequired); } progress = Mathf.Clamp01(progress); ui.SetFill(progress); if (progress >= 1f) { requestSent = true; requestTimer = 1.5f; SavingGraceNet.SendRequest(playerId); } } private void ResetState() { grabber = null; progress = 0f; requestSent = false; ui.SetVisible(visible: false); ui.SetFill(0f); } } internal class SavingGraceUI { private static readonly Color BarFillColor = Color.white; private static readonly Color BarBackColor = new Color(0.04f, 0.04f, 0.05f, 0.75f); private const float Scale = 0.7f; private const float OffsetX = 0f; private const float OffsetY = 105f; private static Sprite whiteSprite; private static Sprite keySprite; private static Sprite slotSprite; private GameObject root; private Image fill; private RectTransform keyTop; private float keyDepth; private float pressAmount; private float sincePress = 1f; private float autoTimer = 0.3f; public void SetVisible(bool visible) { if (visible && (Object)(object)root == (Object)null) { Build(); } if ((Object)(object)root != (Object)null && root.activeSelf != visible) { root.SetActive(visible); } if (!visible) { pressAmount = 0f; sincePress = 1f; autoTimer = 0.3f; } } public void SetFill(float amount) { if ((Object)(object)fill != (Object)null) { fill.fillAmount = amount; } } public void Tick(float dt, bool pressed) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)keyTop == (Object)null) { return; } sincePress += dt; if (pressed) { sincePress = 0f; pressAmount = 1f; autoTimer = 0.45f; } else if (sincePress > 0.6f) { autoTimer -= dt; if (autoTimer <= 0f) { pressAmount = 1f; autoTimer = 0.45f; } } pressAmount = Mathf.MoveTowards(pressAmount, 0f, dt * 7f); keyTop.anchoredPosition = new Vector2(0f, keyDepth * (1f - pressAmount)); } private void Build() { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) HUDManager instance = HUDManager.Instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.HUDContainer == (Object)null)) { float num = 0.7f; float num2 = 320f * num; float num3 = 64f * num; float num4 = Mathf.Max(2f, 3f * num); float num5 = Mathf.Max(2f, num3 * 0.08f); float num6 = 104f * num; float num7 = 38f * num; float num8 = 10f * num; keyDepth = 6f * num; Sprite sprite = GetWhiteSprite(); root = new GameObject("SavingGraceBar", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform component = root.GetComponent<RectTransform>(); ((Transform)component).SetParent(instance.HUDContainer.transform, false); Center(component); component.sizeDelta = new Vector2(num2, num3); Image component2 = root.GetComponent<Image>(); component2.sprite = sprite; ((Graphic)component2).color = Color.white; ((Graphic)component2).raycastTarget = false; Image val = CreateImage("Inner", component, sprite, BarBackColor); Stretch(((Graphic)val).rectTransform, num4); fill = CreateImage("Fill", component, sprite, BarFillColor); Stretch(((Graphic)fill).rectTransform, num4 + num5); fill.type = (Type)3; fill.fillMethod = (FillMethod)0; fill.fillOrigin = 0; fill.fillAmount = 0f; TMP_FontAsset val2 = FindGameFont(instance); Plugin.Log.LogInfo((object)("Saving Grace label font: " + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "none found, using TMP default"))); CreateLabel("MashLabel", component, "MASH!", val2, 34f * num, Color.white, new Vector2(0f, num3 * 0.5f + 28f * num)); GameObject val3 = new GameObject("KeyPrompt", new Type[1] { typeof(RectTransform) }); RectTransform component3 = val3.GetComponent<RectTransform>(); ((Transform)component3).SetParent((Transform)(object)component, false); Center(component3); component3.sizeDelta = new Vector2(num6, num7 + keyDepth); component3.anchoredPosition = new Vector2(0f, 0f - (num3 * 0.5f + num8 + (num7 + keyDepth) * 0.5f)); Image val4 = CreateImage("KeyBase", component3, GetKeySprite(), new Color(0.5f, 0.5f, 0.53f, 1f)); val4.type = (Type)1; Place(((Graphic)val4).rectTransform, new Vector2(0.5f, 0f), new Vector2(num6, num7), Vector2.zero); Image val5 = CreateImage("KeyTop", component3, GetKeySprite(), new Color(0.97f, 0.97f, 0.97f, 1f)); val5.type = (Type)1; Place(((Graphic)val5).rectTransform, new Vector2(0.5f, 0f), new Vector2(num6, num7), new Vector2(0f, keyDepth)); keyTop = ((Graphic)val5).rectTransform; Image val6 = CreateImage("KeySlot", keyTop, GetSlotSprite(), new Color(0.1f, 0.1f, 0.11f, 1f)); val6.type = (Type)1; Place(((Graphic)val6).rectTransform, new Vector2(0.5f, 1f), new Vector2(num6 * 0.56f, 9f * num), new Vector2(0f, (0f - num7) * 0.28f)); PlaceAboveHotbar(component, instance); } } private static void Center(RectTransform rt) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); rt.pivot = val; Vector2 anchorMin = (rt.anchorMax = val); rt.anchorMin = anchorMin; } private static void Stretch(RectTransform rt, float inset) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = new Vector2(inset, inset); rt.offsetMax = new Vector2(0f - inset, 0f - inset); } private static void Place(RectTransform rt, Vector2 anchor, Vector2 size, Vector2 position) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Vector2 val = (rt.pivot = anchor); Vector2 anchorMin = (rt.anchorMax = val); rt.anchorMin = anchorMin; rt.sizeDelta = size; rt.anchoredPosition = position; } private static Image CreateImage(string name, RectTransform parent, Sprite sprite, Color color) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Image) }); ((Transform)val.GetComponent<RectTransform>()).SetParent((Transform)(object)parent, false); Image component = val.GetComponent<Image>(); component.sprite = sprite; ((Graphic)component).color = color; ((Graphic)component).raycastTarget = false; return component; } private static Sprite GetWhiteSprite() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)whiteSprite == (Object)null) { whiteSprite = Sprite.Create(Texture2D.whiteTexture, new Rect(0f, 0f, 4f, 4f), new Vector2(0.5f, 0.5f)); } return whiteSprite; } private static Sprite GetKeySprite() { if ((Object)(object)keySprite == (Object)null) { keySprite = MakeRoundedSprite(10); } return keySprite; } private static Sprite GetSlotSprite() { if ((Object)(object)slotSprite == (Object)null) { slotSprite = MakeRoundedSprite(4); } return slotSprite; } private static Sprite MakeRoundedSprite(int radius) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) int num = radius * 2 + 2; Texture2D val = new Texture2D(num, num, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color32[] array = (Color32[])(object)new Color32[num * num]; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { float num2 = (float)j + 0.5f; float num3 = (float)i + 0.5f; float num4 = Mathf.Clamp(num2, (float)radius, (float)(num - radius)); float num5 = Mathf.Clamp(num3, (float)radius, (float)(num - radius)); float num6 = Mathf.Sqrt((num2 - num4) * (num2 - num4) + (num3 - num5) * (num3 - num5)); byte b = (byte)(Mathf.Clamp01((float)radius - num6 + 0.5f) * 255f); array[i * num + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b); } } val.SetPixels32(array); val.Apply(); Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)num, (float)num), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4((float)radius, (float)radius, (float)radius, (float)radius)); ((Object)val2).hideFlags = (HideFlags)61; return val2; } private static TMP_FontAsset FindGameFont(HUDManager hud) { TMP_FontAsset val = null; TMP_Text[] componentsInChildren = hud.HUDContainer.GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.font == (Object)null)) { if (((Object)val2.font).name.IndexOf("3270", StringComparison.OrdinalIgnoreCase) >= 0) { return val2.font; } if ((Object)(object)val == (Object)null) { val = val2.font; } } } return val; } private static void CreateLabel(string name, RectTransform parent, string text, TMP_FontAsset font, float size, Color color, Vector2 anchoredPosition) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); RectTransform component = val.GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)parent, false); Center(component); component.sizeDelta = new Vector2(300f, 50f); component.anchoredPosition = anchoredPosition; TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); if ((Object)(object)font != (Object)null) { ((TMP_Text)val2).font = font; } ((TMP_Text)val2).text = text; ((TMP_Text)val2).fontSize = size; ((Graphic)val2).color = color; ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).enableWordWrapping = false; ((Graphic)val2).raycastTarget = false; ((TMP_Text)val2).outlineWidth = 0.2f; ((TMP_Text)val2).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue); } private static void PlaceAboveHotbar(RectTransform rt, HUDManager hud) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Image[] itemSlotIconFrames = hud.itemSlotIconFrames; Vector3 val = Vector3.zero; int num = 0; if (itemSlotIconFrames != null) { Image[] array = itemSlotIconFrames; foreach (Image val2 in array) { if (!((Object)(object)val2 == (Object)null)) { val += ((Component)val2).transform.position; num++; } } } if (num > 0) { ((Transform)rt).position = val / (float)num; } rt.anchoredPosition += new Vector2(0f, 105f); } } internal static class SavingGraceJoinGate { internal const string RejectMessage = "An error Occurred!"; private const string MarkerPrefix = ",SavingGrace:"; private const int NoMarker = -1; public static void Install(Harmony harmony) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(GameNetworkManager), "SetConnectionDataBeforeConnecting", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(GameNetworkManager), "ConnectionApproval", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Plugin.Log.LogWarning((object)"Couldn't find the game's join methods, so the join check is off. Players without the mod are removed shortly after joining instead."); return; } try { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SavingGraceJoinGate), "AddMarker", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(SavingGraceJoinGate), "CheckMarker", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Join check installed: players without the mod are refused at the join screen."); } catch (Exception ex) { Plugin.Log.LogError((object)("Couldn't install the join check: " + ex)); } } private static void AddMarker() { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null)) { string text = Encoding.ASCII.GetString(singleton.NetworkConfig.ConnectionData ?? new byte[0]); if (!text.Contains(",SavingGrace:")) { singleton.NetworkConfig.ConnectionData = Encoding.ASCII.GetBytes(text + ",SavingGrace:" + 1); } } } private static bool CheckMarker(ref ConnectionApprovalRequest __0, ConnectionApprovalResponse __1) { int result = -1; if (__0.Payload != null) { string text = Encoding.ASCII.GetString(__0.Payload); int num = text.LastIndexOf(",SavingGrace:", StringComparison.Ordinal); if (num >= 0) { if (!int.TryParse(text.Substring(num + ",SavingGrace:".Length), out result)) { result = 0; } __0.Payload = Encoding.ASCII.GetBytes(text.Substring(0, num)); } } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || __1 == null) { return true; } if (!Plugin.RequireModForAllPlayers.Value) { return true; } if (__0.ClientNetworkId == 0) { return true; } switch (result) { case -1: Refuse(__1, "An error Occurred!", __0.ClientNetworkId, "doesn't have the mod"); return false; default: Refuse(__1, "Saving Grace version mismatch: update the mod to match the host.", __0.ClientNetworkId, "has a different mod version"); return false; case 1: return true; } } private static void Refuse(ConnectionApprovalResponse response, string reason, ulong clientId, string why) { response.Approved = false; response.CreatePlayerObject = false; response.Reason = reason; Plugin.Log.LogWarning((object)$"Refused join from client {clientId}: it {why}."); } } internal static class SavingGraceNet { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnReleaseMessage; public static HandleNamedMessageDelegate <1>__OnRequestMessage; } private const string RequestMsg = "SavingGrace_Request"; private const string ReleaseMsg = "SavingGrace_Release"; private static readonly Dictionary<int, int> Uses = new Dictionary<int, int>(); private static bool loggedAnimatorInfo; public static void ResetRound() { Uses.Clear(); } public static bool HasUseLeft(int playerId) { int usesPerRound = SavingGraceSync.UsesPerRound; if (usesPerRound <= 0) { return true; } Uses.TryGetValue(playerId, out var value); return value < usesPerRound; } public static void RegisterHandlers() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null) { return; } CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__OnReleaseMessage; if (obj == null) { HandleNamedMessageDelegate val = OnReleaseMessage; <>O.<0>__OnReleaseMessage = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("SavingGrace_Release", (HandleNamedMessageDelegate)obj); if (singleton.IsServer) { object obj2 = <>O.<1>__OnRequestMessage; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnRequestMessage; <>O.<1>__OnRequestMessage = val2; obj2 = (object)val2; } customMessagingManager.RegisterNamedMessageHandler("SavingGrace_Request", (HandleNamedMessageDelegate)obj2); } SavingGraceSync.OnNetworkReady(); } public unsafe static void SendRequest(int playerId) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return; } if (singleton.IsServer) { HandleRequest(singleton.LocalClientId, playerId); return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref playerId, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("SavingGrace_Request", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnRequestMessage(ulong senderId, FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) int playerId = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref playerId, default(ForPrimitives)); HandleRequest(senderId, playerId); } private unsafe static void HandleRequest(ulong senderId, int playerId) { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (playerId < 0 || playerId >= allPlayerScripts.Length) { return; } PlayerControllerB val = allPlayerScripts[playerId]; if ((Object)(object)val == (Object)null || val.isPlayerDead || val.actualClientId != senderId) { return; } ForestGiantAI val2 = FindGrabbingGiant(val); if ((Object)(object)val2 == (Object)null) { return; } int value = Plugin.UsesPerRound.Value; Uses.TryGetValue(playerId, out var value2); if (value <= 0 || value2 < value) { Uses[playerId] = value2 + 1; float value3 = Plugin.StunSeconds.Value; FastBufferWriter val3 = default(FastBufferWriter); ((FastBufferWriter)(ref val3))..ctor(16, (Allocator)2, -1); try { ((FastBufferWriter)(ref val3)).WriteValueSafe<int>(ref playerId, default(ForPrimitives)); ulong networkObjectId = ((NetworkBehaviour)val2).NetworkObjectId; ((FastBufferWriter)(ref val3)).WriteValueSafe<ulong>(ref networkObjectId, default(ForPrimitives)); ((FastBufferWriter)(ref val3)).WriteValueSafe<float>(ref value3, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("SavingGrace_Release", val3, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val3))/*cast due to .constrained prefix*/).Dispose(); } ApplyRelease(val, val2, value3); } } private static void OnReleaseMessage(ulong senderId, FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); ulong key = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref key, default(ForPrimitives)); float stunSeconds = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref stunSeconds, default(ForPrimitives)); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.IsServer) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (num < 0 || num >= allPlayerScripts.Length) { return; } Uses.TryGetValue(num, out var value); Uses[num] = value + 1; if (singleton.SpawnManager.SpawnedObjects.TryGetValue(key, out var value2)) { ForestGiantAI component = ((Component)value2).GetComponent<ForestGiantAI>(); if (!((Object)(object)component == (Object)null)) { ApplyRelease(allPlayerScripts[num], component, stunSeconds); } } } private static void SetField(object target, string name, object value) { FieldInfo fieldInfo = AccessTools.Field(target.GetType(), name); if (fieldInfo == null) { Plugin.Log.LogWarning((object)("Field '" + name + "' not found on " + target.GetType().Name + "; check its name in dnSpy.")); } else { fieldInfo.SetValue(target, value); } } internal static ForestGiantAI FindGrabbingGiant(PlayerControllerB player) { ForestGiantAI[] array = Object.FindObjectsOfType<ForestGiantAI>(); foreach (ForestGiantAI val in array) { if ((Object)(object)((EnemyAI)val).inSpecialAnimationWithPlayer == (Object)(object)player) { return val; } } return null; } private static void ApplyRelease(PlayerControllerB player, ForestGiantAI giant, float stunSeconds) { if ((Object)(object)player == (Object)null || (Object)(object)giant == (Object)null) { return; } FieldInfo field = typeof(ForestGiantAI).GetField("eatPlayerCoroutine", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object? value = field.GetValue(giant); Coroutine val = (Coroutine)((value is Coroutine) ? value : null); if (val != null) { ((MonoBehaviour)giant).StopCoroutine(val); field.SetValue(giant, null); } } else { Plugin.Log.LogWarning((object)"Couldn't find the keeper's eat coroutine field; check its name in dnSpy."); } SetField(giant, "inEatingPlayerAnimation", false); SetField(giant, "inSpecialAnimationWithPlayer", null); SetField(player, "inSpecialInteractAnimation", false); SetField(player, "inAnimationWithEnemy", null); if (stunSeconds > 0f) { ((EnemyAI)giant).SetEnemyStunned(true, stunSeconds, player); } InterruptEatAnimation(giant, stunSeconds > 0f); } private static void InterruptEatAnimation(ForestGiantAI giant, bool stunned) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Invalid comparison between Unknown and I4 //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Invalid comparison between Unknown and I4 //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Invalid comparison between Unknown and I4 Animator creatureAnimator = ((EnemyAI)giant).creatureAnimator; if ((Object)(object)creatureAnimator == (Object)null) { return; } if (!loggedAnimatorInfo) { loggedAnimatorInfo = true; List<string> list = new List<string>(); AnimatorControllerParameter[] parameters = creatureAnimator.parameters; foreach (AnimatorControllerParameter val in parameters) { list.Add($"{val.name}({val.type})"); } Plugin.Log.LogInfo((object)("Forest keeper animator parameters: " + string.Join(", ", list))); List<string> list2 = new List<string>(); RuntimeAnimatorController runtimeAnimatorController = creatureAnimator.runtimeAnimatorController; if ((Object)(object)runtimeAnimatorController != (Object)null) { AnimationClip[] animationClips = runtimeAnimatorController.animationClips; foreach (AnimationClip val2 in animationClips) { list2.Add(((Object)val2).name); } } Plugin.Log.LogInfo((object)("Forest keeper animation clips: " + string.Join(", ", list2))); } AnimatorControllerParameter[] parameters2 = creatureAnimator.parameters; foreach (AnimatorControllerParameter val3 in parameters2) { string text = val3.name.ToLowerInvariant(); if (text.Contains("eat") || text.Contains("grab")) { if ((int)val3.type == 4) { creatureAnimator.SetBool(val3.name, false); } else if ((int)val3.type == 9) { creatureAnimator.ResetTrigger(val3.name); } } } if (!stunned) { return; } AnimatorControllerParameter[] parameters3 = creatureAnimator.parameters; foreach (AnimatorControllerParameter val4 in parameters3) { if (val4.name == "stunned" && (int)val4.type == 4) { creatureAnimator.SetBool("stunned", true); break; } } int num = FindStunState(creatureAnimator); if (num != 0) { creatureAnimator.CrossFadeInFixedTime(num, 0.1f); } else { Plugin.Log.LogWarning((object)"Couldn't find a stun state on the keeper's animator; it will finish its eat animation first."); } } private static int FindStunState(Animator animator) { List<string> list = new List<string> { "Stunned", "Stun", "stunned", "stun" }; RuntimeAnimatorController runtimeAnimatorController = animator.runtimeAnimatorController; if ((Object)(object)runtimeAnimatorController != (Object)null) { AnimationClip[] animationClips = runtimeAnimatorController.animationClips; foreach (AnimationClip val in animationClips) { if (((Object)val).name.IndexOf("stun", StringComparison.OrdinalIgnoreCase) >= 0) { list.Insert(0, ((Object)val).name); } } } foreach (string item in list) { int num = Animator.StringToHash(item); if (animator.HasState(0, num)) { return num; } } return 0; } } internal static class SavingGraceSync { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnHello; public static Action<ulong> <1>__OnClientConnected; public static Action<ulong> <2>__OnClientDisconnected; public static HandleNamedMessageDelegate <3>__OnWelcome; } internal const int ProtocolVersion = 1; private const string HelloMsg = "SavingGrace_Hello"; private const string WelcomeMsg = "SavingGrace_Welcome"; private const float HostWaitsForHello = 30f; private const float ClientWaitsForWelcome = 15f; private static readonly Dictionary<ulong, float> Pending = new Dictionary<ulong, float>(); private static bool welcomed; private static float helloDeadline; private static int syncedPresses; private static int syncedUses; private static float syncedDecay; private static string pendingError; private static float errorExpiry; private static float nextErrorCheck; private static bool UseSynced { get { NetworkManager singleton = NetworkManager.Singleton; return welcomed && (Object)(object)singleton != (Object)null && !singleton.IsServer; } } public static int PressesRequired => UseSynced ? syncedPresses : Plugin.PressesRequired.Value; public static int UsesPerRound => UseSynced ? syncedUses : Plugin.UsesPerRound.Value; public static float DecayPerSecond => UseSynced ? syncedDecay : Plugin.DecayPerSecond.Value; public static bool IsActive { get { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return false; } return singleton.IsServer || welcomed; } } public unsafe static void OnNetworkReady() { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null) { return; } CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; welcomed = false; if (singleton.IsServer) { Pending.Clear(); object obj = <>O.<0>__OnHello; if (obj == null) { HandleNamedMessageDelegate val = OnHello; <>O.<0>__OnHello = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("SavingGrace_Hello", (HandleNamedMessageDelegate)obj); singleton.OnClientConnectedCallback -= OnClientConnected; singleton.OnClientConnectedCallback += OnClientConnected; singleton.OnClientDisconnectCallback -= OnClientDisconnected; singleton.OnClientDisconnectCallback += OnClientDisconnected; return; } object obj2 = <>O.<3>__OnWelcome; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnWelcome; <>O.<3>__OnWelcome = val2; obj2 = (object)val2; } customMessagingManager.RegisterNamedMessageHandler("SavingGrace_Welcome", (HandleNamedMessageDelegate)obj2); helloDeadline = Time.time + 15f; FastBufferWriter val3 = default(FastBufferWriter); ((FastBufferWriter)(ref val3))..ctor(4, (Allocator)2, -1); try { int num = 1; ((FastBufferWriter)(ref val3)).WriteValueSafe<int>(ref num, default(ForPrimitives)); customMessagingManager.SendNamedMessage("SavingGrace_Hello", 0uL, val3, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val3))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnClientConnected(ulong clientId) { if (clientId != 0) { Pending[clientId] = Time.time + 30f; } } private static void OnClientDisconnected(ulong clientId) { Pending.Remove(clientId); } private unsafe static void OnHello(ulong senderId, FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { return; } Pending.Remove(senderId); if (num != 1) { Kick(senderId, "Saving Grace version mismatch: update the mod to match the host."); return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); try { int num2 = 1; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num2, default(ForPrimitives)); num2 = Plugin.PressesRequired.Value; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num2, default(ForPrimitives)); num2 = Plugin.UsesPerRound.Value; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num2, default(ForPrimitives)); float value = Plugin.DecayPerSecond.Value; ((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("SavingGrace_Welcome", senderId, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void Kick(ulong clientId, string reason) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer) { Plugin.Log.LogWarning((object)$"Removing client {clientId}: {reason}"); singleton.DisconnectClient(clientId, reason); } } private static void OnWelcome(ulong senderId, FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives)); int num3 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num3, default(ForPrimitives)); float num4 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num4, default(ForPrimitives)); NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !singleton.IsServer) { helloDeadline = 0f; if (num != 1) { LeaveWithError("Saving Grace version mismatch: this lobby's host has a different version of the mod."); return; } syncedPresses = num2; syncedUses = num3; syncedDecay = num4; welcomed = true; Plugin.Log.LogInfo((object)$"Saving Grace: using host settings (presses {num2}, uses per round {num3})."); } } private static void LeaveWithError(string message) { Plugin.Log.LogWarning((object)message); pendingError = message; errorExpiry = Time.unscaledTime + 60f; MethodInfo methodInfo = AccessTools.Method(typeof(GameNetworkManager), "Disconnect", (Type[])null, (Type[])null); if (methodInfo != null && (Object)(object)GameNetworkManager.Instance != (Object)null) { methodInfo.Invoke(GameNetworkManager.Instance, null); } else if ((Object)(object)NetworkManager.Singleton != (Object)null) { NetworkManager.Singleton.Shutdown(false); } } private static void TryShowError() { if (Time.unscaledTime > errorExpiry) { pendingError = null; } else { if (Time.unscaledTime < nextErrorCheck) { return; } nextErrorCheck = Time.unscaledTime + 0.5f; MenuManager val = Object.FindObjectOfType<MenuManager>(); if (!((Object)(object)val == (Object)null)) { MethodInfo methodInfo = AccessTools.Method(typeof(MenuManager), "DisplayMenuNotification", new Type[2] { typeof(string), typeof(string) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(val, new object[2] { pendingError, "[ Back ]" }); } else { Plugin.Log.LogWarning((object)"Couldn't show the menu pop-up (method not found); the reason is in this log."); } pendingError = null; } } } public static void Tick() { if (pendingError != null) { TryShowError(); } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsListening) { return; } if (singleton.IsServer) { if (Pending.Count == 0) { return; } List<ulong> list = null; foreach (KeyValuePair<ulong, float> item in Pending) { if (Time.time > item.Value) { if (list == null) { list = new List<ulong>(); } list.Add(item.Key); } } if (list == null) { return; } { foreach (ulong item2 in list) { Pending.Remove(item2); if (Plugin.RequireModForAllPlayers.Value) { Kick(item2, "This lobby requires the Saving Grace mod."); } } return; } } if (!welcomed && helloDeadline > 0f && Time.time > helloDeadline) { helloDeadline = 0f; if (Plugin.RequireModForAllPlayers.Value) { LeaveWithError("This lobby's host doesn't have the Saving Grace mod. Everyone needs it to play together."); } } } } }