Decompiled source of Enders Ultimate Fish Warden Admin Mod v2.0.0
BepInEx/plugins/Fishwarden/Fishwarden.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
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, "