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

BepInEx/plugins/Fishwarden/Fishwarden.dll

Decompiled an hour 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.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.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("Fishwarden")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.15.0.0")]
[assembly: AssemblyInformationalVersion("0.15.0+445db78896d672f01652a0ae886131ba3809b62f")]
[assembly: AssemblyProduct("Fishwarden")]
[assembly: AssemblyTitle("Fishwarden")]
[assembly: AssemblyVersion("0.15.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace 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 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 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 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.Has(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 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.Confidence == Confidence.Certain;
				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 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.Confidence == Confidence.Certain),
				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.Confidence == Confidence.Certain);
				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 float _baseNoPos;

		private static bool _capturedReach;

		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 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
			{
			}
		}

		public static void FindPunchTarget_Pre(PlayerPunching __instance)
		{
			try
			{
				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 val3 = obj.Field("_noTargetHitPos");
				if (val == null || !val.FieldExists())
				{
					return;
				}
				if (!_capturedReach)
				{
					_baseRange = val.GetValue<float>();
					_baseRadius = ((val2 != null && val2.FieldExists()) ? val2.GetValue<float>() : 0f);
					_baseNoPos = ((val3 != null && val3.FieldExists()) ? val3.GetValue<float>() : 0f);
					_capturedReach = true;
				}
				if (Stretchy)
				{
					val.SetValue((object)(_baseRange * Reach));
					if (val2 != null && val2.FieldExists())
					{
						val2.SetValue((object)(_baseRadius * Mathf.Min(4f, 1f + Reach * 0.1f)));
					}
					if (val3 != null && val3.FieldExists())
					{
						val3.SetValue((object)(_baseNoPos * Reach));
					}
				}
				else
				{
					val.SetValue((object)_baseRange);
					if (val2 != null && val2.FieldExists())
					{
						val2.SetValue((object)_baseRadius);
					}
					if (val3 != null && val3.FieldExists())
					{
						val3.SetValue((object)_baseNoPos);
					}
				}
			}
			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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Expected O, but got Unknown
			List<string> attached = new List<string>();
			List<string> missing = new List<string>();
			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");
			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)
			{
				//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)
				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)), (HarmonyMethod)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_0452;
			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")
					{
						if (arg.Equals("all", StringComparison.OrdinalIgnoreCase))
						{
							Say(Actions.PurgeAll());
						}
						else
						{
							WithPlayer(arg, Actions.Purge);
						}
					}
					return;
				case 't':
					if (cmd == "strip")
					{
						WithPlayer(arg, Actions.Strip);
					}
					return;
				}
				if (!(cmd == "level"))
				{
					break;
				}
				goto IL_0452;
			case 6:
				switch (cmd[0])
				{
				default:
					return;
				case 'w':
					break;
				case 'r':
					if (cmd == "report")
					{
						Report();
					}
					return;
				case 'u':
					if (cmd == "uncrew")
					{
						WithPlayer(arg, Crew.Revoke);
					}
					return;
				case 'p':
					if (cmd == "pardon")
					{
						WithPlayer(arg, delegate(Player p)
						{
							Logbook.Pardon(p.SteamID);
							Rollback.Clear(p.SteamID);
							Extras.Release(p.SteamID);
							return p.SteamName + " pardoned - strikes cleared.";
						});
					}
					return;
				case 'c':
					if (cmd == "cooler")
					{
						WithPlayer(arg, Extras.Cool);
					}
					return;
				case 'l':
					if (cmd == "locker")
					{
						WithPlayer(arg, Extras.Locker);
					}
					return;
				case 'f':
					if (!(cmd == "finder"))
					{
						if (cmd == "freeze")
						{
							WithPlayer(arg, Actions.ToggleFreeze);
						}
					}
					else
					{
						Say(ClientWatch.Readout());
					}
					return;
				case 'a':
					if (!(cmd == "anchor"))
					{
						if (cmd == "accept")
						{
							Decide(arg, accept: true);
						}
					}
					else
					{
						Anchor();
					}
					return;
				case 'i':
					if (cmd == "island")
					{
						Island(arg);
					}
					return;
				case 's':
				{
					if (!(cmd == "stress"))
					{
						return;
					}
					if (!int.TryParse(arg, out var result))
					{
						result = 20000;
					}
					{
						foreach (string item in SelfTest.Stress(result))
						{
							Say(item);
						}
						return;
					}
				}
				}
				if (!(cmd == "warden"))
				{
					break;
				}
				goto IL_0452;
			case 7:
				switch (cmd[0])
				{
				case 'l':
					if (cmd == "logbook")
					{
						Recent();
					}
					break;
				case 'u':
					if (cmd == "unskunk")
					{
						WithPlayer(arg, Extras.Unskunk);
					}
					break;
				case 'd':
					if (cmd == "dismiss")
					{
						Decide(arg, accept: false);
					}
					break;
				case 's':
					if (!(cmd == "summary"))
					{
						break;
					}
					{
						foreach (string item2 in Summary.Lines())
						{
							Say(item2);
						}
						break;
					}
				}
				break;
			case 8:
				switch (cmd[0])
				{
				case 'u':
					if (cmd == "uncooler")
					{
						WithPlayer(arg, Extras.Uncool);
					}
					break;
				case 'b':
					if (cmd == "blowback")
					{
						WithPlayer(arg, Extras.Blowback);
					}
					break;
				case 'a':
					if (cmd == "autokick")
					{
						AutoKick(arg);
					}
					break;
				case 'l':
					if (cmd == "lockdown")
					{
						Say(Actions.ToggleLockdown());
					}
					break;
				case 's':
					if (!(cmd == "selftest"))
					{
						break;
					}
					{
						foreach (string item3 in SelfTest.Run())
						{
							Say(item3);
						}
						break;
					}
				}
				break;
			case 13:
				if (cmd == "butterfingers")
				{
					WithPlayer(arg, Extras.Butterfingers);
				}
				break;
			case 9:
				if (cmd == "autotroll")
				{
					AutoTrollCmd(arg);
				}
				break;
			case 10:
			case 11:
			case 12:
				break;
				IL_0452:
				Net(arg);
				break;
			}
		}

		private static string DropOrClear()
		{
			Extras.ClearChum();
			return "Chum cleared.";
		}

		private static void Help()
		{
			Say("Fishwarden 2.0.0");
			Say("  /level watch|block|strict|lockdown    /level status");
			Say("  /logbook  /report  /wall  /unban <n>");
			Say("  /crew <n>  /uncrew <n>  /pardon <n>  /gut <n|all>");
			Say("  /chum  /skunk <n>  /cooler <n>  /locker <n>");
			Say("  /blowback <n>  /butterfingers <n>  /troll <n>");
			Say("  /mute <n> [swearing|racism|antisemitism|politics|shouting]");
			Say("  /freeze <n>  /strip <n>  /purge <n|all>  /lockdown");
			Say("  /cases  /accept <n|all>  /dismiss <n|all>  /autokick on|off  /rule off <name>");
			Say("  /summary  -  what everyone has been trying");
			Say("  /selftest  -  check the guard is behaving   /stress [n]");
			Say("  /island <n>|next|prev  -  move the whole lobby   /fly");
			Say("  /trace  -  what got called that I have no rule for");
			Say("  /finder (as a guest)  /anchor");
		}

		private static void Net(string arg)
		{
			if (string.IsNullOrEmpty(arg))
			{
				Status();
				return;
			}
			switch (arg.ToLowerInvariant())
			{
			case "watch":
				Plugin.SetLevel(Level.Watch);
				break;
			case "block":
				Plugin.SetLevel(Level.Block);
				break;
			case "strict":
				Plugin.SetLevel(Level.Strict);
				break;
			case "lockdown":
				Plugin.SetLevel(Level.Lockdown);
				break;
			case "status":
				Status();
				return;
			default:
			{
				Presets.Preset preset = Presets.Find(arg);
				if (preset != null)
				{
					Say(Presets.Apply(preset));
					return;
				}
				Say("usage: /level watch | block | strict | lockdown");
				Say("   or a preset: " + string.Join(", ", Presets.All.Select((Presets.Preset x) => x.Name)));
				return;
			}
			}
			Say(Policy.Spell());
		}

		private static void Rule(string arg)
		{
			if (string.IsNullOrWhiteSpace(arg))
			{
				Say((Rules.DisabledCount == 0) ? "Every rule is switched on." : ("Not checking: " + string.Join(", ", Rules.Disabled)));
				Say("usage: /rule off <name>  |  /rule on <name>");
				return;
			}
			int num = arg.IndexOf(' ');
			if (num < 0)
			{
				Say("usage: /rule off <name>  |  /rule on <name>");
				return;
			}
			string text = arg.Substring(0, num).ToLowerInvariant();
			string text2 = arg.Substring(num + 1).Trim();
			if (text == "off")
			{
				Say(Rules.Disable(text2) ? ("'" + text2 + "' switched off.") : ("'" + text2 + "' was already off."));
			}
			else if (text == "on")
			{
				Say(Rules.Enable(text2) ? ("'" + text2 + "' switched back on.") : ("'" + text2 + "' was already on."));
			}
			else
			{
				Say("usage: /rule off <name>  |  /rule on <name>");
			}
		}

		private static void Status()
		{
			if (!TackleCheck.Passed)
			{
				Say("Fishwarden is DISARMED - the game changed and the guard could not attach.");
				{
					foreach (string item in TackleCheck.Failures.Take(4))
					{
						Say("  " + item);
					}
					return;
				}
			}
			Say("Fishwarden 2.0.0 - " + Policy.Spell() + " - " + TackleCheck.Summary());
			if (!Plugin.IsHosting)
			{
				Say("  guest: nothing to enforce here. /finder for what I have seen.");
				return;
			}
			Say("  " + Catalogue.Summary());
			Say("  auto-kick: " + (Plugin.AutoKick.Value ? "ON" : "OFF - cases wait for you"));
			if (Cases.PendingCount > 0)
			{
				Say($"  {Cases.PendingCount} case(s) waiting - /cases");
			}
			string text = Extras.Status();
			if (text.Length > 0)
			{
				Say("  " + text);
			}
			Dictionary<ulong, int> dictionary = Logbook.Tally();
			if (dictionary.Count == 0)
			{
				Say("  no findings this session - clean water.");
				return;
			}
			foreach (KeyValuePair<ulong, int> item2 in dictionary.OrderByDescending((KeyValuePair<ulong, int> k) => k.Value))
			{
				Say($"  {Logbook.Name(item2.Key)}: {item2.Value} strikes" + ((item2.Value >= Plugin.KickThreshold.Value) ? " (over threshold)" : "") + (Rollback.HasDamage(item2.Key) ? " - /gut to undo" : ""));
			}
		}

		private static void Recent()
		{
			List<Entry> list = Logbook.Tail(8);
			if (list.Count == 0)
			{
				Say("Logbook is empty.");
				return;
			}
			foreach (Entry item in list)
			{
				Say(item.Line);
			}
		}

		private static void Report()
		{
			try
			{
				Directory.CreateDirectory(Logbook.Folder);
				string text = Path.Combine(Logbook.Folder, "report_" + DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".txt");
				File.WriteAllText(text, Logbook.Report());
				Say("Report written: " + text);
			}
			catch (Exception ex)
			{
				Say("Could not write the report: " + ex.Message);
			}
		}

		private static void Gut(string arg)
		{
			if (!Plugin.IsHosting)
			{
				Say("Only the host can undo anything.");
				return;
			}
			if (arg.Equals("all", StringComparison.OrdinalIgnoreCase))
			{
				Say(Rollback.RestoreWorld());
				return;
			}
			WithPlayer(arg, (Player p) => Rollback.Gut(p.SteamID));
		}

		private static void Wall()
		{
			if (Bans.Count == 0)
			{
				Say("Trophy wall is empty.");
				return;
			}
			Say($"Trophy wall ({Bans.Count}):");
			foreach (Bans.Record item in Bans.All.Take(12))
			{
				Say($"  {item.Name} - {item.Reason} ({item.When:yyyy-MM-dd})");
			}
		}

		private static void Unban(string arg)
		{
			if (string.IsNullOrWhiteSpace(arg))
			{
				Say("Who? /wall lists them.");
				return;
			}
			Bans.Record record = Bans.Find(arg);
			if (record == null)
			{
				Say("Nobody on the wall matching \"" + arg + "\".");
				return;
			}
			Bans.Remove(record.SteamId);
			Say(record.Name + " taken off the wall - they can rejoin.");
		}

		private static void Decide(string arg, bool accept)
		{
			int result;
			if (!Plugin.IsHosting)
			{
				Say("Only the host reviews cases.");
			}
			else if (string.IsNullOrWhiteSpace(arg))
			{
				Say(accept ? "Accept which? /cases lists them." : "Dismiss which? /cases lists them.");
			}
			else if (arg.Equals("all", StringComparison.OrdinalIgnoreCase))
			{
				Say(accept ? Cases.AcceptAll() : Cases.DismissAll());
			}
			else if (!int.TryParse(arg.TrimStart('#'), out result))
			{
				Say("'" + arg + "' is not a case number. /cases lists them.");
			}
			else
			{
				Say(accept ? Cases.Accept(result) : Cases.Dismiss(result));
			}
		}

		private static void AutoKick(string arg)
		{
			string text = arg.ToLowerInvariant();
			switch (text)
			{
			case "on":
			case "true":
				Plugin.AutoKick.Value = true;
				break;
			case "off":
			case "false":
				Plugin.AutoKick.Value = false;
				break;
			default:
				if (text.Length == 0)
				{
					Plugin.AutoKick.Value = !Plugin.AutoKick.Value;
					break;
				}
				Say("usage: /autokick on | off");
				return;
			}
			Say(Plugin.AutoKick.Value ? "Auto-kick ON - players are removed as soon as they cross the threshold." : "Auto-kick OFF - crossing the threshold opens a case for you to accept or dismiss.");
		}

		private static void AutoTrollCmd(string arg)
		{
			string text = (arg ?? "").Trim().ToLowerInvariant();
			if (text.Length == 0)
			{
				Say(AutoTroll.Summary());
				Say("usage: /autotroll off | proven | strikes | rules");
				return;
			}
			switch (text)
			{
			case "off":
				AutoTroll.SetMode(AutoTroll.Trigger.Off);
				break;
			case "proven":
				AutoTroll.SetMode(AutoTroll.Trigger.AnyProven);
				break;
			case "strikes":
				AutoTroll.SetMode(AutoTroll.Trigger.AfterStrikes);
				break;
			case "rules":
				AutoTroll.SetMode(AutoTroll.Trigger.ChosenRules);
				break;
			default:
				Say("usage: /autotroll off | proven | strikes | rules");
				return;
			}
			Say(AutoTroll.Summary());
		}

		private static void Mute(string arg)
		{
			MuteLines.Reason reason = MuteLines.Reason.General;
			string text = arg ?? "";
			int num = text.LastIndexOf(' ');
			if (num > 0)
			{
				string text2 = text.Substring(num + 1);
				if (Enum.TryParse<MuteLines.Reason>(text2, ignoreCase: true, out var result))
				{
					reason = result;
					text = text.Substring(0, num);
				}
				else if (text2.Equals("politics", StringComparison.OrdinalIgnoreCase))
				{
					reason = MuteLines.Reason.Political;
					text = text.Substring(0, num);
				}
			}
			Player val = Find(text);
			if (!((Object)(object)val == (Object)null))
			{
				Say(Actions.ToggleMute(val, reason));
			}
		}

		private static void Island(string arg)
		{
			string text = (arg ?? "").Trim().ToLowerInvariant();
			if (text.Length != 0)
			{
				switch (text)
				{
				case "status":
					break;
				case "next":
					Say(HostTools.Next(backwards: false));
					return;
				case "prev":
				case "back":
					Say(HostTools.Next(backwards: true));
					return;
				default:
				{
					if (!int.TryParse(text, out var result))
					{
						Say("usage: /island <number> | next | prev");
					}
					else
					{
						Say(HostTools.GoTo(result - 1));
					}
					return;
				}
				}
			}
			Say(HostTools.Where());
			Say("usage: /island <number> | next | prev");
		}

		private static void TraceCmd(string arg)
		{
			switch (arg.ToLowerInvariant())
			{
			case "on":
				Plugin.TraceUnguarded.Value = true;
				Say("Trace on - unguarded RPCs will be logged.");
				break;
			case "off":
				Plugin.TraceUnguarded.Value = false;
				Plugin.TraceEverything.Value = false;
				Say("Trace off.");
				break;
			case "all":
				Plugin.TraceUnguarded.Value = true;
				Plugin.TraceEverything.Value = true;
				Say("Logging EVERY client RPC. Reproduce the thing once, then /trace off.");
				Say("Needs a restart to tap the guarded ones as well.");
				break;
			default:
				Say(Trace.Summary());
				break;
			}
		}

		private static void Anchor()
		{
			Plugin.Anchor.Value = !Plugin.Anchor.Value;
			Say("Anchor " + (Plugin.Anchor.Value ? "ON - teleports aimed at you will be refused." : "OFF - teleports are allowed but still logged."));
		}

		private static void HostOnly(Func<string> f)
		{
			if (!Plugin.IsHosting)
			{
				Say("Only the host can do that.");
			}
			else
			{
				Say(f());
			}
		}

		private static void WithPlayer(string arg, Func<Player, string> f)
		{
			Player val = Find(arg);
			if (!((Object)(object)val == (Object)null))
			{
				Say(f(val));
			}
		}

		private static Player Find(string query)
		{
			if (string.IsNullOrWhiteSpace(query))
			{
				Say("Who? Try /net status for names.");
				return null;
			}
			List<Player> list = new List<Player>();
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if ((Object)(object)player != (Object)null && (Object)(object)player != (Object)(object)Player.LocalPlayer)
					{
						list.Add(player);
					}
				}
			}
			catch
			{
			}
			if (list.Count == 0)
			{
				Say("Nobody else is here.");
				return null;
			}
			string q = Clean(query);
			List<Player> list2 = list.Where((Player p) => Clean(p.SteamName) == q).ToList();
			if (list2.Count == 1)
			{
				return list2[0];
			}
			if (list2.Count > 1)
			{
				Ambiguous(list2);
				return null;
			}
			List<Player> list3 = list.Where(delegate(Player p)
			{
				string text = Clean(p.SteamName);
				return text.Length > 0 && (text.Contains(q) || q.Contains(text));
			}).ToList();
			if (list3.Count == 1)
			{
				Say("(matched \"" + query + "\" to " + list3[0].SteamName + ")");
				return list3[0];
			}
			if (list3.Count > 1)
			{
				Ambiguous(list3);
				return null;
			}
			Say("No player matching \"" + query + "\".");
			return null;
		}

		private static void Ambiguous(List<Player> hits)
		{
			Say("That matches more than one player - be more specific: " + string.Join(", ", hits.Select((Player p) => p.SteamName)));
		}

		private static string Clean(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			s = Regex.Replace(s, "<.*?>", "");
			return Regex.Replace(s, "[^a-zA-Z0-9]", "").ToLowerInvariant();
		}

		private static void Say(string msg)
		{
			Chat.Info(msg);
		}
	}
	internal static class Crash
	{
		private static Traverse _hats;

		private static Traverse _outfits;

		private static Traverse _accessories;

		private static bool _resolved;

		private static void Resolve()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			try
			{
				Traverse obj = Traverse.Create(typeof(SkinManager));
				_hats = obj.Field("_allHats");
				_outfits = obj.Field("_allOutfits");
				_accessories = obj.Field("_allAccessories");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Crash] could not read the skin lists: " + ex.Message));
			}
		}

		private static int CountOf(Traverse list)
		{
			try
			{
				if (list == null || !list.FieldExists())
				{
					return -1;
				}
				return (list.GetValue() as IList)?.Count ?? (-1);
			}
			catch
			{
				return -1;
			}
		}

		public static bool SpawnPlayer_Pre(byte __10, byte __11, byte __12, NetworkConnection __14)
		{
			if (!Plugin.IsHosting || !TackleCheck.Passed)
			{
				return true;
			}
			if (!Plugin.BlockCrashPackets.Value)
			{
				return true;
			}
			if (Sender.IsTrusted(__14))
			{
				return true;
			}
			int num = BodiesFor(__14);
			if (num >= 1)
			{
				Plugin.Log.LogError((object)("[Fishwarden] BLOCKED CLONE from " + Sender.NameOf(__14) + " " + $"(already has {num} body/bodies)"));
				Guard.Report(__14, "SpawnPlayer", Verdict.Certain("clone-spawn", $"tried to spawn another copy of themselves - they already have {num}"));
				return false;
			}
			Resolve();
			List<string> list = new List<string>();
			Check("hat", __10, CountOf(_hats), list);
			Check("outfit", __11, CountOf(_outfits), list);
			Check("accessory", __12, CountOf(_accessories), list);
			if (list.Count == 0)
			{
				return true;
			}
			string text = Sender.NameOf(__14);
			string text2 = "sent a spawn that would crash everyone else in the lobby (" + string.Join(", ", list) + ")";
			Plugin.Log.LogError((object)("[Fishwarden] BLOCKED LOBBY CRASH from " + text + ": " + text2));
			Guard.Report(__14, "SpawnPlayer", Verdict.Certain("lobby-crash", text2));
			return false;
		}

		private static int BodiesFor(NetworkConnection conn)
		{
			if (conn == (NetworkConnection)null)
			{
				return 0;
			}
			int num = 0;
			try
			{
				foreach (Player player in PlayerManager.Players)
				{
					if (!((Object)(object)player == (Object)null) && !((NetworkBehaviour)player).IsDeinitializing && ((NetworkBehaviour)player).Owner == conn)
					{
						num++;
					}
				}
				return num;
			}
			catch
			{
				return 0;
			}
		}

		private static void Check(string what, byte value, int count, List<string> bad)
		{
			if (count >= 0 && value >= count)
			{
				bad.Add($"{what} index {value} with only {count} available");
			}
		}
	}
	internal static class Dashboard
	{
		private sealed class Button
		{
			public string Label;

			public Func<string> Do;

			public bool Hot;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static WindowFunction <0>__Body;
		}

		private static Rect _window = new Rect(0f, 0f, 1420f, 800f);

		private static bool _placed;

		private static float _pulse;

		private static Vector2 _playerScroll;

		private static Vector2 _caseScroll;

		private static Vector2 _wardenScroll;

		private static ulong _selected;

		private static string _toast;

		private static float _toastUntil;

		private static CursorLockMode _prevLock;

		private static bool _prevVisible;

		private static GUISkin _skin;

		private static Texture2D _panelTex;

		private static Texture2D _rowTex;

		private static bool _fontApplied;

		private static Vector2 _dossierScroll;

		private static bool _showBanned;

		private static Vector2 _bannedScroll;

		private static Vector2 _summaryScroll;

		public static bool IsOpen { get; private set; }

		public static bool UsingNative { get; private set; }

		public static void Toggle()
		{
			if (IsOpen)
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		public static void Open()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!IsOpen)
			{
				IsOpen = true;
				UsingNative = Plugin.NativeConsole.Value && NativeConsole.Open();
				if (Plugin.NativeConsole.Value && !UsingNative)
				{
					Plugin.Log.LogInfo((object)("[Console] native UI unavailable (" + NativeUI.Problem + "), using the drawn one."));
				}
				_prevLock = Cursor.lockState;
				_prevVisible = Cursor.visible;
				if (!_placed)
				{
					((Rect)(ref _window)).width = Mathf.Min(((Rect)(ref _window)).width, (float)(Screen.width - 40));
					((Rect)(ref _window)).height = Mathf.Min(((Rect)(ref _window)).height, (float)(Screen.height - 60));
					((Rect)(ref _window)).x = ((float)Screen.width - ((Rect)(ref _window)).width) / 2f;
					((Rect)(ref _window)).y = ((float)Screen.height - ((Rect)(ref _window)).height) / 2f;
					_placed = true;
				}
			}
		}

		public static void Close()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen)
			{
				IsOpen = false;
				NativeConsole.Close();
				UsingNative = false;
				Cursor.lockState = _prevLock;
				Cursor.visible = _prevVisible;
			}
		}

		public static void Tick()
		{
			NativeLook.SampleMenu();
			if (IsOpen)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				if (UsingNative)
				{
					NativeConsole.Tick();
				}
			}
		}

		public static void BlockInputs_Post(Player __instance, ref bool __result)
		{
			if (!IsOpen | __result)
			{
				return;
			}
			try
			{
				if ((Object)(object)__instance == (Object)(object)Player.LocalPlayer)
				{
					__result = true;
				}
			}
			catch
			{
			}
		}

		private static Texture2D Solid(Color c)
		{
			//IL_0002: 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_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1);
			val.SetPixel(0, 0, c);
			val.Apply();
			((Object)val).hideFlags = (HideFlags)61;
			return val;
		}

		private static void BuildSkin()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Expected O, but got Unknown
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_skin != (Object)null && (_fontApplied || (Object)(object)NativeLook.Font == (Object)null))
			{
				return;
			}
			if ((Object)(object)_skin != (Object)null && (Object)(object)NativeLook.Font != (Object)null)
			{
				_skin.font = NativeLook.Font;
				_fontApplied = true;
				return;
			}
			_panelTex = Theme.Solid(Theme.Panel);
			_rowTex = Theme.Solid(Theme.Card);
			_skin = Object.Instantiate<GUISkin>(GUI.skin);
			((Object)_skin).hideFlags = (HideFlags)61;
			Font font = NativeLook.Font;
			if ((Object)(object)font != (Object)null)
			{
				_skin.font = font;
			}
			_skin.window.normal.background = _panelTex;
			_skin.window.onNormal.background = _panelTex;
			_skin.window.border = new RectOffset(8, 8, 24, 8);
			_skin.window.padding = new RectOffset(16, 16, 32, 14);
			_skin.window.normal.textColor = NativeLook.MenuText;
			_skin.window.fontSize = NativeLook.MenuSize + 2;
			_skin.window.fontStyle = (FontStyle)1;
			_skin.label.normal.textColor = NativeLook.MenuText;
			_skin.label.fontSize = NativeLook.MenuSize;
			_skin.label.fontStyle = (FontStyle)(NativeLook.MenuBold ? 1 : 0);
			_skin.label.wordWrap = false;
			GUIStyle[] array = (GUIStyle[])(object)new GUIStyle[2] { _skin.button, _skin.box };
			foreach (GUIStyle obj in array)
			{
				obj.fontSize = NativeLook.MenuSize;
				obj.fontStyle = (FontStyle)(NativeLook.MenuBold ? 1 : 0);
				obj.padding = new RectOffset(10, 10, 6, 6);
				obj.normal.textColor = Color.white;
				obj.hover.textColor = Color.white;
				obj.active.textColor = Color.white;
			}
			_skin.box.normal.background = _rowTex;
			_skin.scrollView.normal.background = _rowTex;
		}

		public static void Draw()
		{
			//IL_001a: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			if (IsOpen && !UsingNative)
			{
				BuildSkin();
				GUISkin skin = GUI.skin;
				Color color = GUI.color;
				GUI.skin = _skin;
				GUI.color = Color.white;
				GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)Solid(new Color(0f, 0f, 0f, 0.45f)));
				_pulse = Mathf.PingPong(Time.realtimeSinceStartup * 1.6f, 1f);
				Rect window = _window;
				object obj = <>O.<0>__Body;
				if (obj == null)
				{
					WindowFunction val = Body;
					<>O.<0>__Body = val;
					obj = (object)val;
				}
				_window = GUI.Window(7957, window, (WindowFunction)obj, "