Decompiled source of SavingGrace v1.0.0

SavingGrace.dll

Decompiled 2 days ago
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.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.");
				}
			}
		}
	}
}