Decompiled source of Enders Ultimate Fish Warden Admin Mod v2.1.8

BepInEx/plugins/EndersGambit/EndersGambit.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Client;
using FishNet.Managing.Logging;
using FishNet.Managing.Server;
using FishNet.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ENDERsBANDITS")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Braden Harker. All rights reserved.")]
[assembly: AssemblyFileVersion("2.1.8.0")]
[assembly: AssemblyInformationalVersion("2.1.8+5f4634330f85f89563159100b740442d08619721")]
[assembly: AssemblyProduct("Ender's Ultimate Fish Warden Admin Mod")]
[assembly: AssemblyTitle("EndersGambit")]
[assembly: AssemblyVersion("2.1.8.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 Fishwarden
{
	internal static class Actions
	{
		private sealed class Frozen
		{
			public Vector3 At;

			public float Next;
		}

		private static readonly Dictionary<ulong, MuteLines.Reason> _muted = new Dictionary<ulong, MuteLines.Reason>();

		private static readonly Dictionary<ulong, float> _lastHeard = new Dictionary<ulong, float>();

		private static readonly Dictionary<ulong, Frozen> _frozen = new Dictionary<ulong, Frozen>();

		private static readonly HashSet<ulong> _shopBanned = new HashSet<ulong>();

		private const float LavaRange = 400f;

		public static int MutedCount => _muted.Count;

		public static int FrozenCount => _frozen.Count;

		public static bool Lockdown { get; private set; }

		public static bool AnyShopBans => _shopBanned.Count > 0;

		public static string Purge(Player keep)
		{
			if (!Plugin.IsHosting)
			{
				return "Only the host can do that.";
			}
			if ((Object)(object)keep == (Object)null)
			{
				return "Nobody by that name.";
			}
			NetworkConnection owner;
			try
			{
				owner = ((NetworkBehaviour)keep).Owner;
			}
			catch
			{
				return "Could not identify them.";
			}
			List<Player> list = new List<Player>();
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if (!((Object)(object)player == (Object)null) && !((NetworkBehaviour)player).IsDeinitializing && ((NetworkBehaviour)player).Owner == owner)
					{
						list.Add(player);
					}
				}
			}
			catch
			{
				return "Could not read the lobby.";
			}
			if (list.Count <= 1)
			{
				return keep.SteamName + " only has the one body.";
			}
			int num = 0;
			foreach (Player item in list.Skip(1))
			{
				try
				{
					((NetworkBehaviour)item).NetworkObject.Despawn((DespawnType?)null);
					num++;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("[Purge] could not despawn a copy: " + ex.Message));
				}
			}
			Plugin.Log.LogWarning((object)$"[Purge] removed {num} copies of {keep.SteamName}");
			return string.Format("Removed {0} spare {1} of {2}.", num, (num == 1) ? "copy" : "copies", keep.SteamName);
		}

		public static string PurgeAll()
		{
			if (!Plugin.IsHosting)
			{
				return "Only the host can do that.";
			}
			Dictionary<NetworkConnection, List<Player>> dictionary = new Dictionary<NetworkConnection, List<Player>>();
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if (!((Object)(object)player == (Object)null) && !((NetworkBehaviour)player).IsDeinitializing && !((Object)(object)player == (Object)(object)Player.LocalPlayer) && !(((NetworkBehaviour)player).Owner == (NetworkConnection)null))
					{
						if (!dictionary.TryGetValue(((NetworkBehaviour)player).Owner, out var value))
						{
							value = new List<Player>();
							dictionary[((NetworkBehaviour)player).Owner] = value;
						}
						value.Add(player);
					}
				}
			}
			catch
			{
				return "Could not read the lobby.";
			}
			int num = 0;
			foreach (KeyValuePair<NetworkConnection, List<Player>> item in dictionary.Where((KeyValuePair<NetworkConnection, List<Player>> k) => k.Value.Count > 1))
			{
				foreach (Player item2 in item.Value.Skip(1))
				{
					try
					{
						((NetworkBehaviour)item2).NetworkObject.Despawn((DespawnType?)null);
						num++;
					}
					catch
					{
					}
				}
			}
			if (num != 0)
			{
				return $"Removed {num} duplicate bodies.";
			}
			return "No duplicates in the lobby.";
		}

		public static bool IsMuted(ulong id)
		{
			return _muted.ContainsKey(id);
		}

		public static MuteLines.Reason ReasonFor(ulong id)
		{
			if (!_muted.TryGetValue(id, out var value))
			{
				return MuteLines.Reason.General;
			}
			return value;
		}

		public static string CycleReason(Player p)
		{
			if ((Object)(object)p == (Object)null)
			{
				return "Nobody by that name.";
			}
			MuteLines.Reason reason = ReasonFor(p.SteamID);
			MuteLines.Reason reason2 = ((reason != MuteLines.Reason.Shouting) ? (reason + 1) : MuteLines.Reason.General);
			if (_muted.ContainsKey(p.SteamID))
			{
				_muted[p.SteamID] = reason2;
			}
			return p.SteamName + " muted for: " + MuteLines.Name(reason2);
		}

		public static string ToggleMute(Player p)
		{
			return ToggleMute(p, MuteLines.Reason.General);
		}

		public static string ToggleMute(Player p, MuteLines.Reason reason)
		{
			if ((Object)(object)p == (Object)null)
			{
				return "Nobody by that name.";
			}
			if (_muted.Remove(p.SteamID))
			{
				return p.SteamName + " can talk again.";
			}
			_muted[p.SteamID] = reason;
			return p.SteamName + " has been muted for " + MuteLines.Name(reason) + ".";
		}

		public static bool VoiceFrame(NetworkConnection conn)
		{
			if (_muted.Count == 0 || !Plugin.IsHosting)
			{
				return true;
			}
			Player val = Sender.PlayerFor(conn);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			ulong steamID;
			try
			{
				steamID = val.SteamID;
			}
			catch
			{
				return true;
			}
			if (!_muted.ContainsKey(steamID))
			{
				return true;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (!_lastHeard.TryGetValue(steamID, out var value) || realtimeSinceStartup - value > Plugin.MuteNoticeInterval.Value)
			{
				_lastHeard[steamID] = realtimeSinceStartup;
				if (Plugin.MuteAnnounce.Value)
				{
					Chat.AsPlayer(val, MuteLines.For(ReasonFor(steamID), val.SteamName));
				}
			}
			return false;
		}

		public static bool ShouldMute(NetworkConnection conn)
		{
			if (_muted.Count == 0)
			{
				return false;
			}
			Player val = Sender.PlayerFor(conn);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				return _muted.ContainsKey(val.SteamID);
			}
			catch
			{
				return false;
			}
		}

		public static bool IsFrozen(ulong id)
		{
			return _frozen.ContainsKey(id);
		}

		public static string ToggleFreeze(Player p)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)p == (Object)null)
			{
				return "Nobody by that name.";
			}
			if (!Plugin.IsHosting)
			{
				return "Only the host can do that.";
			}
			if (_frozen.Remove(p.SteamID))
			{
				return p.SteamName + " can move again.";
			}
			try
			{
				_frozen[p.SteamID] = new Frozen
				{
					At = Body.Of(p)
				};
			}
			catch
			{
				return "Could not read where they are.";
			}
			return p.SteamName + " is frozen where they stand.";
		}

		public static void FreezeAt(ulong id, Vector3 where)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (id != 0L)
			{
				_frozen[id] = new Frozen
				{
					At = where
				};
			}
		}

		public static void TickFreeze()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (_frozen.Count == 0 || !Plugin.IsHosting)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if (!((Object)(object)player == (Object)null) && !((Object)(object)player == (Object)(object)Player.LocalPlayer) && _frozen.TryGetValue(player.SteamID, out var value) && !(realtimeSinceStartup < value.Next))
					{
						value.Next = realtimeSinceStartup + Plugin.FreezeInterval.Value;
						try
						{
							Server.Instance.TeleportPlayer(player, value.At, 0f);
						}
						catch
						{
						}
					}
				}
			}
			catch
			{
			}
		}

		public static string ToggleLockdown()
		{
			if (!Plugin.IsHosting)
			{
				return "Only the host can do that.";
			}
			Lockdown = !Lockdown;
			if (!Lockdown)
			{
				return "Lockdown off - the lobby is open again.";
			}
			return "Lockdown ON - anyone not on the crew list is removed as they arrive.";
		}

		public static void EnforceLockdown()
		{
			if (!Lockdown || !Plugin.IsHosting)
			{
				return;
			}
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if ((Object)(object)player == (Object)null || (Object)(object)player == (Object)(object)Player.LocalPlayer)
					{
						continue;
					}
					ulong steamID;
					try
					{
						steamID = player.SteamID;
					}
					catch
					{
						continue;
					}
					if (!Crew.Trusts(steamID))
					{
						try
						{
							((NetworkBehaviour)player).Owner.Kick((KickReason)0, (LoggingType)3, "This lobby is locked down. Ask the host for an invite.");
							Plugin.Log.LogWarning((object)("[Lockdown] refused " + player.SteamName));
						}
						catch
						{
						}
					}
				}
			}
			catch
			{
			}
		}

		public static bool IsShopBanned(ulong id)
		{
			return _shopBanned.Contains(id);
		}

		public static void ShopBan(ulong id)
		{
			if (id != 0L && _shopBanned.Add(id))
			{
				Plugin.Log.LogWarning((object)$"[ShopBan] {Logbook.Name(id)} ({id}) is barred from every shop.");
			}
		}

		public static void ShopBan_Lift(ulong id)
		{
			_shopBanned.Remove(id);
		}

		public static string ToggleShopBan(Player p)
		{
			if ((Object)(object)p == (Object)null)
			{
				return "Nobody by that name.";
			}
			if (!Plugin.IsHosting)
			{
				return "Only the host can do that.";
			}
			ulong steamID;
			try
			{
				steamID = p.SteamID;
			}
			catch
			{
				return "Could not read who they are.";
			}
			if (steamID == 0L)
			{
				return "They have not finished joining yet.";
			}
			if (_shopBanned.Remove(steamID))
			{
				Plugin.Log.LogWarning((object)$"[ShopBan] {p.SteamName} ({steamID}) may buy again.");
				return p.SteamName + " can use the shops again.";
			}
			_shopBanned.Add(steamID);
			Plugin.Log.LogWarning((object)$"[ShopBan] {p.SteamName} ({steamID}) is barred from every shop.");
			return p.SteamName + " cannot buy anything now.";
		}

		public static string Strip(Player p)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.IsHosting)
			{
				return "Only the host can do that.";
			}
			if ((Object)(object)p == (Object)null)
			{
				return "Nobody by that name.";
			}
			try
			{
				Server.Instance.DropAllItems(p, Body.Of(p), Quaternion.identity);
				return p.SteamName + " has been relieved of everything they were carrying.";
			}
			catch (Exception ex)
			{
				return "Could not: " + ex.Message;
			}
		}

		public static void Release(ulong id)
		{
			_muted.Remove(id);
			_frozen.Remove(id);
		}

		public static string Status()
		{
			List<string> list = new List<string>();
			if (Lockdown)
			{
				list.Add("LOCKDOWN");
			}
			if (_muted.Count > 0)
			{
				list.Add($"{_muted.Count} muted");
			}
			if (_frozen.Count > 0)
			{
				list.Add($"{_frozen.Count} frozen");
			}
			if (list.Count != 0)
			{
				return string.Join(", ", list);
			}
			return "";
		}

		public static string SendBossAway()
		{
			if (!Plugin.IsHosting)
			{
				return "Despawning is server side - host or play solo.";
			}
			try
			{
				if (!Object.op_Implicit((Object)(object)BossManager.Boss))
				{
					return "There is no boss up.";
				}
			}
			catch
			{
				return "The boss manager is not up yet.";
			}
			try
			{
				string text = "The boss";
				try
				{
					text = ((Object)BossManager.Boss).name.Replace("(Clone)", "").Trim();
				}
				catch
				{
				}
				MethodInfo methodInfo = AccessTools.Method(typeof(BossManager), "KillBoss", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					object obj3 = (methodInfo.IsStatic ? null : Object.FindAnyObjectByType<BossManager>());
					if (!methodInfo.IsStatic && obj3 == null)
					{
						return "No boss manager in the scene.";
					}
					ParameterInfo[] parameters = methodInfo.GetParameters();
					methodInfo.Invoke(obj3, (parameters.Length == 0) ? null : new object[parameters.Length]);
					return text + " sent away.";
				}
				Creature boss = BossManager.Boss;
				if ((Object)(object)boss != (Object)null)
				{
					GameObject gameObject = ((Component)boss).gameObject;
					try
					{
						((NetworkBehaviour)Server.Instance).Despawn(gameObject, (DespawnType?)null);
					}
					catch
					{
						Object.Destroy((Object)(object)gameObject);
					}
					Plugin.Log.LogInfo((object)("[Actions] no KillBoss on BossManager - despawned " + text + " directly."));
					return text + " removed. This build has no KillBoss, so the fight state may need a moment to catch up.";
				}
				return "No boss to send away.";
			}
			catch (Exception ex)
			{
				return "Could not send it away: " + ex.Message;
			}
		}
	}
	internal static class Aim
	{
		private sealed class Shooter
		{
			public int Shots;

			public int Hits;

			public float LastShot = -1f;

			public float LastFinding = -999f;

			public int Blocked;
		}

		private static readonly Dictionary<ulong, Shooter> _shooters = new Dictionary<ulong, Shooter>();

		private static Shooter For(NetworkConnection conn)
		{
			ulong key = Sender.KeyOf(conn);
			if (!_shooters.TryGetValue(key, out var value))
			{
				value = new Shooter();
				_shooters[key] = value;
			}
			return value;
		}

		public static void NoteShot(NetworkConnection conn)
		{
			Shooter shooter = For(conn);
			shooter.Shots++;
			shooter.LastShot = Time.realtimeSinceStartup;
		}

		public static float SinceLastShot(NetworkConnection conn)
		{
			Shooter shooter = For(conn);
			if (!(shooter.LastShot < 0f))
			{
				return Time.realtimeSinceStartup - shooter.LastShot;
			}
			return -1f;
		}

		public static Verdict NoteHit(NetworkConnection conn, Player shooter, Vector3 hitPoint)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.WatchAim.Value)
			{
				return Verdict.Clean;
			}
			Shooter shooter2 = For(conn);
			shooter2.Hits++;
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (Plugin.CheckLineOfSight.Value && Blocked(shooter, hitPoint))
			{
				shooter2.Blocked++;
				if (shooter2.Blocked >= Plugin.BlockedShotsForFinding.Value && Ready(shooter2, realtimeSinceStartup))
				{
					shooter2.LastFinding = realtimeSinceStartup;
					int blocked = shooter2.Blocked;
					shooter2.Blocked = 0;
					return Verdict.Strong("shot-through-wall", $"{blocked} hits landed with solid geometry in the way");
				}
				return Verdict.Clean;
			}
			if (shooter2.Shots >= Plugin.AccuracySampleSize.Value)
			{
				float num = (float)shooter2.Hits / (float)shooter2.Shots;
				int shots = shooter2.Shots;
				shooter2.Shots = 0;
				shooter2.Hits = 0;
				if (num >= Plugin.SuspiciousAccuracy.Value && Ready(shooter2, realtimeSinceStartup))
				{
					shooter2.LastFinding = realtimeSinceStartup;
					return Verdict.Contextual("perfect-accuracy", $"{num * 100f:0}% of {shots} shots connected");
				}
			}
			return Verdict.Clean;
		}

		private static bool Blocked(Player shooter, Vector3 hitPoint)
		{
			//IL_003c: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0041: 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_0043: 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_0049: 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_0072: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)shooter == (Object)null)
			{
				return false;
			}
			try
			{
				Vector3 val = (((Object)(object)shooter.CamObject != (Object)null) ? shooter.CamObject.position : (Body.Of(shooter) + Vector3.up * 1.5f));
				Vector3 val2 = hitPoint - val;
				float magnitude = ((Vector3)(ref val2)).magnitude;
				if (magnitude < Plugin.LineOfSightMinDistance.Value)
				{
					return false;
				}
				float value = Plugin.LineOfSightPadding.Value;
				Vector3 val3 = val + ((Vector3)(ref val2)).normalized * value;
				float num = magnitude - value * 2f;
				if (num <= 0f)
				{
					return false;
				}
				return Physics.Raycast(val3, ((Vector3)(ref val2)).normalized, num, Plugin.LineOfSightMask.Value, (QueryTriggerInteraction)1);
			}
			catch
			{
				return false;
			}
		}

		private static bool Ready(Shooter s, float now)
		{
			return now - s.LastFinding >= Plugin.MovementFindingCooldown.Value;
		}

		public static void Reset()
		{
			_shooters.Clear();
		}
	}
	internal static class AutoTroll
	{
		public enum Trigger
		{
			Off,
			AnyProven,
			AfterStrikes,
			ChosenRules
		}

		private static readonly HashSet<ulong> _armedBy = new HashSet<ulong>();

		public static int AutoArmedCount => _armedBy.Count;

		public static IEnumerable<string> Chosen => from x in (Plugin.AutoTrollRules.Value ?? "").Split(',')
			select x.Trim() into x
			where x.Length > 0
			select x;

		public static Trigger Mode
		{
			get
			{
				if (!Enum.TryParse<Trigger>(Plugin.AutoTrollMode.Value, ignoreCase: true, out var result))
				{
					return Trigger.Off;
				}
				return result;
			}
		}

		public static bool WasAutomatic(ulong id)
		{
			return _armedBy.Contains(id);
		}

		public static bool Arms(string rule)
		{
			if (!string.IsNullOrWhiteSpace(rule))
			{
				return Chosen.Contains<string>(rule, StringComparer.OrdinalIgnoreCase);
			}
			return false;
		}

		public static string ToggleRule(string rule)
		{
			if (string.IsNullOrWhiteSpace(rule))
			{
				return "No rule named.";
			}
			List<string> list = Chosen.ToList();
			bool num = list.RemoveAll((string r) => string.Equals(r, rule, StringComparison.OrdinalIgnoreCase)) > 0;
			if (!num)
			{
				list.Add(rule);
			}
			Plugin.AutoTrollRules.Value = string.Join(",", list.ToArray());
			if (!num)
			{
				return rule + " now arms troll mode on its own.";
			}
			return rule + " no longer arms troll mode.";
		}

		public static void SetMode(Trigger t)
		{
			Plugin.AutoTrollMode.Value = t.ToString();
			Plugin.Log.LogInfo((object)("[AutoTroll] " + Describe(t)));
		}

		public static string Describe(Trigger t)
		{
			return t switch
			{
				Trigger.Off => "off - troll mode is manual only", 
				Trigger.AnyProven => "arms the moment anything proven lands", 
				Trigger.AfterStrikes => $"arms after {Plugin.AutoTrollStrikes.Value} strikes", 
				Trigger.ChosenRules => "arms only for the rules you chose", 
				_ => t.ToString(), 
			};
		}

		public static string Summary()
		{
			string text = "Auto-troll: " + Describe(Mode);
			if (Mode == Trigger.ChosenRules)
			{
				List<string> list = Chosen.ToList();
				text += ((list.Count == 0) ? "  (no rules chosen yet)" : ("  [" + string.Join(", ", list) + "]"));
			}
			return text;
		}

		public static void Consider(NetworkConnection conn, ulong key, string who, Verdict v)
		{
			Trigger mode = Mode;
			if (mode == Trigger.Off || !Plugin.IsHosting || Troll.Is(key) || Sender.IsTrusted(conn) || v.Confidence == Confidence.Contextual || (mode != Trigger.ChosenRules && !Response.Trolls(v.Rule)))
			{
				return;
			}
			bool flag = false;
			switch (mode)
			{
			case Trigger.AnyProven:
				flag = v.Proof;
				break;
			case Trigger.AfterStrikes:
				flag = Logbook.StrikesFor(conn) >= Plugin.AutoTrollStrikes.Value;
				break;
			case Trigger.ChosenRules:
				flag = v.Rule != null && Chosen.Any((string r) => string.Equals(r, v.Rule, StringComparison.OrdinalIgnoreCase));
				break;
			}
			if (flag)
			{
				Player val = Sender.PlayerFor(conn);
				if (!((Object)(object)val == (Object)null))
				{
					_armedBy.Add(key);
					Plugin.Log.LogWarning((object)("[AutoTroll] armed on " + who + " after '" + v.Rule + "'"));
					Troll.Toggle(val);
				}
			}
		}

		public static void Forget(ulong key)
		{
			_armedBy.Remove(key);
		}

		public static void Reset()
		{
			_armedBy.Clear();
		}
	}
	internal static class Bans
	{
		internal sealed class Record
		{
			public ulong SteamId;

			public string Name;

			public string Reason;

			public DateTime When;

			public string Line => $"{SteamId}|{When:yyyy-MM-dd HH:mm:ss}|{Name}|{Reason}";

			public static Record Parse(string line)
			{
				string[] array = line.Split('|');
				if (array.Length < 4)
				{
					return null;
				}
				if (!ulong.TryParse(array[0], out var result))
				{
					return null;
				}
				DateTime.TryParse(array[1], CultureInfo.InvariantCulture, DateTimeStyles.None, out var result2);
				return new Record
				{
					SteamId = result,
					When = result2,
					Name = array[2],
					Reason = string.Join("|", array.Skip(3))
				};
			}
		}

		private static readonly Dictionary<ulong, Record> _banned = new Dictionary<ulong, Record>();

		private static string File_ => Path.Combine(Logbook.Folder, "trophy-wall.txt");

		public static int Count => _banned.Count;

		public static IEnumerable<Record> All => _banned.Values;

		public static void Load()
		{
			_banned.Clear();
			try
			{
				if (!File.Exists(File_))
				{
					return;
				}
				string[] array = File.ReadAllLines(File_);
				foreach (string text in array)
				{
					if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#"))
					{
						Record record = Record.Parse(text.Trim());
						if (record != null)
						{
							_banned[record.SteamId] = record;
						}
					}
				}
				Plugin.Log.LogInfo((object)$"[TrophyWall] {_banned.Count} on the wall.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[TrophyWall] could not read the list: " + ex.Message));
			}
		}

		private static void Save()
		{
			try
			{
				Directory.CreateDirectory(Logbook.Folder);
				List<string> list = new List<string> { "# Fishwarden trophy wall - one per line: steamid|when|name|reason", "# Delete a line to let that player back in." };
				list.AddRange(_banned.Values.Select((Record r) => r.Line));
				File.WriteAllLines(File_, list);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[TrophyWall] could not save: " + ex.Message));
			}
		}

		public static bool IsBanned(ulong steamId)
		{
			return _banned.ContainsKey(steamId);
		}

		public static bool IsBanned(NetworkConnection conn)
		{
			Player val = Sender.PlayerFor(conn);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				return IsBanned(val.SteamID);
			}
			catch
			{
				return false;
			}
		}

		public static void Add(ulong steamId, string name, string reason)
		{
			if (steamId != 0L)
			{
				_banned[steamId] = new Record
				{
					SteamId = steamId,
					Name = name,
					Reason = reason,
					When = DateTime.Now
				};
				Save();
				Plugin.Log.LogWarning((object)$"[TrophyWall] mounted {name} ({steamId}): {reason}");
			}
		}

		public static bool Remove(ulong steamId)
		{
			if (!_banned.Remove(steamId))
			{
				return false;
			}
			Save();
			return true;
		}

		public static Record Find(string nameFragment)
		{
			string q = (nameFragment ?? "").ToLowerInvariant();
			return _banned.Values.FirstOrDefault((Record r) => (r.Name ?? "").ToLowerInvariant().Contains(q));
		}

		public static void Sweep()
		{
			if (!Plugin.IsHosting || !Plugin.EnforceBans.Value)
			{
				return;
			}
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if ((Object)(object)player == (Object)null || (Object)(object)player == (Object)(object)Player.LocalPlayer)
					{
						continue;
					}
					ulong steamID;
					try
					{
						steamID = player.SteamID;
					}
					catch
					{
						continue;
					}
					if (steamID != 0L && IsBanned(steamID))
					{
						Record record = _banned[steamID];
						Guard.Announce("[Fishwarden] " + player.SteamName + " is on the trophy wall (" + record.Reason + "). Gone fishing.");
						try
						{
							((NetworkBehaviour)player).Owner.Kick((KickReason)0, (LoggingType)3, "Fishwarden: banned - " + record.Reason);
							Plugin.Log.LogWarning((object)("[TrophyWall] refused " + player.SteamName + " on rejoin."));
						}
						catch (Exception ex)
						{
							Plugin.Log.LogError((object)("[TrophyWall] kick failed: " + ex.Message));
						}
					}
				}
			}
			catch
			{
			}
		}
	}
	internal static class Beacon
	{
		private sealed class Fault
		{
			public string Id;

			public string Kind;

			public string Type;

			public string Message;

			public string Stack;

			public string Where;

			public int Count = 1;

			public string FirstSeen;

			public bool Sent;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static LogCallback <0>__OnUnityLog;

			public static Func<string, string> <1>__Scrub;
		}

		private const int Schema = 1;

		private const string BakedWebhook = "";

		private const int MaxUniquePerSession = 12;

		private const float BatchSeconds = 20f;

		private const int StackFrames = 12;

		private const int MaxMessageChars = 900;

		private static readonly Dictionary<string, Fault> _faults = new Dictionary<string, Fault>(StringComparer.Ordinal);

		private static readonly Queue<Fault> _outbox = new Queue<Fault>();

		private static bool _armed;

		private static bool _draining;

		private static float _sendAfter = -1f;

		private static int _sent;

		private static string _installId;

		private static string _webhook;

		private static bool _transportDead;

		private static readonly Regex GeneratedHash = new Regex("___\\d{4,}", RegexOptions.Compiled);

		private static readonly Regex Offsets = new Regex("\\[0x[0-9a-fA-F]+\\]|:\\d+\\s*$|0x[0-9a-fA-F]{6,}", RegexOptions.Compiled);

		private static readonly Regex WinPath = new Regex("[A-Za-z]:\\\\[^\\s\"'<>|]*", RegexOptions.Compiled);

		private static readonly Regex UnixPath = new Regex("/(?:home|Users)/[^/\\s]+", RegexOptions.Compiled);

		public static int Distinct => _faults.Count;

		public static int Sent => _sent;

		private static string Folder => Path.Combine(Logbook.Folder, "reports");

		public static void Arm()
		{
			//IL_0055: 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)
			//IL_0060: Expected O, but got Unknown
			if (_armed)
			{
				return;
			}
			if (Plugin.TelemetryEnabled == null || !Plugin.TelemetryEnabled.Value)
			{
				Plugin.Log.LogInfo((object)"[Beacon] crash reporting is off (Telemetry/Enabled). Nothing about this session leaves the machine.");
				return;
			}
			_armed = true;
			_webhook = Resolve();
			_installId = InstallId();
			object obj = <>O.<0>__OnUnityLog;
			if (obj == null)
			{
				LogCallback val = OnUnityLog;
				<>O.<0>__OnUnityLog = val;
				obj = (object)val;
			}
			Application.logMessageReceivedThreaded += (LogCallback)obj;
			Plugin.Log.LogInfo((object)"[Beacon] crash reporting is ON. When something throws, it sends: this mod's version and build, the game/Unity/BepInEx versions, your OS, which settings differ from the defaults, which patches attached, and the error with its stack. It does NOT send Steam IDs, player names, chat, or anything about who is in your lobby, and file paths are stripped of your user name. Turn it off with Telemetry/Enabled = false.");
			if (string.IsNullOrEmpty(_webhook))
			{
				Plugin.Log.LogWarning((object)("[Beacon] no webhook configured, so reports are written to " + Folder + " and go nowhere else. If you are the author and did not mean this, BakedWebhook is empty in this build."));
			}
		}

		public static void Disarm()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			if (!_armed)
			{
				return;
			}
			_armed = false;
			try
			{
				object obj = <>O.<0>__OnUnityLog;
				if (obj == null)
				{
					LogCallback val = OnUnityLog;
					<>O.<0>__OnUnityLog = val;
					obj = (object)val;
				}
				Application.logMessageReceivedThreaded -= (LogCallback)obj;
			}
			catch
			{
			}
		}

		private static string Resolve()
		{
			string text = ((Plugin.TelemetryWebhook != null) ? Plugin.TelemetryWebhook.Value : null);
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Trim();
			}
			return "";
		}

		private static string InstallId()
		{
			try
			{
				Directory.CreateDirectory(Folder);
				string path = Path.Combine(Folder, "install-id.txt");
				if (File.Exists(path))
				{
					string text = File.ReadAllText(path).Trim();
					if (text.Length >= 8)
					{
						return text;
					}
				}
				string text2 = Guid.NewGuid().ToString("N").Substring(0, 16);
				File.WriteAllText(path, text2);
				return text2;
			}
			catch
			{
				return "unknown";
			}
		}

		private static void OnUnityLog(string message, string stack, LogType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if ((int)type != 4 && (int)type != 0)
			{
				return;
			}
			try
			{
				if ((stack != null && stack.IndexOf("Fishwarden", StringComparison.Ordinal) >= 0) || (message != null && message.IndexOf("Fishwarden", StringComparison.Ordinal) >= 0))
				{
					int num = message?.IndexOf(':') ?? (-1);
					string type2 = ((num > 0) ? message.Substring(0, num) : "Error");
					string message2 = ((num > 0) ? message.Substring(num + 1).Trim() : message);
					Note("exception", type2, message2, stack, null);
				}
			}
			catch
			{
			}
		}

		public static void Note(string kind, string type, string message, string stack, string where)
		{
			if (!_armed)
			{
				return;
			}
			try
			{
				string text = Fingerprint(type, stack, where);
				lock (_faults)
				{
					if (_faults.TryGetValue(text, out var value))
					{
						value.Count++;
						return;
					}
					if (_faults.Count >= 12)
					{
						return;
					}
					value = new Fault
					{
						Id = text,
						Kind = kind,
						Type = Scrub(type),
						Message = Trim(Scrub(message), 400),
						Stack = Normalise(stack),
						Where = Scrub(where),
						FirstSeen = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss'Z'", CultureInfo.InvariantCulture)
					};
					_faults[text] = value;
					_outbox.Enqueue(value);
				}
				if (_sendAfter < 0f)
				{
					_sendAfter = Time.realtimeSinceStartup + 20f;
				}
			}
			catch
			{
			}
		}

		public static void NoteRpc(string rpc, Exception e)
		{
			if (e != null)
			{
				Note("rpc", e.GetType().Name, e.Message, e.StackTrace, rpc);
			}
		}

		public static void NotePatch(string what, string why)
		{
			Note("patch", "PatchNotAttached", why, null, what);
		}

		private static string Fingerprint(string type, string stack, string where)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(type ?? "?").Append('|').Append(where ?? "");
			foreach (string item in Frames(stack).Take(4))
			{
				stringBuilder.Append('|').Append(item);
			}
			using MD5 mD = MD5.Create();
			byte[] array = mD.ComputeHash(Encoding.UTF8.GetBytes(stringBuilder.ToString()));
			StringBuilder stringBuilder2 = new StringBuilder(16);
			for (int i = 0; i < 6; i++)
			{
				stringBuilder2.Append(array[i].ToString("x2"));
			}
			return stringBuilder2.ToString();
		}

		private static IEnumerable<string> Frames(string stack)
		{
			if (string.IsNullOrEmpty(stack))
			{
				yield break;
			}
			string[] array = stack.Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					text = GeneratedHash.Replace(text, "___#");
					text = Offsets.Replace(text, "");
					text = WinPath.Replace(text, "<path>");
					text = UnixPath.Replace(text, "<path>");
					yield return text.Trim();
				}
			}
		}

		private static string Normalise(string stack)
		{
			if (string.IsNullOrEmpty(stack))
			{
				return "";
			}
			return string.Join("\n", Frames(stack).Take(12).ToArray());
		}

		private static string Scrub(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return s;
			}
			try
			{
				s = WinPath.Replace(s, "<path>");
				s = UnixPath.Replace(s, "<path>");
				string userName = Environment.UserName;
				if (!string.IsNullOrEmpty(userName) && userName.Length > 2)
				{
					s = s.Replace(userName, "<user>");
				}
			}
			catch
			{
			}
			return s;
		}

		private static string Trim(string s, int max)
		{
			if (!string.IsNullOrEmpty(s) && s.Length > max)
			{
				return s.Substring(0, max) + "...";
			}
			return s;
		}

		public static void Tick()
		{
			if (!_armed || _draining || _sendAfter < 0f || Time.realtimeSinceStartup < _sendAfter)
			{
				return;
			}
			_sendAfter = -1f;
			Fault[] batch;
			lock (_faults)
			{
				if (_outbox.Count == 0)
				{
					return;
				}
				batch = _outbox.ToArray();
				_outbox.Clear();
			}
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				_draining = true;
				((MonoBehaviour)instance).StartCoroutine(Send(batch));
			}
		}

		private static IEnumerator Send(Fault[] batch)
		{
			string text;
			string s;
			string file;
			try
			{
				text = Payload(batch);
				s = Summary(batch);
				file = Save(text, batch);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Beacon] could not build the report: " + ex.Message));
				_draining = false;
				yield break;
			}
			if (string.IsNullOrEmpty(_webhook) || _transportDead)
			{
				_draining = false;
				yield break;
			}
			UnityWebRequest req;
			try
			{
				List<IMultipartFormSection> list = new List<IMultipartFormSection>
				{
					(IMultipartFormSection)new MultipartFormDataSection("payload_json", "{\"content\":" + Quote(s) + "}"),
					(IMultipartFormSection)new MultipartFormFileSection("files[0]", Encoding.UTF8.GetBytes(text), "fishwarden-2.1.8-" + batch[0].Id + ".json", "application/json")
				};
				req = UnityWebRequest.Post(_webhook, list);
				req.timeout = 15;
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("[Beacon] could not prepare the request: " + ex2.Message + " - the report is still at " + file));
				_draining = false;
				yield break;
			}
			yield return req.SendWebRequest();
			bool flag;
			try
			{
				flag = (int)req.result == 1;
			}
			catch
			{
				flag = false;
			}
			if (flag)
			{
				_sent += batch.Length;
				Plugin.Log.LogInfo((object)("[Beacon] sent " + batch.Length + " report(s). Copy kept at " + file));
			}
			else
			{
				_transportDead = true;
				string text2;
				try
				{
					text2 = req.responseCode + " " + req.error;
				}
				catch
				{
					text2 = "unknown";
				}
				Plugin.Log.LogWarning((object)("[Beacon] could not send (" + text2 + "). Not trying again this session. The report is at " + file + " - sending that file by hand loses nothing."));
			}
			try
			{
				req.Dispose();
			}
			catch
			{
			}
			_draining = false;
		}

		private static string Payload(Fault[] batch)
		{
			StringBuilder stringBuilder = new StringBuilder(4096);
			stringBuilder.Append('{');
			Field(stringBuilder, "schema", 1.ToString(CultureInfo.InvariantCulture), raw: true);
			stringBuilder.Append(',');
			Field(stringBuilder, "mod", "2.1.8");
			stringBuilder.Append(',');
			Field(stringBuilder, "build", Build());
			stringBuilder.Append(',');
			Field(stringBuilder, "install", _installId);
			stringBuilder.Append(',');
			Field(stringBuilder, "at", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss'Z'", CultureInfo.InvariantCulture));
			stringBuilder.Append(',');
			Field(stringBuilder, "uptime_s", ((int)Time.realtimeSinceStartup).ToString(CultureInfo.InvariantCulture), raw: true);
			stringBuilder.Append(',');
			stringBuilder.Append("\"env\":{");
			Field(stringBuilder, "game", Safely(() => Application.version));
			stringBuilder.Append(',');
			Field(stringBuilder, "unity", Safely(() => Application.unityVersion));
			stringBuilder.Append(',');
			Field(stringBuilder, "bepinex", Safely(() => typeof(Paths).Assembly.GetName().Version.ToString()));
			stringBuilder.Append(',');
			Field(stringBuilder, "os", Safely(() => SystemInfo.operatingSystem));
			stringBuilder.Append(',');
			Field(stringBuilder, "hosting", Safely(() => (!Plugin.IsHosting) ? "no" : "yes"));
			stringBuilder.Append("},");
			stringBuilder.Append("\"patches\":{");
			Field(stringBuilder, "passed", Safely(() => (!TackleCheck.Passed) ? "no" : "yes"));
			stringBuilder.Append(',');
			Field(stringBuilder, "resolved", Safely(() => TackleCheck.Resolved.ToString(CultureInfo.InvariantCulture)), raw: true);
			stringBuilder.Append(',');
			Array(stringBuilder, "failures", Safely(() => TackleCheck.Failures.Select(Scrub).ToArray()));
			stringBuilder.Append(',');
			Array(stringBuilder, "degraded", Safely(() => TackleCheck.Degraded.Select(Scrub).ToArray()));
			stringBuilder.Append("},");
			Array(stringBuilder, "config_drift", Safely(() => Plugin.ConfigDrift().ToArray()));
			stringBuilder.Append(',');
			stringBuilder.Append("\"faults\":[");
			for (int num = 0; num < batch.Length; num++)
			{
				Fault fault = batch[num];
				if (num > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append('{');
				Field(stringBuilder, "id", fault.Id);
				stringBuilder.Append(',');
				Field(stringBuilder, "kind", fault.Kind);
				stringBuilder.Append(',');
				Field(stringBuilder, "type", fault.Type);
				stringBuilder.Append(',');
				Field(stringBuilder, "where", fault.Where ?? "");
				stringBuilder.Append(',');
				Field(stringBuilder, "count", fault.Count.ToString(CultureInfo.InvariantCulture), raw: true);
				stringBuilder.Append(',');
				Field(stringBuilder, "first_seen", fault.FirstSeen);
				stringBuilder.Append(',');
				Field(stringBuilder, "message", fault.Message ?? "");
				stringBuilder.Append(',');
				Field(stringBuilder, "stack", fault.Stack ?? "");
				stringBuilder.Append('}');
				fault.Sent = true;
			}
			stringBuilder.Append(']');
			stringBuilder.Append('}');
			return stringBuilder.ToString();
		}

		private static string Summary(Fault[] batch)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("**").Append("2.1.8").Append("** (")
				.Append(Build())
				.Append(") · ")
				.Append(Safely(() => "game " + Application.version))
				.Append(" · install `")
				.Append(_installId)
				.Append('`');
			string[] array = Safely(() => Plugin.ConfigDrift().ToArray());
			if (array != null && array.Length != 0)
			{
				stringBuilder.Append(" · ").Append(array.Length).Append(" setting(s) off default");
			}
			if (!Safely(() => TackleCheck.Passed))
			{
				stringBuilder.Append(" · **guard disarmed**");
			}
			foreach (Fault fault in batch)
			{
				string text = "\n`" + fault.Id + "` " + fault.Kind + " " + fault.Type + (string.IsNullOrEmpty(fault.Where) ? "" : (" in " + fault.Where)) + ((fault.Count > 1) ? (" ×" + fault.Count) : "") + " — " + Trim(fault.Message, 160);
				if (stringBuilder.Length + text.Length > 900)
				{
					stringBuilder.Append("\n…");
					break;
				}
				stringBuilder.Append(text);
			}
			return stringBuilder.ToString();
		}

		private static string Save(string json, Fault[] batch)
		{
			try
			{
				Directory.CreateDirectory(Folder);
				string path = "report_" + DateTime.Now.ToString("yyyy-MM-dd_HHmmss", CultureInfo.InvariantCulture) + "_" + batch[0].Id + ".json";
				string text = Path.Combine(Folder, path);
				File.WriteAllText(text, json, Encoding.UTF8);
				return text;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Beacon] could not write the report: " + ex.Message));
				return "(not written)";
			}
		}

		private static string Build()
		{
			return "full";
		}

		private static void Field(StringBuilder sb, string key, string value, bool raw = false)
		{
			sb.Append(Quote(key)).Append(':');
			if (raw && !string.IsNullOrEmpty(value))
			{
				sb.Append(value);
			}
			else
			{
				sb.Append(Quote(value ?? ""));
			}
		}

		private static void Array(StringBuilder sb, string key, string[] values)
		{
			sb.Append(Quote(key)).Append(":[");
			if (values != null)
			{
				for (int i = 0; i < values.Length; i++)
				{
					if (i > 0)
					{
						sb.Append(',');
					}
					sb.Append(Quote(values[i] ?? ""));
				}
			}
			sb.Append(']');
		}

		private static string Quote(string s)
		{
			if (s == null)
			{
				return "\"\"";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length + 16);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}

		private static T Safely<T>(Func<T> f)
		{
			try
			{
				return f();
			}
			catch
			{
				return default(T);
			}
		}

		public static string Status()
		{
			if (Plugin.TelemetryEnabled == null || !Plugin.TelemetryEnabled.Value)
			{
				return "Crash reporting is off. Nothing leaves this machine.";
			}
			List<string> list = new List<string>();
			list.Add("Crash reporting is on. " + _faults.Count + " distinct problem(s) this session, " + _sent + " sent.");
			list.Add("Reports are kept at " + Folder);
			List<string> list2 = list;
			if (string.IsNullOrEmpty(_webhook))
			{
				list2.Add("No webhook configured - reports are written locally and go nowhere else.");
			}
			else if (_transportDead)
			{
				list2.Add("The webhook failed earlier this session and is not being retried.");
			}
			foreach (Fault item in _faults.Values.OrderByDescending((Fault x) => x.Count).Take(8))
			{
				list2.Add("   " + item.Id + "  " + item.Type + ((item.Count > 1) ? ("  x" + item.Count) : "") + (item.Sent ? "" : "  (not sent yet)"));
			}
			return string.Join("\n", list2);
		}

		public static string Flush()
		{
			if (!_armed)
			{
				return "Crash reporting is off.";
			}
			lock (_faults)
			{
				if (_outbox.Count == 0)
				{
					return "Nothing waiting to send.";
				}
			}
			_sendAfter = 0f;
			return "Sending now.";
		}
	}
	internal static class Body
	{
		public static string Survey()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			Plugin.Log.LogInfo((object)"[Body] who is where - lowercase transform | Transform | rigidbody");
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if ((Object)(object)player == (Object)null)
					{
						continue;
					}
					string text = "?";
					string text2 = "?";
					string text3 = "?";
					Vector3 val;
					try
					{
						val = ((Component)player).transform.position;
						text = ((Vector3)(ref val)).ToString("0.0");
					}
					catch
					{
					}
					try
					{
						object obj2;
						if (!((Object)(object)player.Transform != (Object)null))
						{
							obj2 = "null";
						}
						else
						{
							val = player.Transform.position;
							obj2 = ((Vector3)(ref val)).ToString("0.0");
						}
						text2 = (string)obj2;
					}
					catch
					{
					}
					try
					{
						object obj4;
						if (!((Object)(object)player.Rigidbody != (Object)null))
						{
							obj4 = "null";
						}
						else
						{
							val = player.Rigidbody.worldCenterOfMass;
							obj4 = ((Vector3)(ref val)).ToString("0.0");
						}
						text3 = (string)obj4;
					}
					catch
					{
					}
					string text4 = "?";
					try
					{
						text4 = player.SteamName;
					}
					catch
					{
					}
					Plugin.Log.LogInfo((object)("[Body]   " + (((Object)(object)player == (Object)(object)Player.LocalPlayer) ? "* " : "  ") + text4 + "  " + text + " | " + text2 + " | " + text3));
					num++;
				}
			}
			catch (Exception ex)
			{
				return "Could not read the lobby: " + ex.Message;
			}
			Plugin.Log.LogInfo((object)"[Body] (* is you. If the first column is 0,0,0 and the others are not, everything reading the first column is wrong.)");
			return num + " player(s) written to the log - compare the three columns.";
		}

		public static bool Sane(Vector3 v)
		{
			//IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (!float.IsNaN(v.x) && !float.IsNaN(v.y) && !float.IsNaN(v.z) && !float.IsInfinity(v.x) && !float.IsInfinity(v.y) && !float.IsInfinity(v.z) && Mathf.Abs(v.x) < 1000000f && Mathf.Abs(v.y) < 1000000f)
			{
				return Mathf.Abs(v.z) < 1000000f;
			}
			return false;
		}

		public static Vector3 Of(Player p)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)p == (Object)null)
			{
				return Vector3.zero;
			}
			try
			{
				if ((Object)(object)p.Rigidbody != (Object)null)
				{
					return p.Rigidbody.worldCenterOfMass;
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)p.Transform != (Object)null)
				{
					return p.Transform.position;
				}
			}
			catch
			{
			}
			try
			{
				return ((Component)p).transform.position;
			}
			catch
			{
				return Vector3.zero;
			}
		}

		public static Vector3 Facing(Player p)
		{
			//IL_0000: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			Vector3 forward = Vector3.forward;
			try
			{
				if ((Object)(object)p != (Object)null && (Object)(object)p.Transform != (Object)null)
				{
					forward = p.Transform.forward;
				}
			}
			catch
			{
			}
			forward.y = 0f;
			if (!(((Vector3)(ref forward)).sqrMagnitude < 0.01f))
			{
				return ((Vector3)(ref forward)).normalized;
			}
			return Vector3.forward;
		}

		public static Vector3 InFrontOf(Player p, float metres = 2f)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			return Of(p) + Facing(p) * metres + Vector3.up * 0.5f;
		}
	}
	internal static class Cases
	{
		internal sealed class Case
		{
			public int Id;

			public ulong Key;

			public string Name;

			public int Strikes;

			public DateTime Opened;

			public float OpenedAt;

			public List<string> Rules = new List<string>();

			public bool Proven;

			public string Shot;
		}

		private static readonly List<Case> _open = new List<Case>();

		private static readonly Dictionary<string, int> _dismissedRules = new Dictionary<string, int>();

		private static int _nextId = 1;

		public static int PendingCount => _open.Count;

		public static IReadOnlyList<Case> Open => _open;

		public static void OpenCase(NetworkConnection conn, int strikes)
		{
			ulong key = Sender.KeyOf(conn);
			if (_open.Any((Case c) => c.Key == key))
			{
				Refresh(key, strikes);
				return;
			}
			List<Entry> source = Logbook.Entries.Where((Entry e) => e.Key == key).ToList();
			Case obj = new Case
			{
				Id = _nextId++,
				Key = key,
				Name = Sender.NameOf(conn),
				Strikes = strikes,
				Opened = DateTime.Now,
				OpenedAt = Time.realtimeSinceStartup,
				Proven = source.Any((Entry e) => e.Verdict.Proof),
				Shot = source.LastOrDefault((Entry e) => e.Shot != null)?.Shot,
				Rules = (from e in source
					select e.Verdict.Rule into r
					where r != null
					select r).Distinct().ToList()
			};
			_open.Add(obj);
			Plugin.Log.LogWarning((object)($"[Case #{obj.Id}] {obj.Name} - {strikes} strikes, " + (obj.Proven ? "PROVEN" : "unproven") + ", rules: " + string.Join(", ", obj.Rules)));
			Tell($"Case #{obj.Id}: {obj.Name} - {strikes} strikes" + (obj.Proven ? " (proven)" : " (heuristics only)"));
			Tell("  " + string.Join(", ", obj.Rules));
			Tell($"  /accept {obj.Id} to remove   /dismiss {obj.Id} to clear");
		}

		private static void Refresh(ulong key, int strikes)
		{
			Case obj = _open.FirstOrDefault((Case x) => x.Key == key);
			if (obj != null)
			{
				obj.Strikes = strikes;
				List<Entry> source = Logbook.Entries.Where((Entry e) => e.Key == key).ToList();
				obj.Proven = source.Any((Entry e) => e.Verdict.Proof);
				obj.Rules = (from e in source
					select e.Verdict.Rule into r
					where r != null
					select r).Distinct().ToList();
			}
		}

		public static void Tick()
		{
			if (_open.Count == 0)
			{
				return;
			}
			float num = Plugin.CaseExpiryMinutes.Value * 60f;
			if (num <= 0f)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			for (int num2 = _open.Count - 1; num2 >= 0; num2--)
			{
				if (!(realtimeSinceStartup - _open[num2].OpenedAt < num))
				{
					Plugin.Log.LogInfo((object)$"[Case #{_open[num2].Id}] expired unreviewed ({_open[num2].Name}).");
					_open.RemoveAt(num2);
				}
			}
		}

		public static string Accept(int id)
		{
			Case obj = _open.FirstOrDefault((Case x) => x.Id == id);
			if (obj == null)
			{
				return $"No open case #{id}.";
			}
			_open.Remove(obj);
			NetworkConnection val = FindConn(obj.Key);
			if (val == (NetworkConnection)null)
			{
				if (Plugin.EnforceBans.Value)
				{
					Bans.Add(obj.Key, obj.Name, string.Format("case #{0}: {1}", obj.Id, string.Join(", ", obj.Rules)));
					return obj.Name + " has already left - added to the trophy wall so they cannot come back.";
				}
				return obj.Name + " has already left.";
			}
			Guard.RemoveNow(val, obj.Strikes, string.Format("case #{0}: {1}", obj.Id, string.Join(", ", obj.Rules)));
			return $"Case #{obj.Id} accepted - {obj.Name} removed.";
		}

		public static string Dismiss(int id)
		{
			Case obj = _open.FirstOrDefault((Case x) => x.Id == id);
			if (obj == null)
			{
				return $"No open case #{id}.";
			}
			_open.Remove(obj);
			Logbook.Pardon(obj.Key);
			Rollback.Clear(obj.Key);
			Safety.Forget(obj.Key);
			foreach (string rule in obj.Rules)
			{
				_dismissedRules.TryGetValue(rule, out var value);
				_dismissedRules[rule] = value + 1;
				if (value + 1 >= Plugin.NoisyRuleThreshold.Value)
				{
					Plugin.Log.LogWarning((object)($"[Bycatch] rule '{rule}' has now been dismissed {value + 1} times. " + "It is probably firing on normal play - worth turning off or loosening."));
				}
			}
			return $"Case #{obj.Id} dismissed - {obj.Name} cleared, strikes wiped.";
		}

		public static string AcceptAll()
		{
			if (_open.Count == 0)
			{
				return "No open cases.";
			}
			List<int> source = _open.Select((Case c) => c.Id).ToList();
			return string.Join("  ", source.Select(Accept));
		}

		public static string DismissAll()
		{
			if (_open.Count == 0)
			{
				return "No open cases.";
			}
			List<int> source = _open.Select((Case c) => c.Id).ToList();
			return string.Join("  ", source.Select(Dismiss));
		}

		public static string List()
		{
			if (_open.Count == 0)
			{
				return "No cases waiting.";
			}
			List<string> list = new List<string> { $"{_open.Count} case(s) waiting:" };
			foreach (Case item in _open)
			{
				list.Add($"  #{item.Id}  {item.Name}  {item.Strikes} strikes  " + (item.Proven ? "PROVEN" : "heuristics only") + "  [" + string.Join(", ", item.Rules) + "]" + ((item.Shot != null) ? ("  shot: " + item.Shot) : ""));
			}
			list.Add("  /accept <n> | /dismiss <n> | /accept all | /dismiss all");
			return string.Join("\n", list);
		}

		public static IEnumerable<string> NoisyRules()
		{
			return from kv in _dismissedRules
				where kv.Value >= Plugin.NoisyRuleThreshold.Value && Rules.Enabled(kv.Key)
				select kv.Key;
		}

		public static string Noisy()
		{
			List<KeyValuePair<string, int>> list = _dismissedRules.Where((KeyValuePair<string, int> kv) => kv.Value >= Plugin.NoisyRuleThreshold.Value).ToList();
			if (list.Count == 0)
			{
				return null;
			}
			return "Noisy rules: " + string.Join(", ", list.Select((KeyValuePair<string, int> kv) => $"{kv.Key} (x{kv.Value})"));
		}

		private static NetworkConnection FindConn(ulong key)
		{
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if (!((Object)(object)player == (Object)null) && !((Object)(object)player == (Object)(object)Player.LocalPlayer) && player.SteamID == key)
					{
						return ((NetworkBehaviour)player).Owner;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static void Tell(string msg)
		{
			Chat.Ask(msg);
		}

		public static void Reset()
		{
			_open.Clear();
			_dismissedRules.Clear();
			_nextId = 1;
		}
	}
	internal static class Catalogue
	{
		private static readonly HashSet<byte> _forSale = new HashSet<byte>();

		private static float _builtAt = -999f;

		private static int _stands;

		private static bool _usable;

		private static float _nextMoan;

		private static string _shelf = "";

		public static bool Usable => _usable;

		public static int Stands => _stands;

		public static int Count => _forSale.Count;

		public static IEnumerable<byte> Ids => _forSale.OrderBy((byte x) => x);

		public static string Shelf
		{
			get
			{
				Refresh();
				return _shelf;
			}
		}

		public static bool Sells(byte id)
		{
			Refresh();
			if (!_usable)
			{
				Blind();
				return true;
			}
			return _forSale.Contains(id);
		}

		public static void Watch()
		{
			Refresh();
			if (!_usable)
			{
				Blind();
			}
		}

		private static void Blind()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (!(realtimeSinceStartup < _nextMoan))
			{
				_nextMoan = realtimeSinceStartup + 30f;
				Plugin.Log.LogWarning((object)"[Catalogue] no shop stands found, so 'not-for-sale' cannot run and every purchase is being allowed on affordability alone. This is the hole creatures come through.");
			}
		}

		private static void Refresh()
		{
			if (Time.realtimeSinceStartup - _builtAt < Plugin.CatalogueRefresh.Value)
			{
				return;
			}
			_builtAt = Time.realtimeSinceStartup;
			try
			{
				ItemPurchasable[] array = Resources.FindObjectsOfTypeAll<ItemPurchasable>();
				if (array == null || array.Length == 0)
				{
					if (_forSale.Count == 0)
					{
						_usable = false;
					}
					return;
				}
				HashSet<byte> hashSet = new HashSet<byte>();
				SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
				ItemPurchasable[] array2 = array;
				foreach (ItemPurchasable val in array2)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					try
					{
						object value = Traverse.Create((object)val).Field("_itemToPurchase").GetValue();
						Item val2 = (Item)((value is Item) ? value : null);
						if ((Object)(object)val2 == (Object)null)
						{
							continue;
						}
						hashSet.Add(val2.ID);
						try
						{
							if (!string.IsNullOrEmpty(((Object)val2).name))
							{
								sortedSet.Add(((Object)val2).name);
							}
						}
						catch
						{
						}
					}
					catch
					{
					}
				}
				if (hashSet.Count == 0)
				{
					if (_forSale.Count == 0)
					{
						_usable = false;
					}
					return;
				}
				bool usable = _usable;
				_stands = array.Length;
				_forSale.Clear();
				foreach (byte item in hashSet)
				{
					_forSale.Add(item);
				}
				_usable = true;
				_shelf = string.Join(", ", sortedSet.ToArray());
				if (!usable)
				{
					Plugin.Log.LogInfo((object)($"[Catalogue] {_stands} shop stand(s) selling " + $"{_forSale.Count} item(s) - purchases of anything else " + "are now proven."));
					if (_shelf.Length > 0)
					{
						Plugin.Log.LogInfo((object)("[Catalogue] on the shelves: " + _shelf));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Catalogue] could not read the shop stands: " + ex.Message));
				if (_forSale.Count == 0)
				{
					_usable = false;
				}
			}
		}

		public static string Summary()
		{
			Refresh();
			if (!_usable)
			{
				return "Shop catalogue unavailable - purchases are not being checked against it.";
			}
			return $"{_forSale.Count} item(s) for sale across {_stands} stand(s).";
		}

		public static void Invalidate()
		{
			_builtAt = -999f;
		}
	}
	internal static class Chat
	{
		private const string Blue = "#5FC8F0";

		private const string Amber = "#FFD24A";

		private const string Red = "#FF7A63";

		private const string Green = "#7FD99A";

		public static void Info(string msg)
		{
			Send("#5FC8F0", "WARDEN", msg);
		}

		public static void Ask(string msg)
		{
			Send("#FFD24A", "WARDEN", msg);
		}

		public static void Hit(string msg)
		{
			Send("#FF7A63", "WARDEN", msg);
		}

		public static void Ok(string msg)
		{
			Send("#7FD99A", "WARDEN", msg);
		}

		private static void Send(string colour, string tag, string msg)
		{
			if (string.IsNullOrEmpty(msg))
			{
				return;
			}
			Plugin.Log.LogInfo((object)("[Fishwarden] " + Strip(msg)));
			string[] array = msg.Split('\n');
			foreach (string text in array)
			{
				if (text.Length != 0)
				{
					try
					{
						ChatManager.ChatMessage("<color=" + colour + "><b>[" + tag + "]</b></color> " + text);
					}
					catch
					{
					}
				}
			}
		}

		public static void Broadcast(string msg)
		{
			Plugin.Log.LogInfo((object)("[Fishwarden] " + Strip(msg)));
			string text = "<color=#FF7A63><b>[WARDEN]</b></color> " + msg;
			try
			{
				Player localPlayer = Player.LocalPlayer;
				if ((Object)(object)Server.Instance != (Object)null && (Object)(object)localPlayer != (Object)null)
				{
					Server.Instance.SendChatMessage(localPlayer.SteamID, text);
				}
				else
				{
					ChatManager.ChatMessage(text);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Fishwarden] broadcast failed: " + ex.Message));
			}
		}

		public static void AsPlayer(Player who, string msg)
		{
			if ((Object)(object)who == (Object)null || string.IsNullOrEmpty(msg))
			{
				return;
			}
			if (!Plugin.MuteSpeaksAsPlayer.Value)
			{
				Broadcast(msg);
				return;
			}
			Plugin.Log.LogInfo((object)("[Fishwarden] (as " + who.SteamName + ") " + Strip(msg)));
			try
			{
				Server.Instance.SendChatMessage(who.SteamID, msg);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Fishwarden] could not speak as them: " + ex.Message));
			}
		}

		private static string Strip(string s)
		{
			if (s != null)
			{
				return Regex.Replace(s, "<.*?>", "");
			}
			return "";
		}
	}
	internal static class Cheats
	{
		public static bool Immortal;

		public static bool GodMode;

		public static bool InfiniteAmmo;

		public static float MoneyMultiplier = 1f;

		public static float LuckMultiplier = 1f;

		public static float SpeedMultiplier = 1f;

		private static float _nextRevive;

		private static readonly List<GameObject> _mine = new List<GameObject>();

		private static float _walk;

		private static float _sprint;

		private static bool _swapped;

		public static bool OnePunch;

		public static float OnePunchForce = 999999f;

		private static bool _toldPunch;

		private static int _basePunch;

		private static bool _punchSwapped;

		public static bool Stretchy;

		public static float Reach = 20f;

		private static float _baseRange;

		private static float _baseRadius;

		private static bool _capturedReach;

		private static bool _toldReach;

		private static bool _toldEntered;

		private static bool _toldExit;

		private static float _nextReachLog;

		public static bool WalkOnWater;

		public static float SurfaceOffset = 0.1f;

		private static List<string> _names;

		private static readonly Dictionary<string, string> _keyOf = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static string _filter = "";

		public static int SpawnedCount
		{
			get
			{
				_mine.RemoveAll((GameObject g) => (Object)(object)g == (Object)null);
				return _mine.Count;
			}
		}

		public static string Filter
		{
			get
			{
				return _filter;
			}
			set
			{
				_filter = value;
			}
		}

		public static string ClearSpawned()
		{
			if (!Plugin.IsHosting)
			{
				return "Despawning is server side - host or play solo.";
			}
			_mine.RemoveAll((GameObject g) => (Object)(object)g == (Object)null);
			if (_mine.Count == 0)
			{
				return "Nothing spawned from here is still around.";
			}
			int num = 0;
			foreach (GameObject item in _mine.ToList())
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				try
				{
					((NetworkBehaviour)Server.Instance).Despawn(item, (DespawnType?)null);
					num++;
				}
				catch
				{
					try
					{
						Object.Destroy((Object)(object)item);
						num++;
					}
					catch
					{
					}
				}
			}
			_mine.Clear();
			Plugin.Log.LogInfo((object)("[Cheats] cleared " + num + " spawned object(s)."));
			return num + " thing(s) sent away.";
		}

		public static void Tick()
		{
			if (Immortal)
			{
				KeepUp();
			}
		}

		public static bool TakeDamage_Pre(PlayerVitals __instance)
		{
			if (!GodMode)
			{
				return true;
			}
			try
			{
				Player localPlayer = Player.LocalPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return true;
				}
				return (Object)(object)localPlayer.Vitals != (Object)(object)__instance;
			}
			catch
			{
				return true;
			}
		}

		public static bool Hit_Pre(Player __0)
		{
			if (!GodMode)
			{
				return true;
			}
			try
			{
				return (Object)(object)__0 != (Object)(object)Player.LocalPlayer;
			}
			catch
			{
				return true;
			}
		}

		public static void Fixed()
		{
			FixedTick();
		}

		private static void KeepUp()
		{
			if (Time.realtimeSinceStartup < _nextRevive)
			{
				return;
			}
			_nextRevive = Time.realtimeSinceStartup + 0.35f;
			try
			{
				Player localPlayer = Player.LocalPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)localPlayer.Vitals == (Object)null) && localPlayer.Vitals.Health <= 0)
				{
					DeadPlayer val = (((Object)(object)localPlayer.Dying != (Object)null) ? localPlayer.Dying.DeadPlayer : null);
					if ((Object)(object)val != (Object)null)
					{
						Server.Instance.ResurrectPlayer(localPlayer, val);
					}
				}
			}
			catch
			{
			}
		}

		public static void Shoot_Pre(Weapon __instance)
		{
			if (!InfiniteAmmo)
			{
				return;
			}
			try
			{
				object value = Traverse.Create((object)__instance).Field("_holder").GetValue();
				Player val = (Player)((value is Player) ? value : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.LocalPlayer))
				{
					object value2 = Traverse.Create((object)__instance).Field("_attachments").GetValue();
					Attachments val2 = (Attachments)((value2 is Attachments) ? value2 : null);
					if (!((Object)(object)val2 == (Object)null))
					{
						Traverse.Create((object)__instance).Property("Ammo", (object[])null).SetValue((object)val2.AmmoPerMag);
					}
				}
			}
			catch
			{
			}
		}

		public static void SellItem_Post(Item item)
		{
			if (MoneyMultiplier <= 1.001f)
			{
				return;
			}
			try
			{
				int num = Mathf.RoundToInt((float)item.TotalWorth * (MoneyMultiplier - 1f));
				if (num <= 0)
				{
					Plugin.Log.LogInfo((object)("[Cheats] sale of " + ((Object)item).name + " worth " + item.TotalWorth + " - x" + MoneyMultiplier.ToString("0.0") + " added nothing."));
				}
				else
				{
					MoneyManager.AddMoney(num, Player.LocalPlayer);
					Plugin.Log.LogInfo((object)("[Cheats] sale of " + ((Object)item).name + " worth " + item.TotalWorth + " - x" + MoneyMultiplier.ToString("0.0") + " added " + num + "."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Cheats] sale bonus failed: " + ex.Message));
			}
		}

		public static void Luck_Pre(List<ItemInfoWeight> __0)
		{
			if (LuckMultiplier <= 1.001f || __0 == null)
			{
				return;
			}
			try
			{
				float num = 1f / Mathf.Max(1f, LuckMultiplier);
				foreach (ItemInfoWeight item in __0)
				{
					if (item == null)
					{
						continue;
					}
					Traverse val = Traverse.Create((object)item).Field("_weight");
					if (val != null && val.FieldExists())
					{
						float value = val.GetValue<float>();
						if (!(value <= 0f))
						{
							val.SetValue((object)Mathf.Max(1f, Mathf.Pow(value, num)));
						}
					}
				}
			}
			catch
			{
			}
		}

		public static void Speed_Pre(PlayerMovement __instance)
		{
			_swapped = false;
			if (SpeedMultiplier <= 1.001f)
			{
				return;
			}
			try
			{
				if (!((Object)(object)Player.LocalPlayer == (Object)null) && !((Object)(object)Player.LocalPlayer.Movement != (Object)(object)__instance))
				{
					Traverse obj = Traverse.Create((object)__instance);
					_walk = obj.Field("_walkSpeed").GetValue<float>();
					_sprint = obj.Field("_sprintSpeed").GetValue<float>();
					obj.Field("_walkSpeed").SetValue((object)(_walk * SpeedMultiplier));
					obj.Field("_sprintSpeed").SetValue((object)(_sprint * SpeedMultiplier));
					_swapped = true;
				}
			}
			catch
			{
			}
		}

		public static void Speed_Fin(PlayerMovement __instance)
		{
			if (!_swapped)
			{
				return;
			}
			_swapped = false;
			try
			{
				Traverse obj = Traverse.Create((object)__instance);
				obj.Field("_walkSpeed").SetValue((object)_walk);
				obj.Field("_sprintSpeed").SetValue((object)_sprint);
			}
			catch
			{
			}
		}

		public static void HitTarget_Pre(PlayerPunching __instance)
		{
			_punchSwapped = false;
			if (!OnePunch)
			{
				return;
			}
			try
			{
				if ((Object)(object)Player.LocalPlayer == (Object)null || ((NetworkBehaviour)__instance).Owner == (NetworkConnection)null || !((NetworkBehaviour)__instance).Owner.IsLocalClient)
				{
					return;
				}
				Traverse val = Traverse.Create((object)__instance).Field("_damage");
				if (val != null && val.FieldExists())
				{
					_basePunch = val.GetValue<int>();
					if (!_toldPunch)
					{
						_toldPunch = true;
						Plugin.Log.LogInfo((object)("[Cheats] vanilla punch damage is " + _basePunch + ". One punch replaces it with " + OnePunchForce.ToString("0") + " for your punches only."));
					}
					val.SetValue((object)Mathf.RoundToInt(Mathf.Max(1f, OnePunchForce)));
					_punchSwapped = true;
				}
			}
			catch
			{
			}
		}

		public static void HitTarget_Fin(PlayerPunching __instance)
		{
			if (!_punchSwapped)
			{
				return;
			}
			_punchSwapped = false;
			try
			{
				Traverse.Create((object)__instance).Field("_damage").SetValue((object)_basePunch);
			}
			catch
			{
			}
		}

		private static string TypeOf(Traverse f)
		{
			try
			{
				if (f == null || !f.FieldExists())
				{
					return "absent";
				}
				object value = f.GetValue();
				return (value == null) ? "null" : value.GetType().Name;
			}
			catch (Exception ex)
			{
				return "unreadable:" + ex.GetType().Name;
			}
		}

		private static float AsFloat(Traverse f, float fallback)
		{
			try
			{
				if (f == null || !f.FieldExists())
				{
					return fallback;
				}
				object value = f.GetValue();
				return (value is float) ? ((float)value) : fallback;
			}
			catch
			{
				return fallback;
			}
		}

		private static void ReachExit(string why)
		{
			if (!_toldExit)
			{
				_toldExit = true;
				Plugin.Log.LogWarning((object)("[Cheats] long arms: prefix ran but stretched NOTHING - " + why));
			}
		}

		public static void FindPunchTarget_Pre(PlayerPunching __instance)
		{
			try
			{
				if (Stretchy && !_toldEntered)
				{
					_toldEntered = true;
					Plugin.Log.LogInfo((object)"[Cheats] long arms: prefix ENTERED with the toggle on. If you see no other long-arms line after this, it left through a silent exit - the next warning says which.");
				}
				if (Stretchy && !_toldReach && ((Object)(object)Player.LocalPlayer == (Object)null || ((NetworkBehaviour)__instance).Owner == (NetworkConnection)null || !((NetworkBehaviour)__instance).Owner.IsLocalClient))
				{
					_toldReach = true;
					Plugin.Log.LogWarning((object)("[Cheats] long arms: bailing before it applies - localPlayer=" + (((Object)(object)Player.LocalPlayer == (Object)null) ? "null" : "ok") + " owner=" + ((((NetworkBehaviour)__instance).Owner == (NetworkConnection)null) ? "null" : "ok") + " isLocalClient=" + (((NetworkBehaviour)__instance).Owner != (NetworkConnection)null && ((NetworkBehaviour)__instance).Owner.IsLocalClient) + ". Nothing is being stretched."));
				}
				if ((Object)(object)Player.LocalPlayer == (Object)null || ((NetworkBehaviour)__instance).Owner == (NetworkConnection)null || !((NetworkBehaviour)__instance).Owner.IsLocalClient)
				{
					return;
				}
				Traverse obj = Traverse.Create((object)__instance);
				Traverse val = obj.Field("_range");
				Traverse val2 = obj.Field("_hitRadius");
				Traverse f = obj.Field("_noTargetHitPos");
				if (val == null || !val.FieldExists())
				{
					if (Stretchy)
					{
						ReachExit("PlayerPunching has no readable _range field, so there is nothing to multiply. The two callees read it, not this method - if it moved, find where CheckForItems reads its distance from.");
					}
					return;
				}
				if (!_capturedReach)
				{
					_baseRange = AsFloat(val, _baseRange);
					_baseRadius = AsFloat(val2, 0f);
					_capturedReach = true;
					Plugin.Log.LogInfo((object)("[Cheats] long arms: field types - _range=" + TypeOf(val) + " (" + _baseRange.ToString("0.00") + "m), _hitRadius=" + TypeOf(val2) + ", _noTargetHitPos=" + TypeOf(f) + " (not touched - it is a position, not a distance, and reading it as one is what broke this)."));
				}
				if (Stretchy)
				{
					float num = _baseRange * Reach;
					val.SetValue((object)num);
					if (val2 != null && val2.FieldExists())
					{
						val2.SetValue((object)(_baseRadius * Mathf.Min(4f, 1f + Reach * 0.1f)));
					}
					if (!_toldReach)
					{
						_toldReach = true;
						Plugin.Log.LogInfo((object)("[Cheats] long arms: vanilla punch reach is " + _baseRange.ToString("0.00") + "m, hit radius " + _baseRadius.ToString("0.00") + ". At x" + Reach.ToString("0") + " that is " + num.ToString("0.0") + "m. Read back after the punch to see whether anything was actually in range."));
					}
				}
				else
				{
					val.SetValue((object)_baseRange);
					if (val2 != null && val2.FieldExists())
					{
						val2.SetValue((object)_baseRadius);
					}
				}
			}
			catch (Exception ex)
			{
				if (Stretchy)
				{
					ReachExit("it threw: " + ex.GetType().Name + " - " + ex.Message);
				}
			}
		}

		public static void FindPunchTarget_Post(PlayerPunching __instance)
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_0135: Unknown result type (might be due to invalid IL or missing references)
			if (!Stretchy)
			{
				return;
			}
			try
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (!(realtimeSinceStartup < _nextReachLog))
				{
					_nextReachLog = realtimeSinceStartup + 1f;
					Traverse val = Traverse.Create((object)__instance);
					object value = val.Field("_curTarget").GetValue();
					Object val2 = (Object)((value is Object) ? value : null);
					float value2 = val.Field("_range").GetValue<float>();
					bool flag = (Object)(object)Player.LocalPlayer != (Object)null && ((NetworkBehaviour)__instance).Owner != (NetworkConnection)null && ((NetworkBehaviour)__instance).Owner.IsLocalClient;
					if (val2 == (Object)null)
					{
						Plugin.Log.LogInfo((object)("[Cheats] long arms: punched with a reach of " + value2.ToString("0.0") + "m and found NOTHING. If something was clearly in front of you, the casts are not reaching it - the two that matter are CheckForItems and CheckForPlayersAndLevel, both cast from the camera. INSTANCE=" + (flag ? "yours - so the prefix really did not multiply _range" : "NOT yours - this is a remote player's punch and the prefix skipped it on purpose; your own punches are not being logged here")));
						return;
					}
					Vector3 value3 = val.Field("_targetHitPoint").GetValue<Vector3>();
					Vector3 val3 = (((Object)(object)Player.LocalPlayer != (Object)null) ? Body.Of(Player.LocalPlayer) : Vector3.zero);
					Plugin.Log.LogInfo((object)("[Cheats] long arms: reach " + value2.ToString("0.0") + "m, hit '" + val2.name + "' at " + Vector3.Distance(val3, value3).ToString("0.0") + "m. The range IS being applied - if the punch looked like it did nothing, that is the arm animation not following, not the detection. INSTANCE=" + (flag ? "yours" : "not yours")));
				}
			}
			catch
			{
			}
		}

		public static void FixedTick()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (!WalkOnWater)
			{
				return;
			}
			try
			{
				Player localPlayer = Player.LocalPlayer;
				if ((Object)(object)localPlayer == (Object)null || localPlayer.BlockInputs)
				{
					return;
				}
				PlayerMovement movement = localPlayer.Movement;
				if ((Object)(object)movement != (Object)null && movement.OnBoat)
				{
					return;
				}
				Rigidbody rigidbody = localPlayer.Rigidbody;
				if ((Object)(object)rigidbody == (Object)null)
				{
					return;
				}
				float num = WaterManager.WaterHeight + SurfaceOffset;
				float y = localPlayer.Transform.position.y;
				if (!(y >= num))
				{
					Vector3 position = rigidbody.position;
					position.y += num - y;
					rigidbody.position = position;
					Vector3 linearVelocity = rigidbody.linearVelocity;
					if (linearVelocity.y < 0f)
					{
						linearVelocity.y = 0f;
						rigidbody.linearVelocity = linearVelocity;
					}
				}
			}
			catch
			{
			}
		}

		public static List<string> Names(string filter)
		{
			if (_names == null)
			{
				_names = new List<string>();
				try
				{
					if (Traverse.Create(typeof(GameInfo)).Field("_nameToSpawnable").GetValue() is IDictionary dictionary)
					{
						foreach (DictionaryEntry item in dictionary)
						{
							object? value = item.Value;
							Item val = (Item)((value is Item) ? value : null);
							if (!((Object)(object)val == (Object)null))
							{
								string text = ((Object)val).name.Replace("(Clone)", "").Trim();
								_names.Add(text);
								string value2 = ((item.Key != null) ? item.Key.ToString() : null);
								if (!string.IsNullOrEmpty(value2) && !_keyOf.ContainsKey(text))
								{
									_keyOf[text] = value2;
								}
							}
						}
					}
					_names = (from x in _names.Distinct()
						orderby x
						select x).ToList();
					Plugin.Log.LogInfo((object)("[Cheats] " + _names.Count + " spawnables (shown -> key):"));
					foreach (string name in _names)
					{
						Plugin.Log.LogInfo((object)("[Cheats]   " + name + "  ->  " + (_keyOf.TryGetValue(name, out var value3) ? value3 : "(no key)")));
					}
				}
				catch
				{
				}
			}
			if (string.IsNullOrWhiteSpace(filter))
			{
				return _names;
			}
			return _names.Where((string n) => n.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
		}

		public static string Spawn(string name)
		{
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.IsHosting)
			{
				return "Only the host can spawn.";
			}
			try
			{
				Names("");
				if (!_keyOf.TryGetValue(name, out var value) || string.IsNullOrEmpty(value))
				{
					value = name.Replace(" ", "").ToLowerInvariant();
				}
				string text = name.Replace(" ", "").ToLowerInvariant();
				List<string> list = new List<string>();
				GameObject val = null;
				string[] array = new string[3]
				{
					value,
					value.ToLowerInvariant(),
					text
				};
				foreach (string text2 in array)
				{
					if (!string.IsNullOrEmpty(text2) && !list.Contains(text2))
					{
						list.Add(text2);
						Item spawnable = GameInfo.GetSpawnable(text2);
						if (!((Object)(object)spawnable == (Object)null))
						{
							val = ((Component)spawnable).gameObject;
							break;
						}
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)("[Cheats] no spawnable for '" + name + "' - tried " + string.Join(", ", list.ToArray())));
					return "No item called " + name + " - looked under " + string.Join(" and ", list.Select((string x) => "\"" + x + "\"").ToArray()) + ".";
				}
				Player localPlayer = Player.LocalPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return "No local player to spawn in front of.";
				}
				Vector3 val2 = Body.InFrontOf(localPlayer);
				GameObject val3 = Object.Instantiate<GameObject>(val, val2, Quaternion.identity);
				((NetworkBehaviour)Server.Instance).Spawn(val3.gameObject, (NetworkConnection)null, default(Scene));
				_mine.RemoveAll((GameObject g) => (Object)(object)g == (Object)null);
				_mine.Add(val3.gameObject);
				Vector3 position = val3.transform.position;
				ManualLogSource log = Plugin.Log;
				string[] obj = new string[8]
				{
					"[Cheats] spawned '",
					name,
					"' as key '",
					value,
					"' at ",
					((Vector3)(ref position)).ToString("0.0"),
					" - you are at ",
					null
				};
				Vector3 val4 = Body.Of(localPlayer);
				obj[7] = ((Vector3)(ref val4)).ToString("0.0");
				log.LogInfo((object)string.Concat(obj));
				Item component = val3.GetComponent<Item>();
				string text3 = (((Object)(object)component != (Object)null) ? Handed(localPlayer, component) : null);
				return "Spawned " + ((Object)val).name + (text3 ?? " just in front of you.");
			}
			catch (Exception ex)
			{
				return "Could not spawn: " + ex.Message;
			}
		}

		private static string Handed(Player who, Item item)
		{
			try
			{
				if ((Object)(object)item.Creature != (Object)null)
				{
					Plugin.Log.LogInfo((object)("[Cheats] " + ((Object)item).name + " is a creature - left where it landed rather than put in your hands."));
					return " in front of you - creatures are not held.";
				}
			}
			catch
			{
			}
			if (Call(item, "PickUp", who, item))
			{
				Plugin.Log.LogInfo((object)("[Cheats] " + ((Object)item).name + " picked up through the game's own path."));
				return " into your hands.";
			}
			if (!Call(who.Holding, "SetHeldItem", who, item))
			{
				Report(who.Holding, "SetHeldItem");
				return null;
			}
			bool num = Call(who.Holding, "ServerTryStoreHeldItem", who, item) || Call(Server.Instance, "ServerTryStoreHeldItem", who, item);
			Plugin.Log.LogWarning((object)("[Cheats] Item.PickUp did not match - fell back to SetHeldItem, so " + ((Object)item).name + " may not be attached properly."));
			if (!num)
			{
				return " into your hands.";
			}
			return " into your inventory.";
		}

		private static bool Call(object target, string method, Player who, Item item)
		{
			if (target == null)
			{
				return false;
			}
			try
			{
				MethodInfo[] methods = target.GetType().GetMethods(AccessTools.all);
				foreach (MethodInfo methodInfo in methods)
				{
					if (methodInfo.Name != method)
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo.GetParameters();
					object[] array = new object[parameters.Length];
					bool flag = true;
					for (int j = 0; j < parameters.Length; j++)
					{
						Type parameterType = parameters[j].ParameterType;
						if (parameterType.IsInstanceOfType(item))
						{
							array[j] = item;
							continue;
						}
						if (parameterType.IsInstanceOfType(who))
						{
							array[j] = who;
							continue;
						}
						if (parameterType == typeof(bool))
						{
							array[j] = true;
							continue;
						}
						if (parameterType.IsValueType)
						{
							array[j] = Activator.CreateInstance(parameterType);
							continue;
						}
						flag = false;
						break;
					}
					if (flag)
					{
						methodInfo.Invoke(target, array);
						Plugin.Log.LogInfo((object)("[Cheats] called " + target.GetType().Name + "." + methodInfo.Name + "(" + parameters.Length + " args)."));
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Cheats] " + method + " failed: " + ex.Message));
			}
			return false;
		}

		private static void Report(object target, string wanted)
		{
			if (target == null)
			{
				Plugin.Log.LogWarning((object)"[Cheats] no holder to give it to.");
				return;
			}
			try
			{
				string[] value = (from n in (from m in target.GetType().GetMethods(AccessTools.all)
						select m.Name into n
						where n.IndexOf("Item", StringComparison.OrdinalIgnoreCase) >= 0
						select n).Distinct()
					orderby n
					select n).ToArray();
				Plugin.Log.LogWarning((object)("[Cheats] no " + wanted + " on " + target.GetType().Name + ". Item-related methods it DOES have: " + string.Join(", ", value)));
			}
			catch
			{
			}
		}

		public static void Install(Harmony h)
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Expected O, but got Unknown
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Expected O, but got Unknown
			List<string> attached = new List<string>();
			List<string> missing = new List<string>();
			Patch(typeof(PlayerVitals), "TakeDamage", "TakeDamage_Pre");
			Patch(typeof(Server), "HitPlayer", "Hit_Pre");
			Patch(typeof(Weapon), "Shoot", "Shoot_Pre");
			Patch(typeof(PlayerPunching), "HitTarget", "HitTarget_Pre", "HitTarget_Fin");
			Patch(typeof(PlayerPunching), "FindPunchTarget", "FindPunchTarget_Pre", null, "FindPunchTarget_Post");
			Patch(typeof(PlayerMovement), "UpdateMoveSpeed", "Speed_Pre", "Speed_Fin");
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(MoneyManager), "SellItem", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Cheats), "SellItem_Post", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Cheats] SellItem: " + ex.Message));
			}
			try
			{
				MethodInfo methodInfo2 = AccessTools.Method(typeof(CreatureManager), "GetRandomItem", (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					h.Patch((MethodBase)methodInfo2, new HarmonyMethod(AccessTools.Method(typeof(Cheats), "Luck_Pre", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("[Cheats] GetRandomItem: " + ex2.Message));
			}
			Plugin.Log.LogInfo((object)("[Cheats] hooked: " + string.Join(", ", attached.ToArray())));
			if (missing.Count > 0)
			{
				Plugin.Log.LogWarning((object)("[Cheats] NOT hooked: " + string.Join(", ", missing.ToArray()) + " - whatever those did will do nothing."));
			}
			Plugin.Log.LogWarning((object)"[Cheats] FULL BUILD - private tools are active. Do not hand this DLL out.");
			void Patch(Type type, string method, string pre, string fin = null, string postfix = null)
			{
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					MethodInfo methodInfo3 = AccessTools.Method(type, method, (Type[])null, (Type[])null);
					if (methodInfo3 == null)
					{
						Plugin.Log.LogWarning((object)("[Cheats] " + type.Name + "." + method + " not found"));
						missing.Add(type.Name + "." + method);
					}
					else
					{
						attached.Add(type.Name + "." + method);
						h.Patch((MethodBase)methodInfo3, (pre == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Cheats), pre, (Type[])null, (Type[])null)), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Cheats), postfix, (Type[])null, (Type[])null)), (HarmonyMethod)null, (fin == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Cheats), fin, (Type[])null, (Type[])null)), (HarmonyMethod)null);
					}
				}
				catch (Exception ex3)
				{
					Plugin.Log.LogWarning((object)("[Cheats] " + method + ": " + ex3.Message));
				}
			}
		}
	}
	internal static class ClientGuard
	{
		private static float _nextBark;

		private static int _swallowed;

		private static float _islandGraceUntil = -1f;

		private static int _lastIsland = -1;

		private static void Bark(string msg)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < _nextBark)
			{
				_swallowed++;
				return;
			}
			_nextBark = realtimeSinceStartup + 1f;
			Plugin.Log.LogWarning((object)(msg + ((_swallowed > 0) ? $"  (+{_swallowed} more in the last second)" : "")));
			_swallowed = 0;
		}

		public static bool Teleport_Pre(Player __instance, NetworkConnection __0, Vector3 __1, float __2)
		{
			//IL_0032: 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)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player localPlayer = Player.LocalPlayer;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)__instance != (Object)(object)localPlayer)
				{
					return true;
				}
				if (Time.realtimeSinceStartup < _islandGraceUntil)
				{
					return true;
				}
				if (!Body.Sane(__1))
				{
					Bark("[Anchor] refused a teleport to coordinates that are not a place - this crashes the game and has been dropped.");
					return false;
				}
				float num = Vector3.Distance(Body.Of(localPlayer), __1);
				if (num < Plugin.AnchorMinDistance.Value)
				{
					return true;
				}
				string text = $"something moved you {num:0}m to {__1.x:0},{__1.y:0},{__1.z:0}";
				if (!Plugin.Anchor.Value)
				{
					Bark("[Anchor] " + text + " (Anchor is off - allowed)");
					Notice("[Fishwarden] " + text);
					return true;
				}
				Bark("[Anchor] refused: " + text);
				Notice("[Fishwarden] refused a teleport - " + text);
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Anchor] check failed, allowing: " + ex.Message));
				return true;
			}
		}

		public static void Tick()
		{
			try
			{
				byte curIsland = OnlineIslandManager.CurIsland;
				if (curIsland != _lastIsland)
				{
					_lastIsland = curIsland;
					_islandGraceUntil = Time.realtimeSinceStartup + Plugin.IslandGraceSeconds.Value;
				}
			}
			catch
			{
			}
		}

		private static void Notice(string msg)
		{
			Chat.Hit(msg);
		}
	}
	internal static class ClientWatch
	{
		private sealed class Track
		{
			public Vector3 LastPos;

			public float LastTime;

			public int Over;

			public float AirborneSince = -1f;

			public float LastReport = -999f;

			public bool Primed;
		}

		private static readonly Dictionary<Player, Track> _tracks = new Dictionary<Player, Track>();

		private static readonly Dictionary<string, int> _suspects = new Dictionary<string, int>();

		private static float _nextSample;

		public static IReadOnlyDictionary<string, int> Suspects => _suspects;

		public static void Tick()
		{
			if (Plugin.IsHosting || !Plugin.WatchAsClient.Value)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < _nextSample)
			{
				return;
			}
			_nextSample = realtimeSinceStartup + 0.1f;
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if (!((Object)(object)player == (Object)null) && !((Object)(object)player == (Object)(object)Player.LocalPlayer))
					{
						Sample(player, realtimeSinceStartup);
					}
				}
			}
			catch
			{
			}
		}

		private static void Sample(Player p, float now)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_0084: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			if (!_tracks.TryGetValue(p, out var value))
			{
				value = new Track();
				_tracks[p] = value;
			}
			Vector3 val = Body.Of(p);
			if (!value.Primed)
			{
				value.LastPos = val;
				value.LastTime = now;
				value.Primed = true;
				return;
			}
			float num = now - value.LastTime;
			value.LastTime = now;
			Vector3 lastPos = value.LastPos;
			value.LastPos = val;
			if (num < 0.05f || num > 1f)
			{
				value.Over = 0;
				return;
			}
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(val.x - lastPos.x, 0f, val.z - lastPos.z);
			float num2 = ((Vector3)(ref val2)).magnitude / num;
			float num3 = Plugin.FallbackSpeedCeiling.Value * Mathf.Max(1f, Plugin.SlackLine.Value);
			if (num2 > num3)
			{
				value.Over++;
				if (value.Over >= Plugin.SpeedSamples.Value && now - value.LastReport > Plugin.MovementFindingCooldown.Value)
				{
					value.LastReport = now;
					value.Over = 0;
					Flag(p, $"moving at {num2:0} u/s (ceiling {num3:0})");
				}
			}
			else
			{
				value.Over = 0;
			}
			bool flag;
			try
			{
				flag = (Object)(object)p.Movement != (Object)null && !p.Movement.Grounded && !p.Movement.OnBoat;
			}
			catch
			{
				flag = false;
			}
			if (!flag)
			{
				value.AirborneSince = -1f;
				return;
			}
			if (value.AirborneSince < 0f)
			{
				value.AirborneSince = now;
			}
			float num4 = now - value.AirborneSince;
			if (num4 > Plugin.FlightSeconds.Value && now - value.LastReport > Plugin.MovementFindingCooldown.Value)
			{
				value.LastReport = now;
				value.AirborneSince = now;
				Flag(p, $"airborne {num4:0.0}s with no boat");
			}
		}

		private static void Flag(Player p, string what)
		{
			string text = p.SteamName ?? "?";
			_suspects.TryGetValue(text, out var value);
			_suspects[text] = value + 1;
			Plugin.Log.LogWarning((object)$"[FishFinder] {text}: {what}  (x{value + 1})");
		}

		public static string Readout()
		{
			if (_suspects.Count == 0)
			{
				return "Fish Finder: nothing unusual seen.";
			}
			List<string> list = new List<string> { "Fish Finder - what I saw from here:" };
			foreach (KeyValuePair<string, int> suspect in _suspects)
			{
				list.Add($"  {suspect.Key}: {suspect.Value} anomalies");
			}
			list.Add("(seen as a guest - I cannot prove intent, only what my client rendered)");
			return string.Join("\n", list);
		}

		public static void Reset()
		{
			_tracks.Clear();
			_suspects.Clear();
		}
	}
	internal static class Commands
	{
		private static readonly string[] Ours = new string[40]
		{
			"net", "level", "rule", "warden", "logbook", "report", "crew", "uncrew", "pardon", "gut",
			"chum", "skunk", "unskunk", "cooler", "uncooler", "locker", "blowback", "butterfingers", "troll", "wall",
			"unban", "finder", "anchor", "help", "cases", "accept", "dismiss", "autokick", "trace", "summary",
			"selftest", "stress", "island", "fly", "purge", "mute", "freeze", "lockdown", "strip", "autotroll"
		};

		public static void Install(Harmony h)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(DazedCommands), "IsServerCommand", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)"[Fishwarden] chat commands unavailable - IsServerCommand not found.");
			}
			else
			{
				h.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(Commands), "Pre", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static bool Pre(string fullCommand, ref bool __result)
		{
			try
			{
				if (string.IsNullOrEmpty(fullCommand) || !fullCommand.StartsWith("/"))
				{
					return true;
				}
				string text = fullCommand.Substring(1).Trim();
				if (text.Length == 0)
				{
					return true;
				}
				int num = text.IndexOf(' ');
				string text2 = ((num < 0) ? text : text.Substring(0, num)).ToLowerInvariant();
				string text3 = ((num < 0) ? "" : text.Substring(num + 1).Trim());
				if (text2 == "help" && !text3.StartsWith("warden", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				if (!Ours.Contains(text2))
				{
					return true;
				}
				Handle(text2, text3);
				__result = true;
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[Fishwarden] command error: " + ex));
				return true;
			}
		}

		private static void Handle(string cmd, string arg)
		{
			if (cmd == null)
			{
				return;
			}
			switch (cmd.Length)
			{
			case 4:
				switch (cmd[0])
				{
				case 'h':
					if (cmd == "help")
					{
						Help();
					}
					break;
				case 'r':
					if (cmd == "rule")
					{
						Rule(arg);
					}
					break;
				case 'c':
					if (!(cmd == "crew"))
					{
						if (cmd == "chum")
						{
							HostOnly(() => (!Extras.ChumOut) ? Extras.DropChum() : DropOrClear());
						}
					}
					else
					{
						WithPlayer(arg, Crew.Grant);
					}
					break;
				case 'w':
					if (cmd == "wall")
					{
						Wall();
					}
					break;
				case 'm':
					if (cmd == "mute")
					{
						Mute(arg);
					}
					break;
				}
				break;
			case 3:
				switch (cmd[0])
				{
				default:
					return;
				case 'n':
					break;
				case 'g':
					if (cmd == "gut")
					{
						Gut(arg);
					}
					return;
				case 'f':
					if (cmd == "fly")
					{
						Say(HostTools.ToggleFly());
					}
					return;
				}
				if (!(cmd == "net"))
				{
					break;
				}
				goto IL_046b;
			case 5:
				switch (cmd[1])
				{
				default:
					return;
				case 'e':
					break;
				case 'k':
					if (cmd == "skunk")
					{
						WithPlayer(arg, Extras.Skunk);
					}
					return;
				case 'r':
					if (!(cmd == "troll"))
					{
						if (cmd == "trace")
						{
							TraceCmd(arg);
						}
					}
					else
					{
						WithPlayer(arg, Troll.Toggle);
					}
					return;
				case 'n':
					if (cmd == "unban")
					{
						Unban(arg);
					}
					return;
				case 'a':
					if (cmd == "cases")
					{
						Say(Cases.List());
					}
					return;
				case 'u':
					if (cmd == "purge")
					{