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 _muted = new Dictionary(); private static readonly Dictionary _lastHeard = new Dictionary(); private static readonly Dictionary _frozen = new Dictionary(); 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 list = new List(); 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> dictionary = new Dictionary>(); 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(); dictionary[((NetworkBehaviour)player).Owner] = value; } value.Add(player); } } } catch { return "Could not read the lobby."; } int num = 0; foreach (KeyValuePair> item in dictionary.Where((KeyValuePair> 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 list = new List(); 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()); 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 _shooters = new Dictionary(); 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 _armedBy = new HashSet(); public static int AutoArmedCount => _armedBy.Count; public static IEnumerable 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(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(rule, StringComparer.OrdinalIgnoreCase); } return false; } public static string ToggleRule(string rule) { if (string.IsNullOrWhiteSpace(rule)) { return "No rule named."; } List 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 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 _banned = new Dictionary(); private static string File_ => Path.Combine(Logbook.Folder, "trophy-wall.txt"); public static int Count => _banned.Count; public static IEnumerable 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 list = new List { "# 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 Rules = new List(); public bool Proven; public string Shot; } private static readonly List _open = new List(); private static readonly Dictionary _dismissedRules = new Dictionary(); private static int _nextId = 1; public static int PendingCount => _open.Count; public static IReadOnlyList 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 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 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 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 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 list = new List { $"{_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 | /dismiss | /accept all | /dismiss all"); return string.Join("\n", list); } public static IEnumerable NoisyRules() { return from kv in _dismissedRules where kv.Value >= Plugin.NoisyRuleThreshold.Value && Rules.Enabled(kv.Key) select kv.Key; } public static string Noisy() { List> list = _dismissedRules.Where((KeyValuePair kv) => kv.Value >= Plugin.NoisyRuleThreshold.Value).ToList(); if (list.Count == 0) { return null; } return "Noisy rules: " + string.Join(", ", list.Select((KeyValuePair 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 _forSale = new HashSet(); 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 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(); if (array == null || array.Length == 0) { if (_forSale.Count == 0) { _usable = false; } return; } HashSet hashSet = new HashSet(); SortedSet sortedSet = new SortedSet(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("[" + tag + "] " + text); } catch { } } } } public static void Broadcast(string msg) { Plugin.Log.LogInfo((object)("[Fishwarden] " + Strip(msg))); string text = "[WARDEN] " + 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 _mine = new List(); 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 _names; private static readonly Dictionary _keyOf = new Dictionary(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 __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(); 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(); _sprint = obj.Field("_sprintSpeed").GetValue(); 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(); 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(); _baseRadius = ((val2 != null && val2.FieldExists()) ? val2.GetValue() : 0f); _baseNoPos = ((val3 != null && val3.FieldExists()) ? val3.GetValue() : 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 Names(string filter) { if (_names == null) { _names = new List(); 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 list = new List(); 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(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(); 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 attached = new List(); List missing = new List(); 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 _tracks = new Dictionary(); private static readonly Dictionary _suspects = new Dictionary(); private static float _nextSample; public static IReadOnlyDictionary 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 list = new List { "Fish Finder - what I saw from here:" }; foreach (KeyValuePair 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 "); Say(" /crew /uncrew /pardon /gut "); Say(" /chum /skunk /cooler /locker "); Say(" /blowback /butterfingers /troll "); Say(" /mute [swearing|racism|antisemitism|politics|shouting]"); Say(" /freeze /strip /purge /lockdown"); Say(" /cases /accept /dismiss /autokick on|off /rule off "); Say(" /summary - what everyone has been trying"); Say(" /selftest - check the guard is behaving /stress [n]"); Say(" /island |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 | /rule on "); return; } int num = arg.IndexOf(' '); if (num < 0) { Say("usage: /rule off | /rule on "); 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 | /rule on "); } } 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 dictionary = Logbook.Tally(); if (dictionary.Count == 0) { Say(" no findings this session - clean water."); return; } foreach (KeyValuePair item2 in dictionary.OrderByDescending((KeyValuePair 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 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(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 | next | prev"); } else { Say(HostTools.GoTo(result - 1)); } return; } } } Say(HostTools.Where()); Say("usage: /island | 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 f) { if (!Plugin.IsHosting) { Say("Only the host can do that."); } else { Say(f()); } } private static void WithPlayer(string arg, Func 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 list = new List(); 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 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 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 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 list = new List(); 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 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 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(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, "Fish Warden".ToUpperInvariant() + " — F4 closes · look and movement are locked"); GUI.skin = skin; GUI.color = color; } } private static void Body(int id) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_016d: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(4f); Banner(); ModeBar(); PresetBar(); NoisyBar(); Divider(); if (!Plugin.IsHosting) { GUILayout.Label("You are a guest in someone else's lobby. Nothing here can be enforced from your machine.", Array.Empty()); GUILayout.Label(ClientWatch.Readout(), Array.Empty()); GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f)); return; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(880f) }); Theme.Line("WARDEN", Theme.Accent, 15); GUILayout.Space(2f); _wardenScroll = GUILayout.BeginScrollView(_wardenScroll, Array.Empty()); if (Cases.PendingCount > 0) { CaseList(); Divider(); } GUILayout.BeginHorizontal(Array.Empty()); PlayerList(); GUILayout.Space(10f); ActionColumn(); GUILayout.EndHorizontal(); Divider(); if (_showBanned) { BannedPanel(); } else { SummaryPanel(); } Divider(); ToolBar(); Divider(); GuardSettings.Draw(); GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(14f); VerticalRule(); GUILayout.Space(14f); GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(460f) }); Theme.Line("ENDER'S TWEAKS", Theme.Accent, 15); GUILayout.Space(4f); Tweaks.Draw(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); if (_toast != null && Time.realtimeSinceStartup < _toastUntil) { GUILayout.Space(4f); Color color = GUI.color; GUI.color = new Color(0.6f, 0.9f, 0.7f); GUILayout.Label(_toast, Array.Empty()); GUI.color = color; } GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f)); } private static void Banner() { //IL_006a: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) if (Plugin.IsHosting) { Dictionary dictionary = Logbook.Tally(); int count = dictionary.Count; int num = dictionary.Keys.Count((ulong k) => Summary.ProvenCount(k) > 0); GUILayout.BeginHorizontal(Array.Empty()); Theme.Stat(Cases.PendingCount.ToString(), "waiting on you", (Cases.PendingCount > 0) ? Color.Lerp(Theme.Urgent, Theme.Bad, _pulse) : Theme.Muted); Theme.Stat(count.ToString(), "flagged", (count > 0) ? Theme.Warn : Theme.Good); Theme.Stat(num.ToString(), "proven", (num > 0) ? Theme.Bad : Theme.Good); Theme.Stat(Troll.Count.ToString(), "being trolled", (Troll.Count > 0) ? Theme.Accent : Theme.Muted); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(Array.Empty()); GUILayout.Space(6f); string text = Actions.Status(); if (text.Length > 0) { Theme.Pill(text, Theme.Bad); } string text2 = Extras.Status(); if (text2.Length > 0) { Theme.Pill(text2, Theme.Warn); } string text3 = Safety.Status(); if (text3 != null) { Theme.Pill(text3, Safety.Tripped ? Theme.Bad : Theme.Warn); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); Theme.Rule(); } } private static void VerticalRule() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0045: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(1f, 1f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(1f), GUILayout.ExpandHeight(true) }); Color color = GUI.color; GUI.color = Theme.Divide; GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = color; } private static void Divider() { //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) //IL_002d: 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_0056: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(6f); Rect rect = GUILayoutUtility.GetRect(1f, 1f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, 0.18f); GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = color; GUILayout.Space(6f); } private static void ModeBar() { //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Level:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) }); LevelButton(Level.Watch); LevelButton(Level.Block); LevelButton(Level.Strict); LevelButton(Level.Lockdown); GUILayout.Space(16f); bool value = Plugin.AutoKick.Value; if (GUILayout.Button(value ? "Auto-remove: ON" : "Auto-remove: ask me first", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(210f) })) { Plugin.AutoKick.Value = !value; Toast(Plugin.AutoKick.Value ? "Auto-remove on - people are removed the moment they cross the line." : "Auto-remove off - you will be asked first."); } GUILayout.Space(8f); if (GUILayout.Button(Plugin.Quiet.Value ? "Quiet: ON" : "Quiet: off", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { Plugin.Quiet.Value = !Plugin.Quiet.Value; Toast(Plugin.Quiet.Value ? "Quiet on - cheats just fail, with nothing telling them why." : "Quiet off - proven findings are announced in chat."); } GUILayout.Space(8f); AutoTroll.Trigger mode = AutoTroll.Mode; Color backgroundColor = GUI.backgroundColor; if (mode != AutoTroll.Trigger.Off) { GUI.backgroundColor = new Color(0.35f, 0.65f, 0.85f); } if (GUILayout.Button("Auto-troll: " + ((mode == AutoTroll.Trigger.Off) ? "off" : mode.ToString()), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) })) { AutoTroll.SetMode((mode != AutoTroll.Trigger.ChosenRules) ? (mode + 1) : AutoTroll.Trigger.Off); Toast(AutoTroll.Summary()); } GUI.backgroundColor = backgroundColor; GUILayout.FlexibleSpace(); if (!Catalogue.Usable) { GUILayout.Label("shop list unavailable", Array.Empty()); } string text = Safety.Status(); if (text != null) { GUILayout.Label(text, Array.Empty()); } else { GUILayout.Label(TackleCheck.Passed ? $"{Plugin.Armed} guards on" : "NOT WORKING", Array.Empty()); } GUILayout.EndHorizontal(); } private static void LevelButton(Level l) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) bool num = Plugin.CurrentLevel == l; Color backgroundColor = GUI.backgroundColor; if (num) { GUI.backgroundColor = new Color(0.35f, 0.75f, 0.95f); } string text = Policy.Name(l); if (GUILayout.Button(num ? ("[" + text + "]") : text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) })) { Plugin.SetLevel(l); Toast(Policy.Explain(l)); } GUI.backgroundColor = backgroundColor; } private static void PresetBar() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Set up for:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) }); Presets.Preset[] all = Presets.All; foreach (Presets.Preset preset in all) { if (GUILayout.Button(preset.Name, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { Toast(Presets.Apply(preset)); } } GUILayout.EndHorizontal(); Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, 0.45f); GUILayout.Label(" " + Policy.Grid(Plugin.CurrentLevel) + ((Rules.DisabledCount > 0) ? (" switched off: " + string.Join(", ", Rules.Disabled)) : ""), Array.Empty()); GUI.color = color; } private static void NoisyBar() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0080: Unknown result type (might be due to invalid IL or missing references) List list = Cases.NoisyRules().ToList(); if (list.Count == 0 && Rules.DisabledCount == 0) { return; } GUILayout.BeginHorizontal(Array.Empty()); foreach (string item in list) { Color color = GUI.color; GUI.color = new Color(1f, 0.8f, 0.4f); GUILayout.Label("'" + item + "' keeps misfiring", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); GUI.color = color; if (GUILayout.Button("Stop checking it", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { Toast(Rules.Disable(item) ? ("'" + item + "' switched off.") : ("'" + item + "' was already off.")); } } foreach (string item2 in Rules.Disabled.ToList()) { if (GUILayout.Button("Re-enable '" + item2 + "'", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { Toast(Rules.Enable(item2) ? ("'" + item2 + "' switched back on.") : ""); } } GUILayout.EndHorizontal(); } private static void CaseList() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_006a: 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) Color color = GUI.color; GUI.color = new Color(1f, 0.82f, 0.35f); GUILayout.Label($"WAITING ON YOU ({Cases.PendingCount})", Array.Empty()); GUI.color = color; _caseScroll = GUILayout.BeginScrollView(_caseScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height((float)Mathf.Min(140, 34 + Cases.PendingCount * 46)) }); foreach (Cases.Case item in Cases.Open.ToList()) { GUILayout.BeginHorizontal(GUIStyle.op_Implicit("box"), Array.Empty()); GUILayout.BeginVertical(Array.Empty()); GUILayout.Label(string.Format("{0} {1} strikes {2}", item.Name, item.Strikes, item.Proven ? "PROVEN" : "judgement call"), Array.Empty()); GUILayout.Label(" " + string.Join(", ", item.Rules), Array.Empty()); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Remove them", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(38f) })) { Toast(Cases.Accept(item.Id)); } if (GUILayout.Button("They're fine", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(38f) })) { Toast(Cases.Dismiss(item.Id)); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private static void PlayerList() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(440f) }); GUILayout.Label("PLAYERS — click one to act on them", Array.Empty()); _playerScroll = GUILayout.BeginScrollView(_playerScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(240f) }); List list = Others(); if (list.Count == 0) { GUILayout.Label(" nobody else in the lobby", Array.Empty()); } Dictionary dictionary = new Dictionary(); foreach (Player item in list) { try { dictionary[item.SteamID] = ((!dictionary.TryGetValue(item.SteamID, out var value)) ? 1 : (value + 1)); } catch { } } HashSet hashSet = new HashSet(); foreach (Player item2 in list) { ulong steamID; try { steamID = item2.SteamID; } catch { continue; } if (hashSet.Add(steamID)) { int value2; int num = (Logbook.Tally().TryGetValue(steamID, out value2) ? value2 : 0); string text = Tags(steamID, num); bool flag = _selected == steamID; Color backgroundColor = GUI.backgroundColor; if (flag) { GUI.backgroundColor = new Color(0.35f, 0.7f, 0.9f); } else if (num >= Plugin.KickThreshold.Value) { GUI.backgroundColor = new Color(0.9f, 0.45f, 0.4f); } else if (num > 0) { GUI.backgroundColor = new Color(0.9f, 0.75f, 0.4f); } int value3; int num2 = ((!dictionary.TryGetValue(steamID, out value3)) ? 1 : value3); string text2 = ((num2 > 1) ? $"{item2.SteamName} x{num2}" : item2.SteamName); if (GUILayout.Button(string.Format("{0}{1,-24} {2,-12} {3}", flag ? "> " : " ", text2, (num > 0) ? (num + " strikes") : "clean", text), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) })) { _selected = (flag ? 0 : steamID); } GUI.backgroundColor = backgroundColor; } } GUILayout.EndScrollView(); GUILayout.EndVertical(); } private static void ActionColumn() { //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(420f) }); Player p = Selected(); if ((Object)(object)p == (Object)null) { GUILayout.Label("Nobody selected.", Array.Empty()); GUILayout.Space(4f); GUILayout.Label("Click a player on the left to see", Array.Empty()); GUILayout.Label("everything about them, and what you", Array.Empty()); GUILayout.Label("can do about it.", Array.Empty()); GUILayout.EndVertical(); return; } Dossier(p); GUILayout.Space(8f); Group("STOP THEM RIGHT NOW"); Row(Btn(Actions.IsFrozen(_selected) ? "Unfreeze" : "Freeze", () => Actions.ToggleFreeze(p)), Btn(Actions.IsMuted(_selected) ? "Unmute" : "Mute", () => Actions.ToggleMute(p)), Actions.IsMuted(_selected) ? Btn("Why: " + MuteLines.Name(Actions.ReasonFor(_selected)), () => Actions.CycleReason(p)) : null, Btn("Strip", () => Actions.Strip(p)), Btn("De-clone", () => Actions.Purge(p))); Group("MAKE IT NOT WORTH IT"); Row(BtnHot(Troll.Is(_selected) ? "STOP TROLL" : "TROLL MODE", () => Troll.Toggle(p)), Btn(Extras.HasBlowback(_selected) ? "No blowback" : "Blow up", () => Extras.Blowback(p)), Btn(Extras.IsButterfingers(_selected) ? "Steady hands" : "Butterfingers", () => Extras.Butterfingers(p))); Row(Btn(Extras.IsSkunked(_selected) ? "Un-skunk" : "Skunk", () => Extras.Skunk(p)), Btn(Extras.InCooler(_selected) ? "Out of cooler" : "Cooler", () => Extras.Cool(p)), Btn("Out to sea", () => Extras.Locker(p))); Group("DECIDE"); Row(BtnHot("REMOVE", delegate { int value; int strikes = (Logbook.Tally().TryGetValue(_selected, out value) ? value : 0); Guard.RemoveNow(((NetworkBehaviour)p).Owner, strikes, "removed by hand"); string steamName = p.SteamName; _selected = 0uL; return steamName + " removed."; }), Rollback.HasDamage(_selected) ? Btn("Undo their damage", () => Rollback.Gut(_selected)) : null, Btn("Trust", delegate { Crew.Add(_selected); return p.SteamName + " is trusted."; }), Btn("Pardon", delegate { Logbook.Pardon(_selected); Rollback.Clear(_selected); Safety.Forget(_selected); Extras.Release(_selected); return p.SteamName + " pardoned."; })); if (!Rollback.HasDamage(_selected)) { Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, 0.35f); GUILayout.Label("Nothing of theirs is in the world to undo.", Array.Empty()); GUI.color = color; } GUILayout.EndVertical(); } private static void Dossier(Player p) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) ulong num; try { num = p.SteamID; } catch { num = 0uL; } Color color = GUI.color; GUI.color = new Color(0.55f, 0.8f, 0.95f); GUILayout.Label(p.SteamName, Array.Empty()); GUI.color = new Color(1f, 1f, 1f, 0.4f); GUILayout.Label("steam " + num, Array.Empty()); List> list = TagList(num); if (list.Count > 0) { GUILayout.BeginHorizontal(Array.Empty()); foreach (KeyValuePair item in list) { Theme.Pill(item.Key, item.Value); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } int value; int num2 = (Logbook.Tally().TryGetValue(num, out value) ? value : 0); int num3 = Summary.AttemptCount(num); int num4 = Summary.ProvenCount(num); GUI.color = Theme.ForStrikes(num2, Plugin.KickThreshold.Value); GUILayout.Label((num2 == 0) ? "No strikes - nothing against them" : $"{num2} strikes · {num3} attempt(s) · {num4} proven", Array.Empty()); GUI.color = color; List list2 = Summary.LinesFor(num); List list3 = Logbook.ShotsFor(num); int num5 = Flight.CorrectionsFor(p); if (list2.Count == 0 && list3.Count == 0 && num5 == 0) { GUI.color = new Color(0.55f, 0.87f, 0.66f); GUILayout.Label("Clean. Nothing recorded against them.", Array.Empty()); GUI.color = color; return; } _dossierScroll = GUILayout.BeginScrollView(_dossierScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(130f) }); if (list2.Count > 0) { GUI.color = new Color(0.55f, 0.8f, 0.95f); GUILayout.Label("WHAT THEY HAVE TRIED", Array.Empty()); foreach (string item2 in list2) { bool flag = item2.TrimStart().StartsWith("!"); GUI.color = (item2.Contains("NONE STOPPED") ? new Color(1f, 0.5f, 0.5f) : (flag ? new Color(0.97f, 0.45f, 0.38f) : new Color(0.93f, 0.72f, 0.35f))); GUILayout.Label(item2, Array.Empty()); } } if (num5 > 0) { GUI.color = new Color(0.93f, 0.72f, 0.35f); GUILayout.Label($"Put back on the ground {num5} time(s)", Array.Empty()); } if (list3.Count > 0) { GUI.color = new Color(0.55f, 0.8f, 0.95f); GUILayout.Label($"EVIDENCE ({list3.Count} screenshot(s))", Array.Empty()); GUI.color = new Color(1f, 1f, 1f, 0.45f); foreach (string item3 in list3.Take(3)) { GUILayout.Label(" " + item3, Array.Empty()); } if (list3.Count > 3) { GUILayout.Label($" ...and {list3.Count - 3} more", Array.Empty()); } } GUI.color = color; GUILayout.EndScrollView(); } private static Button Btn(string label, Func act) { return new Button { Label = label, Do = act }; } private static Button BtnHot(string label, Func act) { return new Button { Label = label, Do = act, Hot = true }; } private static void Group(string title) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(6f); Color color = GUI.color; GUI.color = new Color(0.55f, 0.8f, 0.95f); GUILayout.Label(title, Array.Empty()); GUI.color = color; } private static void Row(params Button[] buttons) { //IL_008d: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); foreach (Button button in buttons) { if (button == null) { continue; } Color backgroundColor = GUI.backgroundColor; if (button.Hot) { GUI.backgroundColor = new Color(0.85f, 0.42f, 0.36f); } if (GUILayout.Button(button.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { try { Toast(button.Do()); } catch (Exception ex) { Toast("Failed: " + ex.Message); } } GUI.backgroundColor = backgroundColor; } GUILayout.EndHorizontal(); } private static void BannedPanel() { //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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = new Color(0.55f, 0.8f, 0.95f); GUILayout.Label($"BANNED ({Bans.Count}) - these people cannot rejoin", Array.Empty()); GUI.color = color; if (Bans.Count == 0) { GUI.color = new Color(0.55f, 0.87f, 0.66f); GUILayout.Label("Nobody is banned.", Array.Empty()); GUI.color = color; return; } _bannedScroll = GUILayout.BeginScrollView(_bannedScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) }); foreach (Bans.Record item in Bans.All.OrderByDescending((Bans.Record x) => x.When).ToList()) { GUILayout.BeginHorizontal(GUIStyle.op_Implicit("box"), Array.Empty()); GUILayout.BeginVertical(Array.Empty()); GUILayout.Label(item.Name, Array.Empty()); GUI.color = new Color(1f, 1f, 1f, 0.45f); GUILayout.Label(" " + item.Reason, Array.Empty()); GUILayout.Label($" banned {item.When:yyyy-MM-dd HH:mm} · steam {item.SteamId}", Array.Empty()); GUI.color = color; GUILayout.EndVertical(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Let them back in", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(46f) })) { string name = item.Name; if (Bans.Remove(item.SteamId)) { Logbook.Pardon(item.SteamId); Safety.Forget(item.SteamId); Extras.Release(item.SteamId); Toast(name + " can rejoin - ban lifted and strikes cleared."); } } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private static void SummaryPanel() { //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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = new Color(0.55f, 0.8f, 0.95f); GUILayout.Label("WHAT PEOPLE ARE TRYING", Array.Empty()); GUI.color = color; List list = Summary.Lines(); _summaryScroll = GUILayout.BeginScrollView(_summaryScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) }); foreach (string item in list) { bool flag = item.TrimStart().StartsWith("!"); GUI.color = (Color)((!item.StartsWith(" ") && !item.StartsWith("(")) ? Color.white : (flag ? new Color(0.97f, 0.45f, 0.38f) : (item.StartsWith("(") ? new Color(1f, 1f, 1f, 0.4f) : new Color(0.93f, 0.72f, 0.35f)))); GUILayout.Label(item, Array.Empty()); } GUI.color = color; GUILayout.EndScrollView(); } private static void ToolBar() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); Color backgroundColor = GUI.backgroundColor; if (Actions.Lockdown) { GUI.backgroundColor = new Color(0.9f, 0.45f, 0.4f); } if (GUILayout.Button(Actions.Lockdown ? "LOCKED" : "Lock lobby", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(110f) })) { Toast(Actions.ToggleLockdown()); } GUI.backgroundColor = backgroundColor; if (GUILayout.Button("Clear clones", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(110f) })) { Toast(Actions.PurgeAll()); } Color backgroundColor2 = GUI.backgroundColor; GUI.backgroundColor = new Color(0.85f, 0.42f, 0.36f); if (GUILayout.Button("Undo the session", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(150f) })) { Toast(Rollback.RestoreWorld()); } GUI.backgroundColor = backgroundColor2; if (GUILayout.Button(Extras.ChumOut ? "Remove the trap" : "Set flight trap", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(120f) })) { if (Extras.ChumOut) { Extras.ClearChum(); Toast("Bait cleared."); } else { Toast(Extras.DropChum()); } } GUILayout.Space(12f); if (GUILayout.Button("< Island", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(80f) })) { Toast(HostTools.Next(backwards: true)); } if (GUILayout.Button("Island >", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(80f) })) { Toast(HostTools.Next(backwards: false)); } if (GUILayout.Button(HostTools.Flying ? "Land" : "Fly", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(70f) })) { Toast(HostTools.ToggleFly()); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); Color backgroundColor3 = GUI.backgroundColor; if (_showBanned) { GUI.backgroundColor = new Color(0.35f, 0.75f, 0.95f); } if (GUILayout.Button(_showBanned ? $"Hide banned ({Bans.Count})" : $"Banned ({Bans.Count})", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(26f), GUILayout.Width(130f) })) { _showBanned = !_showBanned; } GUI.backgroundColor = backgroundColor3; if (GUILayout.Button("Save report", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(26f), GUILayout.Width(110f) })) { Toast(SaveReport()); } if (GUILayout.Button("Summary to chat", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(26f), GUILayout.Width(140f) })) { foreach (string item in Summary.Lines()) { Chat.Info(item); } Toast("Summary posted."); } if (GUILayout.Button("Test the guard", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(26f), GUILayout.Width(130f) })) { Toast(string.Join(" | ", SelfTest.Run())); } GUILayout.EndHorizontal(); } private static List Others() { List list = new List(); try { foreach (Player player in PlayerManager.Players) { if ((Object)(object)player != (Object)null && (Object)(object)player != (Object)(object)Player.LocalPlayer) { list.Add(player); } } } catch { } return list; } private static Player Selected() { if (_selected == 0L) { return null; } foreach (Player item in Others()) { try { if (item.SteamID == _selected) { return item; } } catch { } } return null; } private static List> TagList(ulong id) { //IL_0036: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) List> t = new List>(); if (Cases.Open.Any((Cases.Case c) => c.Key == id)) { Add("NEEDS YOU", Theme.Urgent); } if (Troll.Is(id)) { Add(AutoTroll.WasAutomatic(id) ? "TROLLED (auto)" : "TROLLED", Theme.Accent); } if (Actions.IsFrozen(id)) { Add("frozen", Theme.Accent); } if (Actions.IsMuted(id)) { Add("muted", Theme.Muted); } if (Extras.HasBlowback(id)) { Add("rigged", Theme.Warn); } if (Extras.IsButterfingers(id)) { Add("butterfingers", Theme.Warn); } if (Extras.IsSkunked(id)) { Add("skunked", Theme.Warn); } if (Extras.InCooler(id)) { Add("cooler", Theme.Warn); } if (Bans.IsBanned(id)) { Add("BANNED", Theme.Bad); } if (Crew.Has(id)) { Add("trusted", Theme.Good); } return t; void Add(string k, Color c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) t.Add(new KeyValuePair(k, c)); } } private static string Tags(ulong id, int strikes) { List list = new List(); if (Crew.Has(id)) { list.Add("trusted"); } if (Troll.Is(id)) { list.Add("TROLLED"); } if (Actions.IsFrozen(id)) { list.Add("frozen"); } if (Actions.IsMuted(id)) { list.Add("muted"); } if (Extras.HasBlowback(id)) { list.Add("rigged to blow"); } if (Extras.IsButterfingers(id)) { list.Add("butterfingers"); } if (Extras.IsSkunked(id)) { list.Add("skunked"); } if (Extras.InCooler(id)) { list.Add("in the cooler"); } if (Bans.IsBanned(id)) { list.Add("banned"); } if (Cases.Open.Any((Cases.Case c) => c.Key == id)) { list.Add("WAITING ON YOU"); } return string.Join(", ", list); } private static string SaveReport() { 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()); return "Report saved to " + text; } catch (Exception ex) { return "Could not save: " + ex.Message; } } internal static void Toast(string msg) { _toast = msg; _toastUntil = Time.realtimeSinceStartup + 6f; Plugin.Log.LogInfo((object)("[Dashboard] " + msg)); } } internal static class Evidence { private static float _lastShot = -999f; private static int _sessionCount; public static int SessionCount => _sessionCount; public static string Folder => Path.Combine(Logbook.Folder, "evidence"); public static bool Ready { get { if (Plugin.CaptureEvidence.Value && _sessionCount < Plugin.EvidenceSessionCap.Value) { return Time.realtimeSinceStartup - _lastShot >= Plugin.EvidenceCooldown.Value; } return false; } } public static void Capture(Entry entry) { if (!Ready || entry == null) { return; } _lastShot = Time.realtimeSinceStartup; _sessionCount++; string fileName = FileName(entry); try { ((MonoBehaviour)Plugin.Instance).StartCoroutine(Grab(fileName, entry)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Evidence] could not start capture: " + ex.Message)); } } private static IEnumerator Grab(string fileName, Entry entry) { yield return (object)new WaitForEndOfFrame(); Texture2D val = null; try { val = ScreenCapture.CaptureScreenshotAsTexture(); byte[] array = ImageConversion.EncodeToPNG(val); Directory.CreateDirectory(Folder); File.WriteAllBytes(Path.Combine(Folder, fileName), array); Logbook.AttachShot(entry, fileName); Plugin.Log.LogInfo((object)$"[Evidence] captured {fileName} ({array.Length / 1024} KB)"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Evidence] capture failed: " + ex.Message)); } finally { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } private static string FileName(Entry e) { string text = e.When.ToString("yyyy-MM-dd_HHmmss", CultureInfo.InvariantCulture); return text + "_" + Safe(e.Who) + "_" + Safe(e.Verdict.Rule) + ".png"; } private static string Safe(string s) { if (string.IsNullOrEmpty(s)) { return "unknown"; } s = Regex.Replace(s, "<.*?>", ""); s = Regex.Replace(s, "[^A-Za-z0-9_-]", ""); if (s.Length == 0) { return "unknown"; } if (s.Length <= 24) { return s; } return s.Substring(0, 24); } } internal static class Extras { private static Item _chum; private static Vector3 _chumAt; private static readonly HashSet _skunked = new HashSet(); private static float _nextSkunkTick; private static readonly HashSet _alreadyZeroed = new HashSet(); private static readonly Dictionary _cooler = new Dictionary(); private static readonly HashSet _blowback = new HashSet(); private static readonly Dictionary _butter = new Dictionary(); public static bool ChumOut => (Object)(object)_chum != (Object)null; public static int SkunkedCount => _skunked.Count; public static int CoolerCount => _cooler.Count; public static int BlowbackCount => _blowback.Count; public static string DropChum() { //IL_0079: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsHosting) { return "Only the host can chum the water."; } ClearChum(); try { Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null) { return "No local player."; } Item spawnable = GameInfo.GetSpawnable(Plugin.ChumItem.Value.Replace(" ", "").ToLowerInvariant()); if ((Object)(object)spawnable == (Object)null) { return "No item called '" + Plugin.ChumItem.Value + "'."; } _chumAt = Body.Of(localPlayer) + Vector3.up * Plugin.ChumHeight.Value; Item val = Object.Instantiate(spawnable, _chumAt, Quaternion.identity); ((NetworkBehaviour)Server.Instance).Spawn(((Component)val).gameObject, (NetworkConnection)null, default(Scene)); _chum = val; Plugin.Log.LogInfo((object)$"[Chum] dropped {((Object)spawnable).name} at {_chumAt} ({Plugin.ChumHeight.Value}m up)"); return $"Chum in the water: {((Object)spawnable).name}, {Plugin.ChumHeight.Value}m up. Anyone who reaches it flew."; } catch (Exception ex) { Plugin.Log.LogError((object)("[Chum] " + ex)); return "Could not drop chum: " + ex.Message; } } public static void ClearChum() { if (!((Object)(object)_chum == (Object)null)) { try { _chum.DestroyItem((byte)4, byte.MaxValue); } catch { } _chum = null; } } public static void TickChum() { if ((Object)(object)_chum == (Object)null || !Plugin.IsHosting) { return; } Player val = null; try { val = _chum.Holder; } catch { _chum = null; return; } if (!((Object)(object)val == (Object)null)) { if ((Object)(object)val == (Object)(object)Player.LocalPlayer) { ClearChum(); return; } NetworkConnection owner = ((NetworkBehaviour)val).Owner; _chum = null; Guard.Report(owner, "Chum", Verdict.Certain("chum", $"collected chum placed {Plugin.ChumHeight.Value}m in the air - unreachable without flight")); } } public static bool IsSkunked(ulong id) { return _skunked.Contains(id); } public static string Skunk(Player p) { if ((Object)(object)p == (Object)null) { return "Nobody by that name."; } _skunked.Add(p.SteamID); return p.SteamName + " is skunked - their catches are worth nothing."; } public static string Unskunk(Player p) { if ((Object)(object)p == (Object)null) { return "Nobody by that name."; } _skunked.Remove(p.SteamID); return p.SteamName + " can earn again."; } public static void TickSkunk() { if (_skunked.Count == 0 || !Plugin.IsHosting) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextSkunkTick) { return; } _nextSkunkTick = realtimeSinceStartup + 1f; if (_alreadyZeroed.Count > 256) { _alreadyZeroed.Clear(); } try { foreach (Player player in PlayerManager.Players) { if ((Object)(object)player == (Object)null || (Object)(object)player == (Object)(object)Player.LocalPlayer || !_skunked.Contains(player.SteamID)) { continue; } Item val = (((Object)(object)player.Holding != (Object)null) ? player.Holding.HeldItem : null); if (!((Object)(object)val == (Object)null) && !_alreadyZeroed.Contains(val)) { _alreadyZeroed.Add(val); try { Server.Instance.SetItemMultiplier(val, 0f); } catch { } } } } catch { } } public static bool InCooler(ulong id) { return _cooler.ContainsKey(id); } public static string Cool(Player p) { if ((Object)(object)p == (Object)null) { return "Nobody by that name."; } _cooler[p.SteamID] = 0f; return p.SteamName + " is in the cooler until you let them out."; } public static string Uncool(Player p) { if ((Object)(object)p == (Object)null) { return "Nobody by that name."; } _cooler.Remove(p.SteamID); return p.SteamName + " is out of the cooler."; } public static void TickCooler() { //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) if (_cooler.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) && _cooler.TryGetValue(player.SteamID, out var value) && !(realtimeSinceStartup < value)) { _cooler[player.SteamID] = realtimeSinceStartup + Plugin.CoolerInterval.Value; try { Server.Instance.HitPlayer(player, 99999, Vector3.zero, Vector3.zero, (byte)0, (Player)null); } catch { } } } } catch { } } public static bool HasBlowback(ulong id) { return _blowback.Contains(id); } public static string Blowback(Player p) { if ((Object)(object)p == (Object)null) { return "Nobody by that name."; } if (_blowback.Add(p.SteamID)) { return p.SteamName + " will now blow up every time they attack."; } _blowback.Remove(p.SteamID); return p.SteamName + " can attack safely again."; } public static bool TryDetonate(Player p) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0061: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)p == (Object)null || !Plugin.IsHosting) { return false; } ulong steamID; try { steamID = p.SteamID; } catch { return false; } if (!_blowback.Contains(steamID)) { return false; } try { Vector3 val = Body.Of(p); int value = Plugin.BlowbackDamage.Value; Server.Instance.HitPlayer(p, value, Vector3.up * Plugin.BlowbackLaunch.Value, val, (byte)2, (Player)null); Plugin.Log.LogInfo((object)("[Blowback] " + p.SteamName + " detonated on their own attack.")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Blowback] failed: " + ex.Message)); return false; } } public static bool IsButterfingers(ulong id) { return _butter.ContainsKey(id); } public static string Butterfingers(Player p) { if ((Object)(object)p == (Object)null) { return "Nobody by that name."; } if (_butter.ContainsKey(p.SteamID)) { _butter.Remove(p.SteamID); return p.SteamName + " can hold things again."; } _butter[p.SteamID] = 0f; return p.SteamName + " keeps dropping everything they pick up."; } public static void TickButterfingers() { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if (_butter.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 || !_butter.TryGetValue(player.SteamID, out var value) || realtimeSinceStartup < value) { continue; } _butter[player.SteamID] = realtimeSinceStartup + Plugin.ButterfingersInterval.Value; if (!((Object)(object)(((Object)(object)player.Holding != (Object)null) ? player.Holding.HeldItem : null) == (Object)null)) { try { Server.Instance.DropAllItems(player, Body.Of(player), Quaternion.identity); } catch { } } } } catch { } } public static string Locker(Player p) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //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) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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."; } try { Player localPlayer = Player.LocalPlayer; Vector3 val = (((Object)(object)localPlayer != (Object)null) ? Body.Of(localPlayer) : Vector3.zero) + new Vector3(Plugin.LockerDistance.Value, 0f, Plugin.LockerDistance.Value); Server.Instance.TeleportPlayer(p, val, 0f); return p.SteamName + " has been sent to Davy Jones' locker. It is a long swim back."; } catch (Exception ex) { return "Could not: " + ex.Message; } } public static void ApplyTrollEffects(ulong id) { _blowback.Add(id); _butter[id] = 0f; _skunked.Add(id); } public static void Release(ulong id) { _skunked.Remove(id); _cooler.Remove(id); _blowback.Remove(id); _butter.Remove(id); Actions.Release(id); } public static string Status() { List list = new List(); if (ChumOut) { list.Add("chum out"); } if (_skunked.Count > 0) { list.Add($"{_skunked.Count} skunked"); } if (_cooler.Count > 0) { list.Add($"{_cooler.Count} in the cooler"); } if (_blowback.Count > 0) { list.Add($"{_blowback.Count} rigged to blow"); } if (_butter.Count > 0) { list.Add($"{_butter.Count} butterfingered"); } if (Troll.Count > 0) { list.Add($"{Troll.Count} in troll mode"); } string text = Actions.Status(); if (text.Length > 0) { list.Add(text); } if (list.Count != 0) { return string.Join(", ", list); } return ""; } } internal static class Firing { private sealed class Gun { public float LastShot = -999f; public int SinceReload; public int LastAmmo = -1; public int WeaponId; public int FastShots; public float LastFinding = -999f; } private static readonly Dictionary _guns = new Dictionary(); private static Gun For(NetworkConnection conn) { ulong key = Sender.KeyOf(conn); if (!_guns.TryGetValue(key, out var value)) { value = new Gun(); _guns[key] = value; } return value; } public static Verdict Shot(NetworkConnection conn, Player shooter) { if (!Plugin.WatchFiring.Value) { return Verdict.Clean; } Gun gun = For(conn); float realtimeSinceStartup = Time.realtimeSinceStartup; Weapon val = HeldWeapon(shooter); int num = (((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0); if (num != gun.WeaponId) { gun.WeaponId = num; gun.SinceReload = 0; gun.LastAmmo = -1; } int num2 = AmmoOf(val); if (num2 >= 0) { if (gun.LastAmmo >= 0 && num2 > gun.LastAmmo) { gun.SinceReload = 0; } gun.LastAmmo = num2; } gun.SinceReload++; int num3 = MagSize(val); if (num3 > 0 && (float)gun.SinceReload > (float)num3 * Plugin.MagTolerance.Value) { if (Ready(gun, realtimeSinceStartup)) { gun.LastFinding = realtimeSinceStartup; int sinceReload = gun.SinceReload; gun.SinceReload = 0; return Verdict.Certain("no-reload", $"fired {sinceReload} shots without reloading - the magazine holds {num3}" + ((num2 >= 0) ? $", and the weapon reports {num2} left" : "")); } return Verdict.Clean; } float num4 = realtimeSinceStartup - gun.LastShot; gun.LastShot = realtimeSinceStartup; float num5 = MinInterval(val); if (num5 <= 0f || num4 > num5) { gun.FastShots = 0; return Verdict.Clean; } gun.FastShots++; if (gun.FastShots < Plugin.FastShotsForProof.Value) { return Verdict.Clean; } if (!Ready(gun, realtimeSinceStartup)) { return Verdict.Clean; } gun.LastFinding = realtimeSinceStartup; int fastShots = gun.FastShots; gun.FastShots = 0; return Verdict.Certain("auto-fire", $"{fastShots} shots faster than the weapon can cycle ({num4 * 1000f:0}ms apart, needs {num5 * 1000f:0}ms)"); } public static void Reloaded(NetworkConnection conn) { Gun gun = For(conn); gun.SinceReload = 0; gun.FastShots = 0; } private static Weapon HeldWeapon(Player p) { try { Item val = (((Object)(object)p != (Object)null && (Object)(object)p.Holding != (Object)null) ? p.Holding.HeldItem : null); return ((Object)(object)val != (Object)null) ? val.Weapon : null; } catch { return null; } } private static int AmmoOf(Weapon w) { if ((Object)(object)w == (Object)null) { return -1; } try { Traverse val = Traverse.Create((object)w).Property("Ammo", (object[])null); if (val != null && val.PropertyExists()) { return val.GetValue(); } Traverse val2 = Traverse.Create((object)w).Field("_ammo"); if (val2 != null && val2.FieldExists()) { return val2.GetValue(); } } catch { } return -1; } private static int MagSize(Weapon w) { if ((Object)(object)w == (Object)null) { return -1; } try { object value = Traverse.Create((object)w).Field("_attachments").GetValue(); Attachments val = (Attachments)((value is Attachments) ? value : null); if ((Object)(object)val == (Object)null) { return -1; } int ammoPerMag = val.AmmoPerMag; return (ammoPerMag > 0) ? ammoPerMag : (-1); } catch { return -1; } } private static float MinInterval(Weapon w) { if ((Object)(object)w == (Object)null) { return -1f; } try { Traverse val = Traverse.Create((object)w).Field("_timeBetweenShots"); if (val == null || !val.FieldExists()) { return -1f; } float value = val.GetValue(); if (value <= 0f) { return -1f; } return value * Mathf.Clamp01(Plugin.FireRateTolerance.Value); } catch { return -1f; } } private static bool Ready(Gun g, float now) { return now - g.LastFinding >= Plugin.MovementFindingCooldown.Value; } public static void Reset() { _guns.Clear(); } } internal static class Flight { private sealed class Track { public Vector3 LastGround; public bool HasGround; public float AirborneSince = -1f; public float OnWaterSince = -1f; public float WaterLow; public float WaterHigh; public float LastAction = -999f; public int Corrections; public Vector3 LastPos; public bool HasLast; public int Phased; public float LastPhaseFinding = -999f; } private static readonly Dictionary _tracks = new Dictionary(); private static float _nextTick; private static bool _loggedFirst; private static bool _saidWater; public static void Tick() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsHosting || !Plugin.WatchMovement.Value) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextTick) { return; } _nextTick = realtimeSinceStartup + Plugin.MovementSampleInterval.Value; try { foreach (Player player in PlayerManager.Players) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player == (Object)(object)Player.LocalPlayer)) { if (!_loggedFirst) { _loggedFirst = true; Plugin.Log.LogInfo((object)"[Flight] watching movement directly from transforms."); } Sample(player, Body.Of(player)); } } } catch { } } public static void Sample(Player p, Vector3 pos) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)p == (Object)null || !Plugin.IsHosting || !Plugin.WatchMovement.Value) { return; } NetworkConnection owner = ((NetworkBehaviour)p).Owner; if (Sender.IsTrusted(owner)) { return; } if (!_tracks.TryGetValue(p, out var value)) { value = new Track(); _tracks[p] = value; } float realtimeSinceStartup = Time.realtimeSinceStartup; bool onBoat; try { PlayerMovement movement = p.Movement; if ((Object)(object)movement == (Object)null) { return; } onBoat = movement.OnBoat; } catch { return; } bool num = onBoat || StandingOnSomething(pos); bool flag = Swimming(p); if (Plugin.CheckNoclip.Value && value.HasLast && !onBoat) { Vector3 val = pos - value.LastPos; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude > Plugin.NoclipMinStep.Value && CrossedSolid(value.LastPos, pos, magnitude)) { value.Phased++; if (value.Phased >= Plugin.NoclipCrossingsForFinding.Value && realtimeSinceStartup - value.LastPhaseFinding >= Plugin.MovementFindingCooldown.Value) { value.LastPhaseFinding = realtimeSinceStartup; int phased = value.Phased; value.Phased = 0; Guard.Report(owner, "Movement", Verdict.Certain("noclip", $"moved through solid geometry {phased} times")); if (Plugin.PutFlyersDown.Value && Plugin.CurrentLevel > Level.Watch) { PutDown(p, value, owner); } } } } value.LastPos = pos; value.HasLast = true; if (num) { value.LastGround = pos; value.HasGround = true; value.AirborneSince = -1f; value.OnWaterSince = -1f; return; } if (value.AirborneSince < 0f) { value.AirborneSince = realtimeSinceStartup; } float num2 = realtimeSinceStartup - value.AirborneSince; float num3 = HeightAboveWater(pos); if (num3 > Plugin.WaterWalkFloor.Value && num3 < Plugin.WaterWalkBand.Value) { if (value.OnWaterSince < 0f) { value.OnWaterSince = realtimeSinceStartup; value.WaterLow = num3; value.WaterHigh = num3; } if (num3 < value.WaterLow) { value.WaterLow = num3; } if (num3 > value.WaterHigh) { value.WaterHigh = num3; } float num4 = realtimeSinceStartup - value.OnWaterSince; float num5 = value.WaterHigh - value.WaterLow; if (num4 > Plugin.WaterWalkSeconds.Value && num5 <= Plugin.WaterWalkWobble.Value && realtimeSinceStartup - value.LastAction >= Plugin.FlightActionCooldown.Value) { value.LastAction = realtimeSinceStartup; value.Corrections++; Guard.Report(owner, "Movement", Verdict.Certain("walking-on-water", $"stood on the water for {num4:0.0}s, height steady within " + $"{num5:0.00}m - not swimming, not grounded, no boat")); if (Plugin.PutFlyersDown.Value && Plugin.CurrentLevel > Level.Watch) { PutDown(p, value, owner); } return; } } else { value.OnWaterSince = -1f; } bool num6 = num2 > Plugin.FlightSeconds.Value && !flag; bool flag2 = num3 > Plugin.MaxJumpHeight.Value; if (!num6 && !flag2) { return; } float num7 = (flag2 ? (Plugin.FlightActionCooldown.Value * 0.25f) : Plugin.FlightActionCooldown.Value); if (!(realtimeSinceStartup - value.LastAction < num7)) { value.LastAction = realtimeSinceStartup; value.Corrections++; bool num8 = num2 > Plugin.FlightSeconds.Value * 2f || num3 > Plugin.MaxJumpHeight.Value * 2f; string detail = (flag2 ? $"{num3:0}m above the water with no boat (a jump reaches {Plugin.MaxJumpHeight.Value:0}m)" : $"airborne {num2:0.0}s with no boat"); Verdict v = (num8 ? Verdict.Certain("flying", detail) : Verdict.Strong("flight", detail)); Guard.Report(owner, "Movement", v); if (Plugin.PutFlyersDown.Value && Plugin.CurrentLevel > Level.Watch) { PutDown(p, value, owner); } if (value.Corrections == Plugin.CorrectionsBeforeProof.Value) { Guard.Report(owner, "Movement", Verdict.Certain("persistent-flight", $"put back on the ground {value.Corrections} times and still flying")); } } } private static void PutDown(Player p, Track t, NetworkConnection conn) { //IL_0011: 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_0016: 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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (t.HasGround ? t.LastGround : GroundBeneath(p)); if (val == Vector3.zero) { return; } try { Server.Instance.TeleportPlayer(p, val, 0f); Plugin.Log.LogInfo((object)("[Flight] put " + Sender.NameOf(conn) + " back on the ground " + $"(correction #{t.Corrections})")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Flight] could not put them down: " + ex.Message)); } } private static bool CrossedSolid(Vector3 from, Vector3 to, float len) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_001c: 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) //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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_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_008a: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = (to - from) / len; float value = Plugin.NoclipPadding.Value; Vector3 val2 = from + val * value; float num = len - value * 2f; if (num <= 0f) { return false; } val2 += Vector3.up * Plugin.NoclipProbeHeight.Value; int value2 = Plugin.LineOfSightMask.Value; RaycastHit val3 = default(RaycastHit); if (!Physics.Raycast(val2, val, ref val3, num, value2, (QueryTriggerInteraction)1)) { return false; } RaycastHit val4 = default(RaycastHit); if (!Physics.Raycast(val2 + val * num, -val, ref val4, num, value2, (QueryTriggerInteraction)1)) { return false; } return num - ((RaycastHit)(ref val3)).distance - ((RaycastHit)(ref val4)).distance >= Plugin.NoclipMinThickness.Value; } catch { return false; } } private static bool StandingOnSomething(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) try { return Physics.Raycast(pos + Vector3.up * 0.5f, Vector3.down, Plugin.GroundedProbe.Value, Plugin.LineOfSightMask.Value, (QueryTriggerInteraction)1); } catch { return true; } } private static Vector3 GroundBeneath(Player p) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_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_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) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) try { RaycastHit val = default(RaycastHit); if (Physics.Raycast(Body.Of(p) + Vector3.up, Vector3.down, ref val, Plugin.GroundProbeDistance.Value, Plugin.LineOfSightMask.Value, (QueryTriggerInteraction)1)) { return ((RaycastHit)(ref val)).point + Vector3.up * 0.2f; } Vector3 val2 = Body.Of(p); return new Vector3(val2.x, WaterManager.WaterHeight + 0.5f, val2.z); } catch { return Vector3.zero; } } private static bool Swimming(Player p) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { Transform val = (((Object)(object)p != (Object)null) ? p.Transform : null); if ((Object)(object)val == (Object)null) { return false; } float y = val.position.y; float waterHeight = WaterManager.WaterHeight; if (!_saidWater) { _saidWater = true; Plugin.Log.LogInfo((object)($"[Flight] waterline is y={waterHeight:0.00}; a player is " + $"standing at y={y:0.00}. Swimming means below the " + "first. If everyone reads as swimming, that is why the airtime rule went quiet.")); } return y <= waterHeight; } catch { return false; } } private static float HeightAboveWater(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return pos.y - WaterManager.WaterHeight; } catch { return 0f; } } public static int CorrectionsFor(Player p) { if (!((Object)(object)p != (Object)null) || !_tracks.TryGetValue(p, out var value)) { return 0; } return value.Corrections; } public static void Reset() { _tracks.Clear(); _loggedFirst = false; } } internal static class Guard { private sealed class Bucket { public int Count; public float WindowStart; public float LastFinding = -999f; } private static Player _pendingSpawn; private static ulong _pendingSpawnKey; private static readonly HashSet Attacks = new HashSet { "teleport-crash", "bad-numbers", "lobby-crash", "clone-spawn" }; private static readonly HashSet Symptoms = new HashSet { "crash-packet", "packet-flood" }; private static readonly Dictionary _symptoms = new Dictionary(); private static float _symptomWindow; private const int PatternNeeded = 8; private static bool _saying; private static int _mostPellets; private static readonly Dictionary> _bursts = new Dictionary>(); private static readonly Dictionary _buckets = new Dictionary(); public static void Reader_Pre(NetworkConnection __2) { Sender.Capture(__2); } public static void Reader_Post() { Sender.Clear(); } public static bool Teleport_Pre(Player __0, Vector3 __1, float __2) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } if (!Body.Sane(__1)) { Plugin.Log.LogError((object)("[Fishwarden] BLOCKED CRASH TELEPORT from " + Sender.NameOf(current) + " - coordinates are not a place")); Report(current, "TeleportPlayer", Verdict.Certain("teleport-crash", "sent a teleport to coordinates no position can hold - this crashes everyone it is applied to")); return false; } Verdict clean = Verdict.Clean; string text = (((Object)(object)__0 != (Object)null) ? __0.SteamName : "(gone)"); clean = ((!((Object)(object)__0 != (Object)null) || !(((NetworkBehaviour)__0).Owner != current)) ? Verdict.Certain("teleport-self", "teleported themselves to " + Fmt(__1) + " - no client can do this legitimately") : Verdict.Certain("teleport-other", "moved " + text + " to " + Fmt(__1) + " - sender is not that player")); return Apply(current, "TeleportPlayer", clean); } public static bool HitPlayer_Pre(Player __0, int __1, Vector3 __2, Vector3 __3, byte __4, Player __5) { NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } Verdict v = Verdict.Clean; string text = (((Object)(object)__0 != (Object)null) ? __0.SteamName : "(gone)"); bool flag = OneShotOn(); int ceiling; string what; float distance; float allowed; if ((Object)(object)__5 != (Object)null && ((NetworkBehaviour)__5).Owner != current) { v = Verdict.Certain("damage-spoof", $"claimed {__1} damage to {text} as {__5.SteamName}, who did not send it"); Rollback.NoteKill(Sender.KeyOf(current), __0); } else if (!flag && Weapons.IsImpossible(__5, __1, Weapons.PlayerDamageScale(__0), out ceiling, out what)) { v = Verdict.Certain("damage-above-weapon", $"{__1} damage to {text} using {what}, which cannot exceed {ceiling} against a player"); Rollback.NoteKill(Sender.KeyOf(current), __0); } else if (Plugin.WatchReach.Value && Reach.TooFar(__5, __0, out distance, out allowed)) { v = Verdict.Certain("hit-from-nowhere", $"hit {text} from {distance:0}m away, but their reach is {allowed:0}m"); Plugin.Log.LogWarning((object)("[Reach] " + Sender.NameOf(current) + ": " + Reach.Explain(__5, __0, current))); Rollback.NoteKill(Sender.KeyOf(current), __0); } else if (!flag && __1 > Plugin.MaxPlausibleDamage.Value) { v = Verdict.Certain("damage-impossible", $"{__1} damage to {text} (absolute ceiling {Plugin.MaxPlausibleDamage.Value})"); Rollback.NoteKill(Sender.KeyOf(current), __0); } return Apply(current, "HitPlayer", v); } public static bool BuyItem_Pre(byte __0, Player __1, Item __2, Vector3 __3, Quaternion __4, bool __5) { NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } Verdict v = Verdict.Clean; string text = ItemName(__0); Item val = Resolve(__0); if (__5) { v = Verdict.Certain("free-purchase", "spawned " + text + " with the purchase marked free"); } else if (!Catalogue.Sells(__0)) { v = Verdict.Certain("not-for-sale", "bought " + text + ", which no shop in the game sells"); } else if ((Object)(object)val != (Object)null && HasCreature(val)) { v = Verdict.Certain("bought-a-creature", "bought " + text + ", which is a creature and is not sold anywhere"); } else if ((Object)(object)val != (Object)null && ItemCost(val) <= 0) { v = Verdict.Certain("bought-unpriced", "bought " + text + ", which has no price and is not shop stock"); } else if ((Object)(object)__1 != (Object)null && ((NetworkBehaviour)__1).Owner != current) { v = Verdict.Certain("purchase-other", "bought " + text + " on behalf of " + __1.SteamName); } bool flag = Apply(current, "BuyItem", v); if (flag) { try { Plugin.Log.LogInfo((object)("[Shop] " + Sender.NameOf(current) + " bought " + text + (((Object)(object)val != (Object)null) ? $" for {ItemCost(val)}" : " (unknown item)") + (Catalogue.Usable ? "" : " - CATALOGUE BLIND, not checked against stock"))); } catch { } } if (flag && v.Confidence == Confidence.Certain) { _pendingSpawn = __1; _pendingSpawnKey = Sender.KeyOf(current); } return flag; } public static void BuyItem_Post() { Player pendingSpawn = _pendingSpawn; ulong pendingSpawnKey = _pendingSpawnKey; _pendingSpawn = null; if ((Object)(object)pendingSpawn == (Object)null) { return; } try { Item val = (((Object)(object)pendingSpawn.Holding != (Object)null) ? pendingSpawn.Holding.HeldItem : null); if ((Object)(object)val != (Object)null) { Rollback.NoteSpawn(pendingSpawnKey, val); } } catch { } } public static bool DropAllItems_Pre(Player __0, Vector3 __1, Quaternion __2) { NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } Verdict v = Verdict.Clean; if ((Object)(object)__0 != (Object)null && ((NetworkBehaviour)__0).Owner != current) { v = Verdict.Certain("force-drop", "forced " + __0.SteamName + " to drop everything they were carrying"); } return Apply(current, "DropAllItems", v); } public static bool FinishGame_Pre() { NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } Verdict v = Verdict.Certain("force-finish", "tried to end the run for the whole lobby"); return Apply(current, "SendFinishGame", v); } private static bool Skip(NetworkConnection conn) { if (!TackleCheck.Passed) { return true; } if (!Plugin.IsHosting) { return true; } return Sender.IsTrusted(conn); } private static bool Apply(NetworkConnection conn, string rpc, Verdict v) { if (!v.IsHostile) { return true; } if (!Rules.Enabled(v.Rule)) { return true; } Action action = Policy.For(Plugin.CurrentLevel, v.Confidence); bool flag = Safety.HoldFire && v.Confidence != Confidence.Certain; if (flag) { action = Action.Log; } bool flag2 = action != Action.Log; Safety.Saw(Sender.KeyOf(conn), v.Confidence, v.Rule); ulong num = Sender.KeyOf(conn); string text = Sender.NameOf(conn); Rollback.NoteOffence(num); Summary.Note(num, text, v, flag2); AutoTroll.Consider(conn, num, text, v); Troll.Reacted(num, text, v.Rule); if (!Throttle.ShouldReport(num, text, v)) { return !flag2; } Entry entry = Logbook.Record(conn, rpc, v, flag2); if (v.Confidence == Confidence.Certain) { Evidence.Capture(entry); if (!flag && Plugin.AnnounceFindings.Value && !Plugin.Quiet.Value) { Announce("[Fishwarden] " + text + " - " + v.Detail); } } if (flag2 || action == Action.Remove) { Response.Answer(conn, v.Rule); } if (Attack(v)) { Repel(conn, v); return !flag2; } if (action == Action.Remove) { ConsiderRemoval(conn); } return !flag2; } public static void Report(NetworkConnection conn, string source, Verdict v) { if (v.IsHostile && Plugin.IsHosting && TackleCheck.Passed && !Sender.IsTrusted(conn)) { Apply(conn, source, v); } } private static void Repel(NetworkConnection conn, Verdict v) { string text = Sender.NameOf(conn); Plugin.Log.LogError((object)("[Fishwarden] " + text + " tried to crash the lobby (" + v.Rule + "). Removing regardless of level - this is not a cheat, it is an attack on everyone here.")); Announce("[Fishwarden] " + text + " tried to crash the lobby and has been removed."); try { if (Plugin.EnforceBans.Value) { Player val = Sender.PlayerFor(conn); if ((Object)(object)val != (Object)null) { Bans.Add(val.SteamID, text, "tried to crash the lobby (" + v.Rule + ")"); } } } catch { } try { RemoveNow(conn, Logbook.StrikesFor(conn), "tried to crash the lobby"); } catch (Exception ex) { Plugin.Log.LogError((object)("[Fishwarden] could not remove them: " + ex.Message)); } } private static bool Attack(Verdict v) { if (v.Rule == null) { return false; } if (Attacks.Contains(v.Rule)) { return true; } if (!Symptoms.Contains(v.Rule)) { return false; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup >= _symptomWindow) { _symptomWindow = realtimeSinceStartup + 60f; _symptoms.Clear(); } ulong key = Sender.KeyOf(Sender.Current); _symptoms.TryGetValue(key, out var value); value++; _symptoms[key] = value; if (value < 8) { return false; } Plugin.Log.LogWarning((object)($"[Fishwarden] {value} of '{v.Rule}' from one player inside a " + "minute - that is a pattern rather than bad luck.")); return true; } private static void ConsiderRemoval(NetworkConnection conn) { int num = Logbook.StrikesFor(conn); int value = Plugin.KickThreshold.Value; if (num >= value) { if (!Safety.MayRemove(Sender.KeyOf(conn))) { Plugin.Log.LogInfo((object)($"[Fishwarden] {Sender.NameOf(conn)} is at {num} strikes but " + "nothing is proven - not removing. Heuristics never kick on their own.")); } else if (!Plugin.AutoKick.Value) { Cases.OpenCase(conn, num); } else { RemoveNow(conn, num, "threshold"); } } } public static void RemoveNow(NetworkConnection conn, int strikes, string why) { string text = Sender.NameOf(conn); string text2 = $"Fishwarden: {strikes} strikes ({why})"; string value = Plugin.AppealTo.Value; if (!string.IsNullOrWhiteSpace(value)) { text2 = text2 + " - wrongly kicked? " + value; } try { Announce($"[Fishwarden] {text} removed - {strikes} strikes. Gone fishing."); if (Plugin.EnforceBans.Value) { Player val = Sender.PlayerFor(conn); if ((Object)(object)val != (Object)null) { try { Bans.Add(val.SteamID, text, text2); } catch { } } } if (Plugin.GutOnRemoval.Value) { string text3 = Rollback.Gut(Sender.KeyOf(conn)); Plugin.Log.LogInfo((object)("[Fishwarden] " + text3)); Announce("[Fishwarden] " + text3); } conn.Kick((KickReason)0, (LoggingType)3, text2); Plugin.Log.LogWarning((object)$"[Fishwarden] kicked {text} ({strikes} strikes)"); } catch (Exception ex) { Plugin.Log.LogError((object)("[Fishwarden] kick failed: " + ex.Message)); } } public static void Announce(string msg) { if (_saying) { return; } try { _saying = true; Chat.Broadcast(msg); } finally { _saying = false; } } internal static bool OneShotOn() { try { return ServerSettings.OneShotEnabled; } catch { return false; } } private static Item Resolve(byte id) { try { return GameInfo.IDToItem(id); } catch { return null; } } private static bool HasCreature(Item item) { try { return (Object)(object)item.Creature != (Object)null; } catch { return false; } } private static int ItemCost(Item item) { try { return item.Cost; } catch { return 1; } } private static string ItemName(byte id) { try { Item val = GameInfo.IDToItem(id); if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(((Object)val).name)) { return ((Object)val).name.Replace("(Clone)", "").Trim(); } } catch { } return "item #" + id; } private static string Fmt(Vector3 v) { //IL_0005: 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_001b: Unknown result type (might be due to invalid IL or missing references) return $"({v.x:0},{v.y:0},{v.z:0})"; } private static bool Owns(NetworkConnection conn, Player p) { if ((Object)(object)p != (Object)null) { return ((NetworkBehaviour)p).Owner == conn; } return false; } private static string NameOf(Player p) { object obj; if (!((Object)(object)p == (Object)null)) { obj = p.SteamName; if (obj == null) { return "?"; } } else { obj = "(gone)"; } return (string)obj; } public static bool SetItemMultiplier_Pre(Item __0, float __1) { NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } Worths.Saw(__1); Verdict v = Verdict.Clean; string text = Worths.Abusing(Sender.KeyOf(current), __0); if (text != null) { v = Verdict.Certain("worth-inflation", text + " - the kill bonus is paid once, on the thing you killed"); } else if (__1 > Plugin.ImpossibleWorthMultiplier.Value) { v = Verdict.Certain("worth-inflation", $"set sell value to x{__1:0} on {ItemLabel(__0)} - nothing in the game pays " + $"more than x{Plugin.ImpossibleWorthMultiplier.Value:0}"); } else if (__1 > Plugin.MaxWorthMultiplier.Value) { v = Verdict.Strong("worth-inflation", $"set sell value to x{__1:0.0} on {ItemLabel(__0)} (ordinary play tops out " + $"around x{Plugin.MaxWorthMultiplier.Value:0}, {Worths.Seen()})"); } else if (__1 > Plugin.VanillaWorthCeiling.Value) { v = Verdict.Contextual("worth-nudge", $"set sell value to x{__1:0.0} on {ItemLabel(__0)}"); } if (v.Confidence == Confidence.Certain && (Object)(object)__0 != (Object)null) { Rollback.NoteInflated(Sender.KeyOf(current), __0); } return Apply(current, "SetItemMultiplier", v); } private static Verdict BuyCheck(NetworkConnection conn, Player buyer, string what) { if ((Object)(object)buyer != (Object)null && !Owns(conn, buyer)) { return Verdict.Certain("purchase-other", "bought " + what + " on behalf of " + NameOf(buyer)); } return Limit(conn, "purchase", Plugin.PurchasesPerMinute.Value, "purchases (" + what + ")"); } public static bool BuyBait_Pre(Player __0, byte __1, int __2) { return Guarded((NetworkConnection conn) => BuyCheck(conn, __0, "bait"), "BuyBait"); } public static bool BuyBoatMotor_Pre(Player __0, byte __1, int __2) { return Guarded((NetworkConnection conn) => BuyCheck(conn, __0, "a boat motor"), "BuyBoatMotor"); } public static bool BuyBoatRadar_Pre(Player __0, int __1) { return Guarded((NetworkConnection conn) => BuyCheck(conn, __0, "boat radar"), "BuyBoatRadar"); } public static bool BuyAttachment_Pre(Weapon __0, byte __1) { return Guarded((NetworkConnection conn) => BuyCheck(conn, HolderOf((Component)(object)__0), "a weapon attachment"), "BuyAttachment"); } public static bool BuyBulletUpgrade_Pre(Weapon __0) { return Guarded((NetworkConnection conn) => BuyCheck(conn, HolderOf((Component)(object)__0), "a bullet upgrade"), "BuyBulletUpgrade"); } public static bool BuySharpnessUpgrade_Pre(Melee __0) { return Guarded((NetworkConnection conn) => BuyCheck(conn, HolderOf((Component)(object)__0), "a sharpness upgrade"), "BuySharpnessUpgrade"); } public static bool SetItemHolder_Pre(Item __0, Player __1, Item __2) { return Guarded((NetworkConnection conn) => (Owns(conn, __1) || !((Object)(object)__1 != (Object)null)) ? Verdict.Clean : Verdict.Strong("item-move", "put " + ItemLabel(__0) + " into " + NameOf(__1) + "'s hands"), "SetItemHolder"); } public static bool RemoveItemFromInventory_Pre(Player __0, Item __1) { return Guarded((NetworkConnection conn) => (Owns(conn, __0) || !((Object)(object)__0 != (Object)null)) ? Verdict.Clean : Verdict.Strong("inventory-take", "took " + ItemLabel(__1) + " out of " + NameOf(__0) + "'s inventory"), "RemoveItemFromInventory"); } public static bool PutItemInInventory_Pre(Player __0, Item __1, byte __2) { return Guarded((NetworkConnection conn) => (Owns(conn, __0) || !((Object)(object)__0 != (Object)null)) ? Verdict.Clean : Verdict.Strong("inventory-push", "pushed " + ItemLabel(__1) + " into " + NameOf(__0) + "'s inventory"), "PutItemInInventory"); } public static bool TakeItemFromNpc_Pre(Player __0, byte __1) { return Guarded((NetworkConnection conn) => (Owns(conn, __0) || !((Object)(object)__0 != (Object)null)) ? Verdict.Clean : Verdict.Strong("npc-take", "took an NPC item as " + NameOf(__0)), "TakeItemFromNpc"); } public static bool UnlockPocket_Pre(Player __0, byte __1) { return Guarded((NetworkConnection conn) => (Owns(conn, __0) || !((Object)(object)__0 != (Object)null)) ? Verdict.Clean : Verdict.Strong("pocket-unlock", "unlocked a pocket as " + NameOf(__0)), "UnlockPocket"); } public static bool ResurrectPlayer_Pre(Player __0, DeadPlayer __1) { return Guarded(delegate(NetworkConnection conn) { if (!Owns(conn, __0)) { return Verdict.Clean; } int num = Burst(conn, "selfrevive", Plugin.SelfReviveWindow.Value); return (num <= 1) ? Verdict.Certain("self-revive", "resurrected themselves - the game cancels a revive if the reviver is dead") : Verdict.Certain("self-revive", $"resurrected themselves ({num} times in {Plugin.SelfReviveWindow.Value:0}s)"); }, "ResurrectPlayer"); } private static int Burst(NetworkConnection conn, string what, float window) { string key = (conn?.ClientId ?? (-1)) + ":" + what; float now = Time.realtimeSinceStartup; if (!_bursts.TryGetValue(key, out var value)) { value = new List(); _bursts[key] = value; } value.Add(now); value.RemoveAll((float t) => now - t > window); return value.Count; } public static bool ReloadWeapon_Pre(Weapon __0) { return Guarded(delegate(NetworkConnection conn) { Firing.Reloaded(conn); Player val = HolderOf((Component)(object)__0); return (!((Object)(object)val != (Object)null) || Owns(conn, val)) ? Verdict.Clean : Verdict.Strong("reload-other", "reloaded " + NameOf(val) + "'s weapon"); }, "ReloadWeapon"); } public static bool ChangeBait_Pre(Player __0, byte __1) { return Guarded(delegate(NetworkConnection conn) { if ((Object)(object)__0 != (Object)null && !Owns(conn, __0)) { return Verdict.Certain("bait-set-other", "changed " + NameOf(__0) + "'s bait"); } if (__1 == 0) { return Verdict.Clean; } return (OwnedBait(__0, __1) == 0) ? Verdict.Certain("bait-not-owned", $"equipped bait #{__1} without owning any") : Verdict.Clean; }, "ChangeBait"); } private static int OwnedBait(Player p, byte index) { if ((Object)(object)p == (Object)null || index == 0) { return -1; } try { PlayerInventory inventory = p.Inventory; if ((Object)(object)inventory == (Object)null) { return -1; } if (!(Traverse.Create((object)inventory).Field("_ownedBaits").GetValue() is IList list)) { return -1; } int num = index - 1; if (num < 0 || num >= list.Count) { return -1; } return Convert.ToInt32(list[num]); } catch { return -1; } } public static bool HandOverItemSimulation_Pre(Item __0) { return Guarded(delegate(NetworkConnection conn) { Player val = (((Object)(object)__0 != (Object)null) ? __0.Holder : null); return ((Object)(object)val != (Object)null && !Owns(conn, val)) ? Verdict.Strong("physics-grab", "took physics control of " + ItemLabel(__0) + " while " + NameOf(val) + " was holding it") : Limit(conn, "handover", Plugin.HandoversPerMinute.Value, "physics handovers"); }, "HandOverItemSimulation"); } public static bool RespawnPlayer_Pre(Player __0, Vector3 __1, Quaternion __2) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) return Guarded((NetworkConnection conn) => (Owns(conn, __0) || !((Object)(object)__0 != (Object)null)) ? Verdict.Clean : Verdict.Certain("respawn-other", $"respawned {NameOf(__0)} at ({__1.x:0},{__1.y:0},{__1.z:0}) - a teleport by another name"), "RespawnPlayer"); } public static bool HitCreature_Pre(Creature __0, Player __1, int __2, Vector3 __3, Vector3 __4) { //IL_00d0: 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) NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } Verdict clean = Verdict.Clean; int ceiling; string what; float distance; float allowed; if ((Object)(object)__1 != (Object)null && !Owns(current, __1)) { clean = Verdict.Certain("creature-damage-spoof", $"claimed {__2} damage to a creature as {NameOf(__1)}"); } else if (!OneShotOn() && Weapons.IsImpossible(__1, __2, Weapons.CreatureScale(), bareHandsBound: false, out ceiling, out what)) { clean = Verdict.Certain("creature-damage-above-weapon", $"{__2} damage to a creature using {what}, which cannot exceed {ceiling}"); } else if (Plugin.WatchReach.Value && Reach.TooFarFromPoint(__1, __3, out distance, out allowed)) { clean = Verdict.Certain("creature-hit-from-nowhere", $"hit a creature {distance:0}m away, but their reach is {allowed:0}m"); } else { Verdict verdict2; Verdict verdict = (verdict2 = Aim.NoteHit(current, __1, __3)); clean = (verdict.IsHostile ? verdict2 : ((OneShotOn() || __2 <= Plugin.MaxPlausibleDamage.Value) ? Limit(current, "hitcreature", Plugin.HitsPerMinute.Value, "creature hits") : Verdict.Certain("creature-damage-impossible", $"{__2} damage to a creature (absolute ceiling {Plugin.MaxPlausibleDamage.Value})"))); } return Apply(current, "HitCreature", clean); } public static bool MeleeAttack_Pre(Melee __0, Transform __1, bool __2, Vector3 __3) { return Guarded((NetworkConnection conn) => Extras.TryDetonate(Sender.PlayerFor(conn)) ? Verdict.Clean : Limit(conn, "melee", Plugin.HitsPerMinute.Value, "melee swings"), "MeleeAttack"); } public static bool Punch_Pre(Player __0, Transform __1, bool __2, Vector3 __3) { return Guarded((NetworkConnection conn) => Extras.TryDetonate(Sender.PlayerFor(conn)) ? Verdict.Clean : Limit(conn, "punch", Plugin.HitsPerMinute.Value, "punches"), "Punch"); } public static bool ActivateExplosive_Pre(Explosive __0, uint __1, bool __2, bool __3, Player __4) { return Guarded((NetworkConnection conn) => (!((Object)(object)__4 != (Object)null) || Owns(conn, __4)) ? Limit(conn, "explosive", Plugin.ExplosivesPerMinute.Value, "detonations") : Verdict.Certain("remote-detonate", "detonated an explosive as " + NameOf(__4)), "ActivateExplosive"); } public static bool SendChatMessage_Pre(ulong __0, string __1) { NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } if (Actions.ShouldMute(current)) { return false; } Verdict clean = Verdict.Clean; Player val = Sender.PlayerFor(current); ulong num = 0uL; try { if ((Object)(object)val != (Object)null) { num = val.SteamID; } } catch { } clean = ((num == 0L || __0 == 0L || __0 == num) ? Limit(current, "chat", Plugin.ChatPerMinute.Value, "chat messages") : Verdict.Certain("chat-impersonation", $"sent chat claiming to be steam id {__0}")); return Apply(current, "SendChatMessage", clean); } public static bool SetDriver_Pre(Player __0) { return Guarded((NetworkConnection conn) => (!((Object)(object)__0 != (Object)null) || Owns(conn, __0)) ? Verdict.Clean : Verdict.Certain("boat-hijack", "made " + NameOf(__0) + " the driver"), "SetDriver"); } public static bool SetItemSkin_Pre(Item __0, byte __1) { return Guarded((NetworkConnection conn) => Limit(conn, "skin", Plugin.CosmeticPerMinute.Value, "skin changes"), "SetItemSkin"); } public static bool SetBoatSkin_Pre(byte __0) { return Guarded((NetworkConnection conn) => Limit(conn, "boatskin", Plugin.CosmeticPerMinute.Value, "boat skin changes"), "SetBoatSkin"); } public static bool PlaceBet_Pre(byte __0) { return Guarded((NetworkConnection conn) => Limit(conn, "bet", Plugin.BetsPerMinute.Value, "bets"), "PlaceBet"); } public static bool FinishEatingCreature_Pre(Creature __0, Player __1) { return Guarded((NetworkConnection conn) => (!((Object)(object)__1 != (Object)null) || Owns(conn, __1)) ? Limit(conn, "eat", Plugin.EatsPerMinute.Value, "creatures eaten") : Verdict.Certain("eat-as-other", "finished eating a creature as " + NameOf(__1)), "FinishEatingCreature"); } public static bool ToggleEatCreature_Pre(Player __0, bool __1) { return Guarded((NetworkConnection conn) => (!((Object)(object)__0 != (Object)null) || Owns(conn, __0)) ? Verdict.Clean : Verdict.Certain("eat-toggle-other", "started " + NameOf(__0) + " eating"), "ToggleEatCreature"); } public static bool AddProjectile_Pre(Player __0, WeaponInfo __1, uint __2, uint __3, Vector3 __4, Vector3 __5) { return Guarded(delegate(NetworkConnection conn) { if ((Object)(object)__0 != (Object)null && !Owns(conn, __0)) { return Verdict.Certain("projectile-spoof", "fired a projectile as " + NameOf(__0)); } if (Extras.TryDetonate(__0)) { return Verdict.Clean; } Aim.NoteShot(conn); Verdict verdict = Firing.Shot(conn, __0); return verdict.IsHostile ? verdict : Limit(conn, "projectile", Plugin.ProjectilesPerMinute.Value, "shots"); }, "AddProjectile"); } public static bool AddProjectiles_Pre(Player __0, WeaponInfo __1, uint __2, uint __3, Vector3 __4, Vector3[] __5) { return Guarded(delegate(NetworkConnection conn) { if ((Object)(object)__0 != (Object)null && !Owns(conn, __0)) { return Verdict.Certain("projectile-spoof", "fired a volley as " + NameOf(__0)); } if (Extras.TryDetonate(__0)) { return Verdict.Clean; } int num = ((__5 != null) ? __5.Length : 0); if (num > _mostPellets) { _mostPellets = num; Plugin.Log.LogInfo((object)("[Rules] biggest honest volley so far: " + num + " projectiles (ceiling " + Plugin.MaxProjectilesPerShot.Value + "). If a real weapon is reaching the ceiling, this is the number to raise it past.")); } if (num > Plugin.MaxProjectilesPerShot.Value) { if (num < Plugin.MaxProjectilesPerShot.Value * 2) { Plugin.Log.LogWarning((object)($"[Rules] {num} projectiles in one shot against a " + $"ceiling of {Plugin.MaxProjectilesPerShot.Value} - close enough to " + "the ceiling that this is more likely a real weapon than an attack. Raise CatchLimit/MaxProjectilesPerShot in the config; the code default no longer applies once the file exists.")); return Verdict.Strong("projectile-flood", $"{num} projectiles in a single shot (ceiling " + $"{Plugin.MaxProjectilesPerShot.Value}, which may simply be too low)"); } return Verdict.Certain("projectile-flood", $"{num} projectiles in a single shot (ceiling {Plugin.MaxProjectilesPerShot.Value})"); } Aim.NoteShot(conn); Verdict verdict = Firing.Shot(conn, __0); return verdict.IsHostile ? verdict : Limit(conn, "projectile", Plugin.ProjectilesPerMinute.Value, "shots"); }, "AddProjectiles"); } public static bool ProjectileHitDynamic_Pre(NetworkConnection __0, uint __1) { return Guarded((NetworkConnection conn) => Limit(conn, "projhit", Plugin.ProjectilesPerMinute.Value, "projectile hits"), "ProjectileHitDynamic"); } public static bool ReleaseItemFromBait_Pre(FishingRod __0) { return Guarded(delegate(NetworkConnection conn) { Player val = HolderOf((Component)(object)__0); return (!((Object)(object)val != (Object)null) || Owns(conn, val)) ? Limit(conn, "bait", Plugin.BaitReleasesPerMinute.Value, "catches released") : Verdict.Certain("bait-release-other", "released a catch from " + NameOf(val) + "'s rod"); }, "ReleaseItemFromBait"); } private static bool Guarded(Func judge, string rpc) { NetworkConnection current = Sender.Current; if (Skip(current)) { return true; } return Apply(current, rpc, judge(current)); } private static Player HolderOf(Component tool) { if ((Object)(object)tool == (Object)null) { return null; } try { Item component = tool.GetComponent(); return ((Object)(object)component != (Object)null) ? component.Holder : null; } catch { return null; } } private static string ItemLabel(Item i) { if ((Object)(object)i == (Object)null) { return "an item"; } try { return (((Object)i).name ?? "an item").Replace("(Clone)", "").Trim(); } catch { return "an item"; } } private static Verdict Limit(NetworkConnection conn, string what, int perMinute, string label) { if (perMinute <= 0) { return Verdict.Clean; } string key = (conn?.ClientId ?? (-1)) + ":" + what; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!_buckets.TryGetValue(key, out var value)) { value = new Bucket { WindowStart = realtimeSinceStartup }; _buckets[key] = value; } if (realtimeSinceStartup - value.WindowStart >= 60f) { value.WindowStart = realtimeSinceStartup; value.Count = 0; } value.Count++; if (value.Count <= perMinute) { return Verdict.Clean; } if (realtimeSinceStartup - value.LastFinding < Plugin.MovementFindingCooldown.Value) { return Verdict.Clean; } value.LastFinding = realtimeSinceStartup; return Verdict.Strong("catch-limit", $"{value.Count} {label} in under a minute (ceiling {perMinute})"); } public static void ResetLimits() { _buckets.Clear(); } } internal static class GuardSettings { private static bool _open; private static Vector2 _scroll; public static void Draw() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (GUILayout.Button(_open ? "Hide warden settings" : "Warden settings", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(26f), GUILayout.Width(200f) })) { _open = !_open; } if (_open) { _scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(200f) }); Enforcement(); Theme.Rule(); TrollTuning(); Theme.Rule(); Detection(); Theme.Rule(); Records(); GUILayout.EndScrollView(); } } private static void Enforcement() { Theme.Header("ENFORCEMENT"); GUILayout.BeginHorizontal(Array.Empty()); if (Toggle(Plugin.AutoKick.Value, Plugin.AutoKick.Value ? "Removes on its own" : "Asks me first", 170)) { Plugin.AutoKick.Value = !Plugin.AutoKick.Value; } if (Toggle(Plugin.Quiet.Value, Plugin.Quiet.Value ? "Quiet" : "Announces", 110)) { Plugin.Quiet.Value = !Plugin.Quiet.Value; } if (Toggle(Plugin.EnforceBans.Value, "Bans stick", 110)) { Plugin.EnforceBans.Value = !Plugin.EnforceBans.Value; } GUILayout.EndHorizontal(); IntSlider("Strikes before removal", Plugin.KickThreshold, 10, 100); Slider("Grace period (min)", Plugin.WarmupMinutes, 0f, 15f, "0.0"); IntSlider("Stand down after N unproven", Plugin.BreakerPlayers, 0, 8); } private static void TrollTuning() { Theme.Header("TROLL"); GUILayout.BeginHorizontal(Array.Empty()); AutoTroll.Trigger mode = AutoTroll.Mode; if (GUILayout.Button("Arms: " + ((mode == AutoTroll.Trigger.Off) ? "never" : mode.ToString()), (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(190f), GUILayout.Height(26f) })) { AutoTroll.SetMode((mode != AutoTroll.Trigger.ChosenRules) ? (mode + 1) : AutoTroll.Trigger.Off); Toast(AutoTroll.Summary()); } if (Toggle(Plugin.TrollTaunts.Value, Plugin.TrollTaunts.Value ? "Talking" : "Silent", 100)) { Plugin.TrollTaunts.Value = !Plugin.TrollTaunts.Value; } GUILayout.EndHorizontal(); Slider("Shove every (s)", Plugin.TrollJostleInterval, 0.3f, 6f, "0.0"); Slider("Shove distance (m)", Plugin.TrollJostleRadius, 0.5f, 8f, "0.0"); Slider("Remark every (s)", Plugin.TrollIdleInterval, 10f, 120f, "0"); IntSlider("Arms after strikes", Plugin.AutoTrollStrikes, 10, 100); } private static void Detection() { Theme.Header("HOW SUSPICIOUS TO BE"); GUILayout.BeginHorizontal(Array.Empty()); if (Toggle(Plugin.WatchMovement.Value, "Movement", 110)) { Plugin.WatchMovement.Value = !Plugin.WatchMovement.Value; } if (Toggle(Plugin.WatchFiring.Value, "Shooting", 110)) { Plugin.WatchFiring.Value = !Plugin.WatchFiring.Value; } if (Toggle(Plugin.WatchReach.Value, "Distance", 110)) { Plugin.WatchReach.Value = !Plugin.WatchReach.Value; } if (Toggle(Plugin.WatchAim.Value, "Aim", 90)) { Plugin.WatchAim.Value = !Plugin.WatchAim.Value; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (Toggle(Plugin.PutFlyersDown.Value, "Put flyers down", 150)) { Plugin.PutFlyersDown.Value = !Plugin.PutFlyersDown.Value; } if (Toggle(Plugin.CheckNoclip.Value, "Walls", 90)) { Plugin.CheckNoclip.Value = !Plugin.CheckNoclip.Value; } if (Toggle(Plugin.BlockCrashPackets.Value, "Crash packets", 140)) { Plugin.BlockCrashPackets.Value = !Plugin.BlockCrashPackets.Value; } GUILayout.EndHorizontal(); Slider("Lag tolerance", Plugin.SlackLine, 1f, 4f, "0.0"); Slider("Damage headroom", Plugin.DamageTolerance, 1f, 4f, "0.0"); Slider("Seconds airborne = flying", Plugin.FlightSeconds, 1f, 10f, "0.0"); Slider("Height that means flying (m)", Plugin.MaxJumpHeight, 4f, 30f, "0"); } private static void Records() { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) Theme.Header("RECORD KEEPING"); GUILayout.BeginHorizontal(Array.Empty()); if (Toggle(Plugin.CaptureEvidence.Value, "Screenshots", 130)) { Plugin.CaptureEvidence.Value = !Plugin.CaptureEvidence.Value; } if (Toggle(Plugin.TraceUnguarded.Value, "Log unknown traffic", 180)) { Plugin.TraceUnguarded.Value = !Plugin.TraceUnguarded.Value; } GUILayout.EndHorizontal(); IntSlider("Screenshot limit", Plugin.EvidenceSessionCap, 5, 200); GUILayout.Space(4f); Theme.Line(Logbook.Folder, Theme.Faint, 11); } private static bool Toggle(bool on, string label, int width) { //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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = (on ? new Color(0.3f, 0.62f, 0.82f) : new Color(1f, 1f, 1f, 0.1f)); bool result = GUILayout.Button((on ? "✓ " : " ") + label, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width((float)width), GUILayout.Height(26f) }); GUI.backgroundColor = backgroundColor; return result; } private static void Slider(string label, ConfigEntry cfg, float min, float max, string fmt) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); Theme.Line(label, Theme.Muted, 12); GUILayout.FlexibleSpace(); Theme.Line(cfg.Value.ToString(fmt), Theme.Ink, 12); GUILayout.EndHorizontal(); float num = GUILayout.HorizontalSlider(cfg.Value, min, max, Array.Empty()); if (Mathf.Abs(num - cfg.Value) > 0.0001f) { cfg.Value = num; } GUILayout.Space(2f); } private static void IntSlider(string label, ConfigEntry cfg, int min, int max) { //IL_000b: 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) GUILayout.BeginHorizontal(Array.Empty()); Theme.Line(label, Theme.Muted, 12); GUILayout.FlexibleSpace(); Theme.Line(cfg.Value.ToString(), Theme.Ink, 12); GUILayout.EndHorizontal(); int num = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)cfg.Value, (float)min, (float)max, Array.Empty())); if (num != cfg.Value) { cfg.Value = num; } GUILayout.Space(2f); } private static void Toast(string msg) { Dashboard.Toast(msg); } } internal static class Hazing { public static readonly Dictionary Reactions; public static readonly string[][] Idle; public static readonly string[] Opening; public static readonly string[] Closing; public static readonly Dictionary Shame; private static readonly string[] Generic; public static string ShameFor(string rule, int roll) { if (rule == null || !Shame.TryGetValue(rule, out var value) || value.Length == 0) { value = Generic; } return value[Math.Abs(roll) % value.Length]; } static Hazing() { Dictionary dictionary = new Dictionary(); dictionary["free-purchase"] = new string[4] { "{0} tried to pull a fish out of an empty bucket. The bucket won.", "{0} reached into a tackle box that has never existed and came up with a fistful of nothing.", "Somebody tell {0} the shop is that way, and it takes money.", "{0} attempted to catch something without going near the water. Bold. Wrong, but bold." }; dictionary["not-for-sale"] = new string[3] { "{0} is shopping from a catalogue nobody else can see, and buying nothing from it.", "No stall on this island sells what {0} just tried to order.", "{0} keeps asking the sea for a receipt." }; dictionary["bought-a-creature"] = new string[4] { "{0} tried to summon a fish out of thin air. The fish declined the invitation.", "You catch fish, {0}. You do not order them.", "{0} has attempted to conjure a whale on a beginner's island. The whale is not coming.", "Every real angler here caught theirs. {0} is still trying to invent one." }; dictionary["bought-unpriced"] = new string[1] { "{0} is trying to walk off with stock that was never on the shelf." }; dictionary["teleport-self"] = new string[4] { "{0} tried to slip away across the water and is still standing exactly where they were.", "There is no shortcut to the good fishing, {0}. There never was.", "{0} attempted to vanish. The tide had other ideas.", "Landlubber trick. Didn't work." }; dictionary["teleport-other"] = new string[2] { "{0} tried to drag someone else across the map. Nobody moved an inch.", "Keep your hands off the crew, {0}." }; dictionary["respawn-other"] = new string[1] { "{0} tried to relocate a fellow angler like a piece of tackle. Denied." }; dictionary["flying"] = new string[4] { "{0} is airborne again. Somebody fetch the landing net.", "Fish swim, {0}. Anglers stand on the dock. Pick one.", "{0} has left the ground for reasons known only to {0}.", "Gravity has been notified about {0} and is on its way." }; dictionary["persistent-flight"] = new string[2] { "{0} has been hauled back down more times than a snagged line and still hasn't taken the hint.", "That's the eighth time we've scraped {0} off the sky." }; dictionary["walking-on-water"] = new string[5] { "There's only ever been one fella who walked on water, {0}, and you ain't him.", "{0} is standing on the sea. Last man who managed that got a book written about him. You're getting a warning.", "Down you come, {0}. The water's for swimming in.", "{0} out there strolling across the bay like the Almighty. The Almighty could fish, though.", "Miracles are above your pay grade, {0}." }; dictionary["noclip"] = new string[2] { "{0} just went straight through a wall. The wall's fine. {0} is a different matter.", "Doors, {0}. We have those here." }; dictionary["auto-fire"] = new string[3] { "{0}'s rod is making a noise no rod should make.", "{0} has found a trigger speed the manufacturer never tested and the sea does not respect.", "That gun was built for fish, {0}, not for whatever that was." }; dictionary["projectile-flood"] = new string[2] { "shame on me for firing a shotgun that thinks it is a swarm", "shame on me for putting a whole box of shells down one barrel" }; dictionary["no-reload"] = new string[3] { "{0} has now fired more rounds than the magazine holds. The magazine would like a word.", "{0}'s ammo box appears to be a portal. It has been sealed.", "Count your shots, {0}. Everyone else has to." }; dictionary["shot-through-wall"] = new string[2] { "{0} shot straight through solid timber. The timber is unmarked. So is the target.", "{0} is aiming at things they cannot see, and hitting none of them." }; dictionary["perfect-accuracy"] = new string[1] { "{0} hasn't missed once all evening. Nobody believes it either." }; dictionary["damage-above-weapon"] = new string[3] { "{0} swung with the force of a trawler and connected with absolutely nothing.", "That punch was written on paper, {0}. It didn't arrive.", "{0} is hitting well above their weight class, and well outside the rules." }; dictionary["hit-from-nowhere"] = new string[3] { "{0} attacked from an address they do not live at.", "{0} is throwing punches from the next island over. They are not landing.", "Come closer if you want to swing, {0}. That's how arms work." }; dictionary["damage-spoof"] = new string[1] { "{0} tried to pin their handiwork on somebody else. We saw." }; dictionary["creature-damage-above-weapon"] = new string[1] { "{0} hit a fish harder than any tackle on this island allows. The fish is unbothered." }; dictionary["bait-not-owned"] = new string[3] { "{0} is fishing with bait they never bought, and catching nothing on it.", "That's a fine lure, {0}. Shame about the part where you never paid for it.", "Stolen bait, empty bucket. Poetic, really." }; dictionary["bait-set-other"] = new string[1] { "{0} went through someone else's tackle box. Hands off." }; dictionary["worth-inflation"] = new string[2] { "{0} tried to sell a shrimp for the price of a boat. The buyer laughed.", "That's a common fish, {0}. It has always been a common fish." }; dictionary["self-revive"] = new string[2] { "{0} hauled themselves back up off the deck. Nobody clapped.", "{0} performed emergency care on their own corpse. Denied, and slightly disturbing." }; dictionary["force-drop"] = new string[1] { "{0} tried to empty somebody else's pockets into the sea." }; dictionary["physics-grab"] = new string[1] { "{0} put their hands on gear that isn't theirs." }; dictionary["item-move"] = new string[3] { "{0} has been shifting gear that belongs to somebody else.", "Keep your hands in your own tackle box, {0}.", "{0} moved another angler's catch. Bold thing to do on a small dock." }; dictionary["inventory-take"] = new string[2] { "{0} just went through someone else's pockets.", "Thieving from a fellow fisherman, {0}. There's a word for that and it isn't angler." }; dictionary["inventory-push"] = new string[1] { "{0} tried to stuff something into another angler's bag. Nobody wants it, {0}." }; dictionary["npc-take"] = new string[2] { "{0} tried to take a reward that was never offered to them.", "That quest wasn't yours, {0}." }; dictionary["pocket-unlock"] = new string[1] { "{0} attempted to pick a lock that isn't on their own bag." }; dictionary["force-drop"] = new string[1] { "{0} tried to tip somebody else's bucket into the sea." }; dictionary["boat-hijack"] = new string[3] { "{0} tried to take the wheel of a boat they aren't even standing on.", "Hands off the tiller, {0}.", "{0} attempted a mutiny from the shore. It went about as well as you'd expect." }; dictionary["bait-release-other"] = new string[2] { "{0} cut somebody else's line. That's the lowest thing you can do on a dock.", "{0} went after another angler's catch mid-fight." }; dictionary["eat-as-other"] = new string[2] { "{0} tried to eat a fish out of another man's hands.", "That wasn't your supper, {0}." }; dictionary["eat-toggle-other"] = new string[1] { "{0} tried to make somebody else start chewing. Leave them be." }; dictionary["catch-limit"] = new string[3] { "{0} is working faster than any pair of human hands ever has.", "Slow down, {0}. The fish aren't going anywhere and neither are you.", "There's a limit, {0}, and you sailed past it some time ago." }; dictionary["chum"] = new string[2] { "{0} went up and collected bait floating eighty metres in the air. Caught red-handed.", "The bait was in the sky, {0}. You went and got it. That's a confession." }; dictionary["projectile-spoof"] = new string[1] { "{0} fired a shot and tried to sign somebody else's name on it." }; dictionary["projectile-flood"] = new string[2] { "{0} loosed more shot in one pull than any barrel could hold.", "That wasn't a shotgun, {0}. That was a swarm." }; dictionary["reload-other"] = new string[1] { "{0} tried to reload a gun that isn't in their hands." }; dictionary["damage-impossible"] = new string[2] { "{0} swung with a force no tackle on this island can produce.", "Nothing you own hits that hard, {0}. Nothing anyone owns does." }; dictionary["creature-damage-impossible"] = new string[1] { "{0} hit a fish hard enough to sink a trawler. The fish declined to notice." }; dictionary["creature-damage-spoof"] = new string[1] { "{0} tried to claim somebody else's catch as their own handiwork." }; dictionary["creature-hit-from-nowhere"] = new string[2] { "{0} is landing fish from six hundred metres away. Impressive cast. Fictional, but impressive.", "The fish are over there, {0}. You are not." }; dictionary["remote-detonate"] = new string[2] { "{0} set off a charge from somewhere they aren't standing.", "Dynamite works better when you're actually near it, {0}." }; dictionary["speed"] = new string[2] { "{0} is moving quicker than any boots on this island allow.", "Nobody walks that fast, {0}. Not even downhill." }; dictionary["flight"] = new string[2] { "{0} appears to be having trouble staying on the ground.", "Something's keeping {0} off the deck and it isn't the wind." }; dictionary["chat-impersonation"] = new string[2] { "{0} just tried to speak in somebody else's voice. We all heard whose it really was.", "Nice try, {0}. Your name is still on it." }; dictionary["force-finish"] = new string[2] { "{0} tried to call the whole day off for everyone. Not your call, {0}.", "{0} attempted to end the run for eight people. Sit down." }; dictionary["purchase-other"] = new string[1] { "{0} went shopping on somebody else's tab." }; dictionary["worth-nudge"] = new string[1] { "{0} has been quietly adjusting what their catch is worth." }; dictionary["clone-spawn"] = new string[3] { "{0} has tried to bring a second {0} to the dock. One is plenty.", "There is already a {0} here and nobody was pleased about the first one.", "{0} attempted to clone themselves. The sea does not need more of that." }; dictionary["lobby-crash"] = new string[1] { "{0} just tried to sink the whole boat with everyone on it. That one isn't funny." }; Reactions = dictionary; Idle = new string[3][] { new string[4] { "{0} seems to be having a spot of bother.", "Is anyone helping {0}? They've been out there a while.", "{0} is fishing. After a fashion.", "Rough day on the water for {0}." }, new string[5] { "{0} has now dropped everything for the {1}th time. We are counting.", "Still nothing in {0}'s bucket.", "{0} continues to wrestle with forces well beyond their understanding.", "Every angler here has caught something today except {0}.", "{0} came all this way to stand in the shallows and fail." }, new string[6] { "{0} has achieved precisely nothing this entire session and shows no sign of stopping.", "Somebody ought to tell {0}. Nobody is going to tell {0}.", "{0} remains loyal to a technique that has never once worked for anybody.", "At this point {0} is simply providing entertainment for the rest of the dock.", "The fish have started ignoring {0} out of pity.", "{0}: all the gear, none of the idea." } }; Opening = new string[3] { "{0} has been marked. The sea has opinions about {0}.", "Eyes on {0}. This should be good.", "{0} is on the warden's list. Enjoy the show." }; Closing = new string[2] { "{0} has been let off. Behave.", "The sea is done with {0}. For now." }; Shame = new Dictionary { ["worth-inflation"] = new string[3] { "shame on me for pricing a common fish like a wedding ring", "shame on me for inventing money in front of everyone", "shame on me for selling the same fish twice and hoping nobody counted" }, ["free-purchase"] = new string[2] { "shame on me for walking out without paying", "shame on me for shopping with an empty wallet and a full basket" }, ["bought-a-creature"] = new string[2] { "shame on me for trying to buy a fish I could not catch", "shame on me for ordering a whale like it was a sandwich" }, ["not-for-sale"] = new string[1] { "shame on me for shopping from a catalogue nobody else can see" }, ["catch-limit"] = new string[1] { "shame on me for reeling faster than the sea can refill" }, ["teleport-self"] = new string[2] { "shame on me for skipping the walk everyone else took", "shame on me for trying to be somewhere I did not earn" }, ["teleport-other"] = new string[1] { "shame on me for moving somebody who did not ask to be moved" }, ["flying"] = new string[1] { "shame on me for fishing from the sky like a seagull with a licence" }, ["walking-on-water"] = new string[1] { "shame on me for standing on the sea as if I had been invited" }, ["noclip"] = new string[1] { "shame on me for walking through a wall that was doing its best" }, ["chat-impersonation"] = new string[2] { "shame on me for putting words in somebody else's mouth", "shame on me for speaking as a person who was standing right there" }, ["eat-as-other"] = new string[1] { "shame on me for eating out of somebody else's hands" }, ["inventory-take"] = new string[1] { "shame on me for going through pockets that were not mine" }, ["respawn-other"] = new string[1] { "shame on me for deciding when somebody else gets up" }, ["self-revive"] = new string[1] { "shame on me for refusing to stay down like everybody else" }, ["no-reload"] = new string[2] { "shame on me for a magazine that never ends", "shame on me for firing a gun that forgot how many it holds" }, ["auto-fire"] = new string[1] { "shame on me for pulling a trigger faster than a finger can" }, ["shot-through-wall"] = new string[1] { "shame on me for shooting through something solid and calling it aim" }, ["remote-detonate"] = new string[1] { "shame on me for setting off something I was nowhere near" }, ["clone-spawn"] = new string[1] { "shame on me for filling the lobby with copies of myself" } }; Generic = new string[3] { "shame on me for cheating at a fishing game", "shame on me for taking a shortcut in front of witnesses", "shame on me for being caught doing something the sea never offered" }; } } internal static class HostTools { private static Traverse _rig; private static object _rigOwner; private static bool _hadGravity = true; public static bool Flying { get; private set; } public static bool FriendlyFire { get { try { return ServerSettings.UseFriendlyFire; } catch { return false; } } } public static string GoTo(int index) { if (!Plugin.IsHosting) { return "Only the host can move the lobby."; } int num; try { num = IslandManager.TotalIslands; } catch { num = 6; } if (index < 0 || index >= num) { return $"Islands are 1 to {num} (that is index 0 to {num - 1})."; } try { OnlineIslandManager.TpToSpecificIsland((byte)index); return $"Everyone moved to island {index + 1} of {num}."; } catch (Exception ex) { return "Could not travel: " + ex.Message; } } public static string Next(bool backwards) { int curIsland; int totalIslands; try { curIsland = OnlineIslandManager.CurIsland; totalIslands = IslandManager.TotalIslands; } catch { return "Island state unavailable."; } int num = (backwards ? (curIsland - 1) : (curIsland + 1)); if (num < 0) { num = totalIslands - 1; } if (num >= totalIslands) { num = 0; } return GoTo(num); } public static string Where() { try { byte curIsland = OnlineIslandManager.CurIsland; int totalIslands = IslandManager.TotalIslands; byte maxIslandUnlocked = OnlineIslandManager.MaxIslandUnlocked; return $"Island {curIsland + 1} of {totalIslands} (unlocked up to {maxIslandUnlocked})."; } catch { return "Island state unavailable."; } } public static string ToggleFly() { Flying = !Flying; if (!Flying) { RestoreGravity(); } if (!Flying) { return "Fly off."; } return "Fly on. WASD to move, Space up, Ctrl down, Shift to go faster."; } public static void Move_Post(PlayerMovement __instance) { //IL_00cc: 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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: 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_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) if (!Flying) { return; } try { Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer.Movement != (Object)(object)__instance) { return; } if (_rigOwner != __instance) { _rigOwner = __instance; _rig = Traverse.Create((object)__instance).Field("_rig"); _hadGravity = true; } Rigidbody val = ((_rig != null && _rig.FieldExists()) ? _rig.GetValue() : null); if (!((Object)(object)val == (Object)null)) { if (val.useGravity) { _hadGravity = true; val.useGravity = false; } Transform val2 = (((Object)(object)localPlayer.CurCam != (Object)null) ? ((Component)localPlayer.CurCam).transform : null); Vector3 val3 = (((Object)(object)val2 != (Object)null) ? val2.forward : ((Component)localPlayer).transform.forward); Vector3 val4 = (((Object)(object)val2 != (Object)null) ? val2.right : ((Component)localPlayer).transform.right); Vector3 val5 = Vector3.zero; if (Input.GetKey((KeyCode)119)) { val5 += val3; } if (Input.GetKey((KeyCode)115)) { val5 -= val3; } if (Input.GetKey((KeyCode)100)) { val5 += val4; } if (Input.GetKey((KeyCode)97)) { val5 -= val4; } if (Input.GetKey((KeyCode)32)) { val5 += Vector3.up; } if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)99)) { val5 += Vector3.down; } float num = Plugin.FlySpeed.Value; if (Input.GetKey((KeyCode)304)) { num *= Plugin.FlyBoost.Value; } val.linearVelocity = ((val5 == Vector3.zero) ? Vector3.zero : (((Vector3)(ref val5)).normalized * num)); } } catch { } } private static void RestoreGravity() { try { Rigidbody val = ((_rig != null && _rig.FieldExists()) ? _rig.GetValue() : null); if ((Object)(object)val != (Object)null && _hadGravity) { val.useGravity = true; } } catch { } } public static void Install(Harmony h) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(typeof(PlayerMovement), "Move", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)"[HostTools] PlayerMovement.Move not found - fly unavailable."); } else { h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(HostTools), "Move_Post", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { Plugin.Log.LogError((object)("[HostTools] fly unavailable: " + ex.Message)); } } public static string ToggleFriendlyFire() { if (!Plugin.IsHosting) { return "Only the host can change that."; } try { bool flag = !ServerSettings.UseFriendlyFire; Traverse val = Traverse.Create(typeof(ServerSettings)).Property("UseFriendlyFire", (object[])null); if (val != null && val.PropertyExists()) { val.SetValue((object)flag); } else { Traverse.Create(typeof(ServerSettings)).Field("_useFriendlyFire").SetValue((object)flag); } bool useFriendlyFire = ServerSettings.UseFriendlyFire; Plugin.Log.LogInfo((object)("[HostTools] friendly fire -> " + useFriendlyFire)); return useFriendlyFire ? "Friendly fire ON - players can hurt each other." : "Friendly fire OFF - punches between players do nothing. That is the game's setting, not the guard."; } catch (Exception ex) { return "Could not change it: " + ex.Message; } } } internal static class JoinFixPatches { [HarmonyPatch(typeof(Player), "InitializePlayer")] internal static class Player_InitializePlayer_Patch { private static bool Prefix(Player __instance) { if (_reentering) { return true; } NetworkConnection owner; try { owner = ((NetworkBehaviour)__instance).Owner; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[JoinFix] could not read Owner: " + ex.Message)); return true; } if (owner != (NetworkConnection)null && Client.Clients.ContainsKey(owner)) { return true; } if (!_pending.Add(__instance)) { return false; } Plugin.Log.LogWarning((object)("[JoinFix] Client object for connection " + ((owner == (NetworkConnection)null) ? "" : owner.ClientId.ToString()) + " hasn't spawned yet — deferring InitializePlayer instead of letting it crash the spawn batch.")); Plugin instance = Plugin.Instance; if ((Object)(object)instance == (Object)null) { _pending.Remove(__instance); return true; } ((MonoBehaviour)instance).StartCoroutine(WaitForClientThenInitialize(__instance, owner)); return false; } } private static bool _reentering; private static readonly HashSet _pending = new HashSet(); private static readonly MethodInfo InitializePlayerMethod = AccessTools.Method(typeof(Player), "InitializePlayer", (Type[])null, (Type[])null); private const float WaitTimeoutSeconds = 15f; private static IEnumerator WaitForClientThenInitialize(Player player, NetworkConnection owner) { float deadline = Time.realtimeSinceStartup + 15f; while (Time.realtimeSinceStartup < deadline) { if ((Object)(object)player == (Object)null) { _pending.Remove(player); yield break; } if (owner != (NetworkConnection)null && Client.Clients.ContainsKey(owner)) { _pending.Remove(player); RunOriginal(player, owner); yield break; } yield return null; } _pending.Remove(player); Plugin.Log.LogError((object)($"[JoinFix] gave up after {15f:0}s waiting for the Client of connection " + ((owner == (NetworkConnection)null) ? "" : owner.ClientId.ToString()) + ". That player will stay uninitialized, but the rest of the world still loaded.")); } private static void RunOriginal(Player player, NetworkConnection owner) { if (InitializePlayerMethod == null) { Plugin.Log.LogError((object)"[JoinFix] Player.InitializePlayer not found — cannot finish deferred init."); return; } try { _reentering = true; InitializePlayerMethod.Invoke(player, null); Plugin.Log.LogInfo((object)$"[JoinFix] deferred InitializePlayer completed for connection {owner.ClientId}."); } catch (Exception ex) { Plugin.Log.LogError((object)$"[JoinFix] deferred InitializePlayer failed: {ex.InnerException ?? ex}"); } finally { _reentering = false; } } } internal static class Kit { public static bool Aimbot; public static float AimFov = 80f; public static float AimRange = 300f; public static float AimSnap = 30f; public static float AimTurnRate = 720f; public static bool ManHunt; public static float ManFov = 90f; public static float ManSnap = 30f; public static bool Laser; private static bool _inShot; public static bool BunnyHop; private static float _lastHop; public static bool BossRush; public static string BossName = "Mutated"; private static bool _fishing; private static Fishable _cachedBoss; private static float _lastScan = -999f; public static float WhaleDistance = 40f; private static Creature _whalePrefab; private const float LavaRange = 400f; public static bool Visibility; public static float FogMultiplier = 0.15f; public static float ViewDistance = 5000f; private static bool _fogCaptured; private static bool _fogWasOn; private static bool _visWasOn; private static bool _clipCaptured; private static float _fogDensity; private static float _origClip; public static bool UnlockSkins; private static bool _itemSkinsDone; private static float _nextSkinTry; public static string CasinoRarity = "off"; public static bool Roulette; public static string RouletteColour = "green"; public static bool DripperTakesAnything; public static float WorthMultiplier = 1000f; public static bool RevealIslands; public static bool UnlockTravel = true; private static bool _inDots; private static bool _inTravel; private static bool Muffle { get { if (_inShot) { return Laser; } return false; } } private static void ApplyAim(PlayerAimAssist aa) { float num = Mathf.Clamp(AimFov, 1f, 90f); float num2 = Mathf.Max(1f, AimRange); float num3 = Mathf.Min(num + 40f, 179f); float num4 = Mathf.Cos(num * (MathF.PI / 180f)); Traverse obj = Traverse.Create((object)aa); obj.Field("_maxTargetDistance").SetValue((object)num2); obj.Field("_maxSqrTargetDistance").SetValue((object)(num2 * num2)); obj.Field("_adsAcquireAngle").SetValue((object)num); obj.Field("_minAcquireAlignment").SetValue((object)num4); obj.Field("_acquireAlignmentRange").SetValue((object)Mathf.Max(1f - num4, Mathf.Epsilon)); obj.Field("_trackingBreakAngle").SetValue((object)num3); obj.Field("_minTrackingAlignment").SetValue((object)Mathf.Cos(num3 * (MathF.PI / 180f))); obj.Field("_maxRotationSpeed").SetValue((object)AimTurnRate); obj.Field("_trackingSharpness").SetValue((object)AimSnap); } public static void AimCache_Post(PlayerAimAssist __instance) { try { if (Aimbot) { ApplyAim(__instance); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Kit] aim cache: " + ex.Message)); } } public static void AimCanUse_Post(PlayerAimAssist __instance, ref bool __result) { try { if (Aimbot) { Player value = Traverse.Create((object)__instance).Field("_player").GetValue(); if (!((Object)(object)value == (Object)null) && !value.BlockInputs && !((Object)(object)value.Camera == (Object)null) && value.Camera.MouseLocked) { ApplyAim(__instance); __result = true; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Kit] aim gate: " + ex.Message)); } } public static void AimAssist_Post(PlayerCamera __instance) { try { HuntFrame(__instance); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Kit] manhunt: " + ex.Message)); } } private static void HuntFrame(PlayerCamera cam) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) if (!ManHunt) { return; } Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null || localPlayer.BlockInputs || (Object)(object)Traverse.Create((object)cam).Field("_player").GetValue() != (Object)(object)localPlayer || (Object)(object)localPlayer.Camera == (Object)null || !localPlayer.Camera.MouseLocked) { return; } Item val = (((Object)(object)localPlayer.Holding != (Object)null) ? localPlayer.Holding.HeldItem : null); if ((Object)(object)val == (Object)null || (Object)(object)val.Weapon == (Object)null || !val.Weapon.IsAds) { return; } Vector3 position = cam.CamTransform.position; Traverse val2 = Traverse.Create((object)cam).Field("_rot"); Vector3 value = val2.GetValue(); Vector3 val3 = Quaternion.Euler(value) * Vector3.forward; float num = Mathf.Clamp(ManFov, 1f, 180f); Player val4 = null; float num2 = float.MaxValue; Vector3 val5 = Vector3.zero; foreach (Player alivePlayer in PlayerManager.AlivePlayers) { if ((Object)(object)alivePlayer == (Object)null || (Object)(object)alivePlayer == (Object)(object)localPlayer) { continue; } Vector3 val6 = (Object.op_Implicit((Object)(object)alivePlayer.Rigidbody) ? alivePlayer.Rigidbody.worldCenterOfMass : alivePlayer.Transform.position); Vector3 val7 = val6 - position; float magnitude = ((Vector3)(ref val7)).magnitude; if (magnitude < 0.01f) { continue; } float num3 = Vector3.Angle(val3, val7); if (!(num3 > num)) { float num4 = num3 + magnitude * 0.01f; if (num4 < num2) { num2 = num4; val4 = alivePlayer; val5 = val6; } } } if (!((Object)(object)val4 == (Object)null)) { Quaternion val8 = Quaternion.LookRotation(val5 - position, Vector3.up); Vector3 eulerAngles = ((Quaternion)(ref val8)).eulerAngles; float num5 = 1f - Mathf.Exp((0f - Mathf.Max(0.01f, ManSnap)) * Time.deltaTime); value.x += Mathf.DeltaAngle(value.x, eulerAngles.x) * num5; value.y += Mathf.DeltaAngle(value.y, eulerAngles.y) * num5; value.x = Mathf.Clamp(value.x, -90f, 90f); val2.SetValue((object)value); } } public static void Cooldown_Post(Weapon __instance, ref bool __result) { if (Laser && (Object)(object)__instance != (Object)null && Mine(__instance)) { __result = false; } } public static void WeaponUpdate_Pre(Weapon __instance) { if (Laser && (Object)(object)__instance != (Object)null && Mine(__instance)) { Traverse.Create((object)__instance).Field("_fullAuto").SetValue((object)true); } } public static void LaserShoot_Pre(Weapon __instance) { _inShot = Laser && (Object)(object)__instance != (Object)null && Mine(__instance); } public static void LaserShoot_Fin() { _inShot = false; } public static bool Kick_Pre() { return !Muffle; } private static bool Mine(Weapon w) { Player value = Traverse.Create((object)w).Field("_holder").GetValue(); if ((Object)(object)value != (Object)null) { return (Object)(object)value == (Object)(object)Player.LocalPlayer; } return false; } private static void HopFrame() { if (!BunnyHop) { return; } Player localPlayer = Player.LocalPlayer; if (!((Object)(object)localPlayer == (Object)null) && !localPlayer.BlockInputs) { PlayerMovement movement = localPlayer.Movement; if (!((Object)(object)movement == (Object)null) && movement.Grounded && Input.GetKey((KeyCode)32) && !(Time.time - _lastHop < 0.1f)) { movement.Jump(); _lastHop = Time.time; } } } public static void FindFish_Pre() { _fishing = true; } public static void FindFish_Fin() { _fishing = false; } public static void BossRoll_Post(List weights, ref Fishable __result) { if (!BossRush || !_fishing) { return; } try { if (Object.op_Implicit((Object)(object)BossManager.Boss)) { return; } } catch { return; } Fishable val = FindBoss(); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.ItemToSpawn == (Object)null) && !((Object)(object)__result == (Object)(object)val)) { __result = val; Plugin.Log.LogInfo((object)("[Kit] boss on the line: " + ((Object)val.ItemToSpawn).name)); } } private static Fishable FindBoss() { if ((Object)(object)_cachedBoss != (Object)null && Time.time - _lastScan < 5f) { return _cachedBoss; } _lastScan = Time.time; string wanted = (BossName ?? "").Trim(); if (wanted.Length == 0) { wanted = "Mutated"; } try { _cachedBoss = (from f in Resources.FindObjectsOfTypeAll() where (Object)(object)f != (Object)null && (Object)(object)f.ItemToSpawn != (Object)null where ((Object)f.ItemToSpawn).name.IndexOf(wanted, StringComparison.OrdinalIgnoreCase) >= 0 select f).FirstOrDefault(); } catch { _cachedBoss = null; } return _cachedBoss; } public static List BossNames() { try { return (from n in (from f in Resources.FindObjectsOfTypeAll() where (Object)(object)f != (Object)null && (Object)(object)f.ItemToSpawn != (Object)null select ((Object)f.ItemToSpawn).name).Distinct() where n.IndexOf("Whale", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("Mutated", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("Piranha", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("Shark", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("Squid", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("Kraken", StringComparison.OrdinalIgnoreCase) >= 0 orderby n select n).ToList(); } catch { return new List(); } } public static void Lava_Post(MainLava __instance) { if ((Object)(object)_whalePrefab != (Object)null) { return; } try { Creature value = Traverse.Create((object)__instance).Field("_mutatedWhalePrefab").GetValue(); if (!((Object)(object)value == (Object)null)) { _whalePrefab = value; Plugin.Log.LogInfo((object)("[Kit] cached " + ((Object)value).name + " - summonable from any island now.")); } } catch { } } public static string SummonWhale() { //IL_004c: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsHosting) { return "Spawning is server side - host or play solo."; } try { if (Object.op_Implicit((Object)(object)BossManager.Boss)) { return "A boss is already up. Kill it first."; } } catch { } try { MainLava val = Object.FindAnyObjectByType(); if ((Object)(object)val != (Object)null && (Object)(object)Player.LocalPlayer != (Object)null) { float num = Vector3.Distance(Body.Of(Player.LocalPlayer), ((Component)val).transform.position); if (num > 400f) { Plugin.Log.LogInfo((object)("[Kit] whale: " + num.ToString("0") + "m from the volcano, so spawning it here instead.")); val = null; } } if ((Object)(object)val != (Object)null) { MethodInfo methodInfo = AccessTools.Method(typeof(MainLava), "SpawnMutatedWhale", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.Invoke(val, null) is IEnumerator enumerator) { ((MonoBehaviour)val).StartCoroutine(enumerator); return "The volcano is erupting. Whale in about three seconds."; } } } catch { } if ((Object)(object)_whalePrefab == (Object)null) { return "The whale prefab lives on the lava island. Visit it once this session and it can be summoned anywhere after."; } Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null) { return "No local player to spawn in front of."; } try { Vector3 val2 = Body.Facing(localPlayer); Vector3 val3 = Body.Of(localPlayer) + val2 * Mathf.Max(5f, WhaleDistance); val3.y = WaterManager.WaterHeight; Creature val4 = Object.Instantiate(_whalePrefab, val3, Quaternion.LookRotation(-val2)); ((NetworkBehaviour)Server.Instance).Spawn(((Component)val4).gameObject, (NetworkConnection)null, default(Scene)); ManualLogSource log = Plugin.Log; string text = ((Vector3)(ref val3)).ToString("0.0"); Vector3 val5 = Body.Of(localPlayer); log.LogInfo((object)("[Kit] whale at " + text + " - you are at " + ((Vector3)(ref val5)).ToString("0.0"))); return "Magma whale, " + WhaleDistance.ToString("0") + "m ahead. Its attacks still aim at the lava island."; } catch (Exception ex) { return "Could not: " + ex.Message; } } public static void SetFog_Post() { CaptureFog(); if (Visibility) { ApplyFog(); } } private static void CaptureFog() { _fogDensity = RenderSettings.fogDensity; _fogWasOn = RenderSettings.fog; _fogCaptured = true; } private static void ApplyFog() { float num = Mathf.Clamp01(FogMultiplier); if (num <= 0f) { RenderSettings.fog = false; return; } RenderSettings.fog = _fogWasOn; RenderSettings.fogDensity = _fogDensity * num; } private static void VisibilityFrame() { if (Visibility) { if (!_fogCaptured) { CaptureFog(); } ApplyFog(); Camera curCamera = GameInfo.CurCamera; if ((Object)(object)curCamera != (Object)null && ViewDistance > 0f) { if (!_clipCaptured) { _origClip = curCamera.farClipPlane; _clipCaptured = true; } if (curCamera.farClipPlane < ViewDistance) { curCamera.farClipPlane = ViewDistance; } } _visWasOn = true; } else if (_visWasOn) { _visWasOn = false; if (_fogCaptured) { RenderSettings.fog = _fogWasOn; RenderSettings.fogDensity = _fogDensity; } Camera curCamera2 = GameInfo.CurCamera; if ((Object)(object)curCamera2 != (Object)null && _clipCaptured) { curCamera2.farClipPlane = _origClip; } } } public static void SkinAwake_Post() { _itemSkinsDone = false; UnlockClothing(); } private static void SkinFrame() { if (UnlockSkins && !_itemSkinsDone && !(Time.time < _nextSkinTry)) { _nextSkinTry = Time.time + 2f; UnlockItemSkins(); } } public static string UnlockEverything() { UnlockSkins = true; _itemSkinsDone = false; int num = UnlockClothing(); int num2 = UnlockItemSkins(); if (num2 <= 0) { return "Unlocked " + num + " clothing. Item skins are not loaded yet - they will be picked up as soon as they are."; } return "Unlocked " + num + " clothing and " + num2 + " skins."; } private static int UnlockClothing() { int num = 0; try { string[] array = new string[3] { "_unlockedHats", "_unlockedOutfits", "_unlockedAccessories" }; foreach (string text in array) { if (Traverse.Create(typeof(SkinManager)).Field(text).GetValue() is IList list) { num += list.Count; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Kit] clothing: " + ex.Message)); } return num; } private static int UnlockItemSkins() { int num = 0; try { Item[] availableItems = SlotMachine.AvailableItems; if (availableItems != null) { Item[] array = availableItems; foreach (Item val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.SkinPreset == (Object)null) { continue; } for (int j = 0; j < val.SkinPreset.Skins.Count; j++) { try { SaveManager.UnlockSkin(val.ID, (byte)j); num++; } catch { } } } } Boat boat = BoatManager.Boat; if ((Object)(object)boat != (Object)null && (Object)(object)boat.SkinPreset != (Object)null) { for (int k = 0; k < boat.SkinPreset.Skins.Count; k++) { try { SaveManager.UnlockSkin(byte.MaxValue, (byte)k); num++; } catch { } } } _itemSkinsDone = true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Kit] item skins: " + ex.Message)); } if (num > 0) { _itemSkinsDone = true; } return num; } public unsafe static void Roll_Pre() { //IL_003d: 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) string text = (CasinoRarity ?? "").Trim(); if (text.Length == 0 || text.Equals("off", StringComparison.OrdinalIgnoreCase)) { try { SlotMachineManager.SetCheatSkin((Item)null, byte.MaxValue); return; } catch { return; } } Rarity target = ResolveRarity(text); if (!PickSkin(target, out var item, out var skinIndex)) { Plugin.Log.LogWarning((object)("[Kit] no " + ((object)(*(Rarity*)(&target))/*cast due to .constrained prefix*/).ToString() + " skin to force this roll.")); return; } try { SlotMachineManager.SetCheatSkin(item, skinIndex); } catch { } } private static Rarity ResolveRarity(string mode) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (mode.Equals("legendary", StringComparison.OrdinalIgnoreCase)) { return (Rarity)3; } if (mode.Equals("rare", StringComparison.OrdinalIgnoreCase)) { return (Rarity)2; } if (mode.Equals("common", StringComparison.OrdinalIgnoreCase)) { return (Rarity)1; } return Greenest(); } private static Rarity Greenest() { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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) Rarity best = (Rarity)2; float bestScore = float.NegativeInfinity; try { Consider((Rarity)1, GameInfo.CommonColor); Consider((Rarity)2, GameInfo.RareColor); Consider((Rarity)3, GameInfo.LegendaryColor); } catch { } return best; void Consider(Rarity r, Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_002b: Unknown result type (might be due to invalid IL or missing references) float num = c.g - Mathf.Max(c.r, c.b); if (num > bestScore) { bestScore = num; best = r; } } } private static bool PickSkin(Rarity target, out Item item, out byte skinIndex) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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) item = null; skinIndex = byte.MaxValue; try { List list = new List(); if (SlotMachine.AvailableItems != null) { list.AddRange(SlotMachine.AvailableItems); } if (!SlotMachine.ExcludeBoat) { list.Add(null); } foreach (Item item2 in list) { SkinPreset val = (((Object)(object)item2 != (Object)null) ? item2.SkinPreset : (((Object)(object)BoatManager.Boat != (Object)null) ? BoatManager.Boat.SkinPreset : null)); if ((Object)(object)val == (Object)null) { continue; } for (int i = 0; i < val.Skins.Count; i++) { ItemSkin val2 = val.Skins[i]; if (((ItemSkin)(ref val2)).Rarity == target) { item = item2; skinIndex = (byte)i; return true; } } } } catch { } return false; } public static string CycleCasino() { string text = (CasinoRarity ?? "").Trim().ToLowerInvariant(); if (!(text == "off") && (text == null || text.Length != 0)) { if (!(text == "green")) { if (text == "rare") { CasinoRarity = "legendary"; } else { CasinoRarity = "off"; } } else { CasinoRarity = "rare"; } } else { CasinoRarity = "green"; } if (CasinoRarity == "off") { try { SlotMachineManager.SetCheatSkin((Item)null, byte.MaxValue); } catch { } } return "Dripper rigged: " + CasinoLabel(); } public static string CasinoLabel() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) string text = (CasinoRarity ?? "").Trim(); if (text.Length == 0) { return "off"; } if (text.Equals("green", StringComparison.OrdinalIgnoreCase)) { return "green (" + ((object)Greenest()/*cast due to .constrained prefix*/).ToString() + ")"; } return text.ToLowerInvariant(); } public static void Roulette_Post(ref BetColor __result) { //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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between I4 and Unknown //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected I4, but got Unknown if (Roulette) { BetColor val = ResolveColour(RouletteColour); if ((int)__result != (int)val) { __result = (BetColor)(int)val; } } } private static BetColor ResolveColour(string name) { name = (name ?? "").Trim(); if (!name.Equals("black", StringComparison.OrdinalIgnoreCase)) { if (!name.Equals("red", StringComparison.OrdinalIgnoreCase)) { return (BetColor)2; } return (BetColor)1; } return (BetColor)0; } public static string CycleRoulette() { string text = (RouletteColour ?? "").Trim().ToLowerInvariant(); if (!Roulette) { Roulette = true; RouletteColour = "green"; } else if (text == "green") { RouletteColour = "red"; } else if (text == "red") { RouletteColour = "black"; } else { Roulette = false; } return "Roulette: " + RouletteLabel(); } public static string RouletteLabel() { if (!Roulette) { return "off"; } return RouletteColour; } public static bool Dripper_Pre(Collider other) { if (!DripperTakesAnything) { return true; } try { if ((Object)(object)Server.Instance == (Object)null || !((NetworkBehaviour)Server.Instance).IsServerInitialized) { return false; } if (SlotMachine.IsRolling) { return false; } Item val = ItemManager.Get(other); if ((Object)(object)val == (Object)null || (Object)(object)val.Holder != (Object)null) { return false; } if ((Object)(object)val.Creature != (Object)null && !val.Creature.IsDead) { return false; } Player val2 = (((Object)(object)val.Holder != (Object)null) ? val.Holder : val.LastHolder); if ((Object)(object)val2 == (Object)null) { return false; } val.DestroyItem((byte)4, byte.MaxValue); SlotMachineManager.RollRandom(val2); } catch { } return false; } private static float WorthOf(Item item) { try { Traverse val = Traverse.Create((object)item).Field("_worth"); if (val != null && val.FieldExists()) { return val.GetValue(); } } catch { } return -1f; } public static string InflateHeld() { if (!Plugin.IsHosting) { Plugin.Log.LogInfo((object)"[Kit] inflate: not hosting."); return "Worth is server side - host or play solo."; } Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)Server.Instance == (Object)null) { Plugin.Log.LogInfo((object)("[Kit] inflate: player=" + (((Object)(object)localPlayer == (Object)null) ? "null" : "ok") + " server=" + (((Object)(object)Server.Instance == (Object)null) ? "null" : "ok"))); return "Not connected."; } List list = Carried(localPlayer); if (list.Count == 0) { Plugin.Log.LogInfo((object)("[Kit] inflate: nothing carried - holder=" + (((Object)(object)localPlayer.Holding == (Object)null) ? "null" : (((Object)(object)localPlayer.Holding.HeldItem == (Object)null) ? "empty" : "ok")))); return "You are not carrying anything to inflate."; } float num = Mathf.Clamp(WorthMultiplier, 1f, 100000f); int num2 = 0; int num3 = 0; string text = null; foreach (Item item in list) { float num4 = WorthOf(item); try { Server.Instance.SetItemMultiplier(item, num); } catch (Exception ex) { if (text == null) { text = ex.Message; } Plugin.Log.LogWarning((object)("[Kit] inflate threw on " + ((Object)item).name + ": " + ex.Message)); continue; } float num5 = WorthOf(item); Plugin.Log.LogInfo((object)("[Kit] inflate " + ((Object)item).name + " x" + num.ToString("0") + " worth " + num4.ToString("0") + " -> " + num5.ToString("0"))); if (num4 >= 0f && num5 >= 0f && Mathf.Approximately(num4, num5)) { num3++; } else { num2++; } } if (text != null && num2 == 0) { return "SetItemMultiplier threw: " + text; } if (num2 == 0) { return "Nothing changed - the server took the call and ignored it for all " + list.Count + " item(s). Check the log."; } return "Inflated " + num2 + " of " + list.Count + " item(s) x" + num.ToString("0") + ((num3 > 0) ? (". " + num3 + " would not take - already inflated, probably.") : ".") + " Sell them now."; } private static List Carried(Player me) { List list = new List(); try { Item val = (((Object)(object)me.Holding != (Object)null) ? me.Holding.HeldItem : null); if ((Object)(object)val != (Object)null) { list.Add(val); } } catch { } try { object value = Traverse.Create((object)me).Property("Inventory", (object[])null).GetValue(); if (value == null) { value = Traverse.Create((object)me).Field("_inventory").GetValue(); } if (value == null) { return list; } IEnumerable enumerable = (Traverse.Create(value).Property("Items", (object[])null).GetValue() as IEnumerable) ?? (Traverse.Create(value).Field("_slots").GetValue() as IEnumerable); if (enumerable == null) { Plugin.Log.LogInfo((object)"[Kit] inflate: found an inventory with no Items or _slots to read."); return list; } foreach (object item in enumerable) { if (item != null) { Item val2 = (Item)((item is Item) ? item : null); if ((Object)(object)val2 == (Object)null) { Traverse val3 = Traverse.Create(item); object value2 = val3.Property("Item", (object[])null).GetValue(); val2 = (Item)(((value2 is Item) ? value2 : null) ?? ((object)/*isinst with value type is only supported in some contexts*/)); } if ((Object)(object)val2 != (Object)null && !list.Contains(val2)) { list.Add(val2); } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Kit] inflate: could not read the inventory: " + ex.Message)); } return list; } public static void Radar_Pre(RadarUI __instance) { if (!RevealIslands) { return; } try { Traverse obj = Traverse.Create((object)__instance); obj.Field("_isOn").SetValue((object)true); CanvasGroup value = obj.Field("_canvasGroup").GetValue(); if ((Object)(object)value != (Object)null) { value.alpha = 1f; } } catch { } } public static void Dots_Pre() { _inDots = true; } public static void Dots_Fin() { _inDots = false; } public static void Travel_Pre() { _inTravel = true; } public static void Travel_Fin() { _inTravel = false; } public static void MaxIsland_Post(ref byte __result) { if (!RevealIslands || (!_inDots && (!_inTravel || !UnlockTravel))) { return; } try { int num = Mathf.Clamp(IslandManager.TotalIslands, 0, 255); if (num > __result) { __result = (byte)num; } } catch { } } public static void Tick() { try { HopFrame(); } catch { } try { VisibilityFrame(); } catch { } try { SkinFrame(); } catch { } } public static void Install(Harmony h) { //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Expected O, but got Unknown Patch(typeof(PlayerAimAssist), "CacheSettings", null, "AimCache_Post"); Patch(typeof(PlayerAimAssist), "CanUseAimAssist", null, "AimCanUse_Post"); Patch(typeof(PlayerCamera), "ApplyAimAssist", null, "AimAssist_Post"); Patch(typeof(Weapon), "HasCooldown", null, "Cooldown_Post"); Patch(typeof(Weapon), "Update", "WeaponUpdate_Pre"); Patch(typeof(Weapon), "Shoot", "LaserShoot_Pre", null, "LaserShoot_Fin"); Patch(typeof(Weapon), "AddModelRecoil", "Kick_Pre"); Patch(typeof(PlayerToolMovement), "Recoil", "Kick_Pre"); Patch(typeof(PlayerCamera), "Recoil", "Kick_Pre"); Patch(typeof(PlayerMovement), "Knockback", "Kick_Pre"); Patch(typeof(CreatureManager), "FindFishForBait", "FindFish_Pre", null, "FindFish_Fin"); Patch(typeof(CreatureManager), "GetRandomItem", null, "BossRoll_Post"); Patch(typeof(MainLava), "Awake", null, "Lava_Post"); Patch(typeof(ShaderManager), "SetFog", null, "SetFog_Post"); Patch(typeof(SkinManager), "Awake", null, "SkinAwake_Post"); Patch(typeof(SlotMachineManager), "RollRandom", "Roll_Pre"); Patch(typeof(LocalCasino), "GetRouletteColorFromBall", null, "Roulette_Post"); Patch(typeof(SlotMachine), "OnTriggerStay", "Dripper_Pre"); Patch(typeof(RadarUI), "Update", "Radar_Pre"); Patch(typeof(RadarUI), "UpdateIslandDots", "Dots_Pre", null, "Dots_Fin"); Patch(typeof(IslandSpawner), "OnTriggerEnter", "Travel_Pre", null, "Travel_Fin"); try { MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(OnlineIslandManager), "MaxIslandUnlocked"); if (methodInfo != null) { h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Kit), "MaxIsland_Post", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Kit] MaxIslandUnlocked: " + ex.Message)); } Plugin.Log.LogWarning((object)"[Kit] the rest of Ender's Tweaks is loaded. Private build only."); void Patch(Type type, string method, string pre = null, string post = null, string fin = null) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) try { MethodInfo methodInfo2 = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo2 == null) { Plugin.Log.LogWarning((object)("[Kit] " + type.Name + "." + method + " not found")); } else { h.Patch((MethodBase)methodInfo2, (pre == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Kit), pre, (Type[])null, (Type[])null)), (post == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Kit), post, (Type[])null, (Type[])null)), (HarmonyMethod)null, (fin == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Kit), fin, (Type[])null, (Type[])null)), (HarmonyMethod)null); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Kit] " + method + ": " + ex2.Message)); } } } } internal static class Lobby { private static int _applied; public static int Applied => _applied; public static string Widen() { int num = ((Plugin.LobbySize != null) ? Plugin.LobbySize.Value : 0); if (num <= 0) { Plugin.Log.LogInfo((object)"[Lobby] MaxPlayers is 0, so the lobby holds whatever the game says. Raise it in the console under Warden, or set Lobby/MaxPlayers in the config."); return null; } if (!Plugin.IsHosting) { return "Only the host decides how big the lobby is."; } string text = ""; try { NetworkManager val = Object.FindAnyObjectByType(); ServerManager val2 = (((Object)(object)val != (Object)null) ? val.ServerManager : null); if ((Object)(object)val2 != (Object)null) { MethodInfo methodInfo = AccessTools.Method(((object)val2).GetType(), "SetMaximumClients", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(val2, new object[1] { num }); text += "FishNet "; } else { Plugin.Log.LogWarning((object)"[Lobby] no SetMaximumClients on the server manager."); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Lobby] FishNet cap: " + ex.Message)); } try { int num2 = 0; Type[] types = typeof(Player).Assembly.GetTypes(); foreach (Type type in types) { if (type == null || type.IsNested) { continue; } string[] array = new string[3] { "MaxPlayers", "maxPlayers", "_maxPlayers" }; foreach (string text2 in array) { FieldInfo fieldInfo = AccessTools.Field(type, text2); if (fieldInfo == null || fieldInfo.FieldType != typeof(int)) { continue; } if (fieldInfo.IsStatic) { fieldInfo.SetValue(null, num); num2++; continue; } Object val3 = Object.FindAnyObjectByType(type); if (val3 != (Object)null) { fieldInfo.SetValue(val3, num); num2++; } } } if (num2 > 0) { text = text + num2 + " game field(s, may be inert) "; } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Lobby] game field: " + ex2.Message)); } if (Steam(num)) { text += "Steam "; } _applied = num; string text3 = (string.IsNullOrEmpty(text) ? "Could not find anything to raise - the lobby size is unchanged." : ("Lobby raised to " + num + " (" + text.Trim() + ").")); Plugin.Log.LogInfo((object)("[Lobby] " + text3)); return text3; } private static bool Steam(int want) { try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name.IndexOf("steamworks", StringComparison.OrdinalIgnoreCase) >= 0); if (assembly == null) { return false; } Type type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "SteamMatchmaking"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "SetLobbyMemberLimit", (Type[])null, (Type[])null) : null); if (methodInfo == null) { return false; } object obj = null; Type[] types = typeof(Player).Assembly.GetTypes(); foreach (Type type2 in types) { if (type2 == null || type2.IsNested) { continue; } string[] array = new string[3] { "CurrentLobbyID", "LobbyID", "LobbyId" }; foreach (string text in array) { PropertyInfo propertyInfo = AccessTools.Property(type2, text); if (propertyInfo == null || propertyInfo.GetMethod == null) { continue; } Object val = (propertyInfo.GetMethod.IsStatic ? null : Object.FindAnyObjectByType(type2)); if (propertyInfo.GetMethod.IsStatic || !(val == (Object)null)) { object value = propertyInfo.GetValue(val); if (value != null) { obj = value; break; } } } if (obj != null) { break; } array = new string[5] { "k__BackingField", "m_steamIDLobby", "m_ulSteamIDLobby", "_lobbyId", "LobbyId" }; foreach (string text2 in array) { FieldInfo fieldInfo = AccessTools.Field(type2, text2); if (fieldInfo == null) { continue; } Object val2 = (fieldInfo.IsStatic ? null : Object.FindAnyObjectByType(type2)); if (fieldInfo.IsStatic || !(val2 == (Object)null)) { object value2 = fieldInfo.GetValue(val2); if (value2 != null) { obj = value2; break; } } } if (obj != null) { break; } } if (obj == null) { Plugin.Log.LogWarning((object)"[Lobby] no lobby id to resize."); return false; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length != 2 || !parameters[0].ParameterType.IsInstanceOfType(obj)) { return false; } object obj2 = methodInfo.Invoke(null, new object[2] { obj, want }); Plugin.Log.LogInfo((object)("[Lobby] SetLobbyMemberLimit(" + want + ") returned " + obj2)); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Lobby] Steam limit: " + ex.Message)); return false; } } } internal sealed class Entry { public DateTime When; public string Who; public ulong Key; public string Rpc; public Verdict Verdict; public bool Blocked; public string Shot; public string Line => When.ToString("HH:mm:ss", CultureInfo.InvariantCulture) + " [" + Verdict.Tag + "] " + Who + " " + Rpc + " " + Verdict.Detail + (Blocked ? " (blocked)" : "") + ((Shot != null) ? (" [" + Shot + "]") : ""); } internal static class Logbook { private static readonly List _entries = new List(512); private static readonly Dictionary _strikes = new Dictionary(); private static readonly Dictionary _names = new Dictionary(); private static string _file; private static readonly object _gate = new object(); public static IReadOnlyList Entries => _entries; public static string FilePath => _file; public static string Folder => Path.GetFullPath(Path.Combine(Paths.ConfigPath, "..", "Fishwarden")); public static void BeginSession() { try { Directory.CreateDirectory(Folder); string text = DateTime.Now.ToString("yyyy-MM-dd_HHmmss", CultureInfo.InvariantCulture); _file = Path.Combine(Folder, "poachers-log_" + text + ".txt"); File.WriteAllText(_file, "Fishwarden 2.0.0 - Poacher's Log" + Environment.NewLine + "Session opened " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture) + Environment.NewLine + "Level at open: " + Policy.Spell() + Environment.NewLine + new string('-', 72) + Environment.NewLine); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Logbook] could not open a log file: " + ex.Message + " - keeping findings in memory only.")); _file = null; } } public static Entry Record(NetworkConnection conn, string rpc, Verdict v, bool blocked) { ulong key = Sender.KeyOf(conn); string text = Sender.NameOf(conn); Rules.Note(v.Rule); Entry entry = new Entry { When = DateTime.Now, Who = text, Key = key, Rpc = rpc, Verdict = v, Blocked = blocked }; lock (_gate) { _entries.Add(entry); _names[key] = text; _strikes.TryGetValue(key, out var value); _strikes[key] = value + v.StrikeWeight; } Plugin.Log.LogWarning((object)("[Fishwarden] " + entry.Line)); Append(entry.Line); return entry; } public static void AttachShot(Entry e, string shot) { if (e != null) { e.Shot = shot; Append(" evidence: " + shot); } } private static void Append(string line) { if (_file == null) { return; } try { File.AppendAllText(_file, line + Environment.NewLine); } catch { } } public static int StrikesFor(NetworkConnection conn) { ulong key = Sender.KeyOf(conn); lock (_gate) { int value; return _strikes.TryGetValue(key, out value) ? value : 0; } } public static void Pardon(ulong key) { lock (_gate) { _strikes.Remove(key); } Append(" PARDONED " + Name(key) + " - strikes cleared by host"); } public static string Name(ulong key) { lock (_gate) { string value; return _names.TryGetValue(key, out value) ? value : ("#" + key); } } public static Dictionary Tally() { lock (_gate) { return new Dictionary(_strikes); } } public static List Tail(int n) { lock (_gate) { int num = Math.Max(0, _entries.Count - n); return _entries.GetRange(num, _entries.Count - num); } } public static List ShotsFor(ulong key) { lock (_gate) { return (from e in _entries where e.Key == key && e.Shot != null select e.Shot).Distinct().ToList(); } } public static List EntriesFor(ulong key, int max) { lock (_gate) { List list = _entries.Where((Entry e) => e.Key == key).ToList(); int num = Math.Max(0, list.Count - max); return list.GetRange(num, list.Count - num); } } public static string Report() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Fishwarden 2.0.0 - session report"); stringBuilder.AppendLine("Generated " + DateTime.Now.ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture)); stringBuilder.AppendLine(); Dictionary dictionary = Tally(); if (dictionary.Count == 0) { stringBuilder.AppendLine("No findings. Clean session."); return stringBuilder.ToString(); } stringBuilder.AppendLine("Strikes by player:"); foreach (KeyValuePair item in dictionary) { stringBuilder.AppendLine($" {Name(item.Key),-24} {item.Value}"); } stringBuilder.AppendLine(); stringBuilder.AppendLine("What people were doing:"); foreach (string item2 in Summary.Lines(includeClean: false)) { stringBuilder.AppendLine(" " + item2); } stringBuilder.AppendLine(); stringBuilder.AppendLine("Findings:"); lock (_gate) { foreach (Entry entry in _entries) { stringBuilder.AppendLine(" " + entry.Line); } } return stringBuilder.ToString(); } } internal static class Movement { private sealed class Track { public Vector3 LastPos; public float LastTime; public int OverSpeed; public float LastFinding = -999f; public bool Primed; public Traverse WalkSpeed; public Traverse SprintSpeed; } private static readonly Dictionary _tracks = new Dictionary(); private static float _nextTick; public static void Tick() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsHosting || !Plugin.WatchMovement.Value) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextTick) { return; } _nextTick = realtimeSinceStartup + Plugin.MovementSampleInterval.Value; try { foreach (Player player in PlayerManager.Players) { if ((Object)(object)player == (Object)null || (Object)(object)player == (Object)(object)Player.LocalPlayer) { continue; } NetworkConnection owner = ((NetworkBehaviour)player).Owner; if (!Sender.IsTrusted(owner)) { try { Judge(player, Body.Of(player), owner); } catch { } } } } catch { } } private static void Judge(Player p, Vector3 pos, NetworkConnection conn) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!value.Primed) { value.LastPos = pos; value.LastTime = realtimeSinceStartup; value.Primed = true; return; } float num = realtimeSinceStartup - value.LastTime; value.LastTime = realtimeSinceStartup; Vector3 lastPos = value.LastPos; value.LastPos = pos; if (num < 0.03f || num > 1f) { value.OverSpeed = 0; return; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(pos.x - lastPos.x, 0f, pos.z - lastPos.z); float num2 = ((Vector3)(ref val)).magnitude / num; float num3 = CeilingFor(p, value) * Mathf.Max(1f, Plugin.SlackLine.Value); if (num3 > 0f && num2 > num3) { value.OverSpeed++; if (value.OverSpeed >= Plugin.SpeedSamples.Value && Ready(value, realtimeSinceStartup)) { value.LastFinding = realtimeSinceStartup; value.OverSpeed = 0; Guard.Report(conn, "UpdatePlayerPosRot", Verdict.Strong("speed", $"moving at {num2:0} u/s over {Plugin.SpeedSamples.Value} samples (ceiling {num3:0} incl. slack)")); } } else { value.OverSpeed = 0; } } private static bool Ready(Track t, float now) { return now - t.LastFinding >= Plugin.MovementFindingCooldown.Value; } private static float CeilingFor(Player p, Track t) { try { PlayerMovement movement = p.Movement; if ((Object)(object)movement == (Object)null) { return Plugin.FallbackSpeedCeiling.Value; } if (t.SprintSpeed == null) { t.SprintSpeed = Traverse.Create((object)movement).Field("_sprintSpeed"); t.WalkSpeed = Traverse.Create((object)movement).Field("_walkSpeed"); } float num = ((t.SprintSpeed != null && t.SprintSpeed.FieldExists()) ? t.SprintSpeed.GetValue() : 0f); if (num > 0.01f) { return num; } } catch { } return Plugin.FallbackSpeedCeiling.Value; } public static void Forget(Player p) { if (!((Object)(object)p == (Object)null)) { _tracks.Remove(p); } } public static void Reset() { _tracks.Clear(); } } internal static class MuteLines { public enum Reason { General, Swearing, Racism, Antisemitism, Political, Shouting } private static readonly string[] General = new string[4] { "{0} is making noises again. The fish have stopped biting.", "Something is mumbling near {0}. Nobody can make it out.", "{0} appears to be talking. The wind takes it.", "A sound comes from {0}'s direction. It is not words." }; private static readonly string[] Swearing = new string[4] { "sorry, I can't speak right now. Mum put chilli powder on my tongue for saying bad words. It BURRRRNS", "rinsing my mouth out, this may take a while", "looking for some different words. still looking", "turns out I know a lot of words and only some of them are allowed" }; private static readonly string[] Racism = new string[4] { "hey guys, I really struggle with racism and I am working on myself", "I am on a journey. it is going badly but I am on it", "my views are under review. so am I", "I have enrolled myself in a course. attendance is mandatory" }; private static readonly string[] Antisemitism = new string[3] { "the Holocaust was a terrible tragedy and I hope we never see anything like it again", "I have reconsidered and I would like to apologise properly", "that was indefensible and I am not going to try to defend it" }; private static readonly string[] Political = new string[4] { "I am here to fish and I have decided to do only that", "nobody came to this dock for my opinions", "I am going to keep it to the fish from now on", "you know what, not the time, not the place, not the lake" }; private static readonly string[] Shouting = new string[3] { "I found my indoor voice, it was behind the sofa", "I am aware of the volume. I am working on the volume", "taking it down several notches, my apologies" }; private static int _pick; public static string Name(Reason r) { return r switch { Reason.Swearing => "swearing", Reason.Racism => "racism", Reason.Antisemitism => "antisemitism", Reason.Political => "politics", Reason.Shouting => "shouting", _ => "noise", }; } public static string For(Reason r, string who) { string[] array = Set(r); return string.Format(array[_pick++ % array.Length], who); } private static string[] Set(Reason r) { return r switch { Reason.Swearing => Swearing, Reason.Racism => Racism, Reason.Antisemitism => Antisemitism, Reason.Political => Political, Reason.Shouting => Shouting, _ => General, }; } } internal static class NativeAudit { private const int FaultLimit = 40; private const int LineLimit = 800; private const int HitLimit = 60; private static GameObject _pending; private static string _tag; private static int _wait; private static int _lines; private static readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4]; public static void Request(GameObject root, string tag) { _pending = root; _tag = tag; _wait = 2; } public static void Tick() { if ((Object)(object)_pending == (Object)null || _wait-- > 0) { return; } GameObject pending = _pending; _pending = null; if ((Object)(object)pending == (Object)null) { return; } try { Canvas.ForceUpdateCanvases(); Report(pending, _tag); } catch (Exception ex) { Plugin.Log.LogError((object)("[Audit] could not measure: " + ex)); } } private static void Report(GameObject root, string tag) { List list = new List(); Plugin.Log.LogInfo((object)("[Audit] ---- " + tag + " ----")); Plugin.Log.LogInfo((object)("[Audit] screen " + Screen.width + "x" + Screen.height + Canvasing(root))); Plugin.Log.LogInfo((object)"[Audit] name [screen x,y w,h] layout | components"); _lines = 0; Walk(root.transform, 0, list); if (_lines >= 800) { Plugin.Log.LogInfo((object)("[Audit] (tree truncated at " + 800 + " lines - faults below are still counted in full.)")); } Capabilities(root, list); if (list.Count == 0) { Plugin.Log.LogInfo((object)"[Audit] no overlaps, no overflow, no zero-sized boxes, and every control can be reached and clicked."); } else { Plugin.Log.LogWarning((object)("[Audit] " + list.Count + " fault(s):")); for (int i = 0; i < list.Count && i < 40; i++) { Plugin.Log.LogWarning((object)("[Audit] " + list[i])); } if (list.Count > 40) { Plugin.Log.LogWarning((object)("[Audit] ... and " + (list.Count - 40) + " more, most of them the same fault on another control.")); } } Plugin.Log.LogInfo((object)("[Audit] ---- end " + tag + " ----")); } private static void Capabilities(GameObject root, List faults) { //IL_0066: 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_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_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_038c: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Expected O, but got Unknown //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) Appearance(root, faults); Plugin.Log.LogInfo((object)"[Audit] -- what can actually be used --"); ScrollRect[] componentsInChildren = root.GetComponentsInChildren(true); foreach (ScrollRect val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.content == (Object)null) && !((Object)(object)val.viewport == (Object)null)) { Rect rect = val.viewport.rect; float height = ((Rect)(ref rect)).height; rect = val.content.rect; float height2 = ((Rect)(ref rect)).height; float num = height2 - height; float verticalNormalizedPosition = val.verticalNormalizedPosition; Plugin.Log.LogInfo((object)("[Audit] scroll " + Path(((Component)val).transform) + " viewport " + height.ToString("0") + " content " + height2.ToString("0") + ((num > 1f) ? (" hidden " + num.ToString("0")) : " fits") + " parked at " + verticalNormalizedPosition.ToString("0.00") + " (1 = top) bar " + (((Object)(object)val.verticalScrollbar == (Object)null) ? "MISSING" : (((Component)val.verticalScrollbar).gameObject.activeInHierarchy ? "shown" : "hidden")) + " wheel " + (((Object)(object)((Component)val).GetComponent() != (Object)null) ? "ours" : "ScrollRect"))); if (num > 1f && (Object)(object)val.verticalScrollbar == (Object)null && (Object)(object)((Component)val).GetComponent() == (Object)null) { faults.Add(Path(((Component)val).transform) + ": " + num.ToString("0") + "px hidden and neither a scrollbar nor a wheel handler to reach it with."); } if (num > 1f && verticalNormalizedPosition < 0.01f) { faults.Add(Path(((Component)val).transform) + ": opens at the BOTTOM with " + num.ToString("0") + "px above the view - everything in this column is off the top of the screen."); } } } EventSystem current = EventSystem.current; if ((Object)(object)current == (Object)null) { faults.Add("no EventSystem - nothing on the sheet can be clicked at all."); return; } int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; List list = new List(); Selectable[] componentsInChildren2 = root.GetComponentsInChildren(true); Vector2 val5 = default(Vector2); foreach (Selectable val2 in componentsInChildren2) { if ((Object)(object)val2 == (Object)null) { continue; } if (!((Component)val2).gameObject.activeInHierarchy || !val2.interactable) { num5++; continue; } Transform transform = ((Component)val2).transform; RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val3 == (Object)null) { num5++; continue; } Rect val4 = ScreenRect(val3); ((Vector2)(ref val5))..ctor(((Rect)(ref val4)).x + ((Rect)(ref val4)).width * 0.5f, ((Rect)(ref val4)).y + ((Rect)(ref val4)).height * 0.5f); if (val5.x < 0f || val5.x > (float)Screen.width || val5.y < 0f || val5.y > (float)Screen.height) { num3++; } else if (Clipped(val3, val5)) { num3++; } else { if (num6++ >= 60) { continue; } list.Clear(); current.RaycastAll(new PointerEventData(current) { position = val5 }, list); object obj; if (list.Count <= 0) { obj = null; } else { RaycastResult val6 = list[0]; obj = ((RaycastResult)(ref val6)).gameObject; } GameObject val7 = (GameObject)obj; if ((Object)(object)val7 != (Object)null && ((Object)(object)val7 == (Object)(object)((Component)val2).gameObject || val7.transform.IsChildOf(((Component)val2).transform))) { num2++; continue; } num4++; if (num4 <= 6) { faults.Add(Path(((Component)val2).transform) + ": a click at its centre lands on " + (((Object)(object)val7 == (Object)null) ? "nothing" : Quote(((Object)val7).name)) + " instead."); } } } Plugin.Log.LogInfo((object)("[Audit] controls: " + num2 + " clickable, " + num4 + " blocked, " + num3 + " off-screen (scroll to reach), " + num5 + " inactive.")); if (num3 > 0 && num2 == 0) { faults.Add("every control is off-screen - the sheet is built but none of it can be reached where it is parked."); } } private static void Appearance(GameObject root, List faults) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) string text = ((Plugin.Backdrop != null) ? (Plugin.Backdrop.Value ?? "") : ""); Image component = root.GetComponent(); string text2 = (((Object)(object)component != (Object)null && (Object)(object)((Graphic)component).material != (Object)null && (Object)(object)((Graphic)component).material != (Object)(object)Graphic.defaultGraphicMaterial) ? ((Object)((Graphic)component).material).name : null); Transform val = root.transform.Find("Tint"); Image val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); Plugin.Log.LogInfo((object)("[Audit] appearance: tint " + (((Object)(object)val2 != (Object)null) ? Say(((Graphic)val2).color) : "MISSING") + " backdrop " + (string.IsNullOrWhiteSpace(text) ? "ours (no material)" : ("'" + text + "'")) + " material " + (text2 ?? "none") + " root " + (((Object)(object)component != (Object)null) ? Say(((Graphic)component).color) : "none"))); if ((Object)(object)val2 == (Object)null) { faults.Add("the sheet has no Tint layer - whatever colour it is, it is not the one Theme decides."); } else if (!Near(((Graphic)val2).color, Theme.Glass)) { faults.Add("the sheet's tint is " + Say(((Graphic)val2).color) + " but Theme.Glass is " + Say(Theme.Glass) + " - something is painting over the palette."); } if (string.IsNullOrWhiteSpace(text) && text2 != null) { faults.Add("the sheet borrowed '" + text2 + "' but Interface/Backdrop is empty - nothing should have been borrowed at all."); } if (text2 == null && (Object)(object)component != (Object)null && ((Graphic)component).color.a > 0.02f) { faults.Add("the sheet's base is " + Say(((Graphic)component).color) + " with no material - it should be fully transparent and let the Tint do the work."); } if (!string.IsNullOrWhiteSpace(text) && text2 == null) { Plugin.Log.LogInfo((object)("[Audit] (backdrop '" + text + "' was asked for and is not in this scene - plain sheet, by design.)")); } } private static bool Near(Color a, Color b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0038: 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) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Abs(a.r - b.r) < 0.02f && Mathf.Abs(a.g - b.g) < 0.02f && Mathf.Abs(a.b - b.b) < 0.02f) { return Mathf.Abs(a.a - b.a) < 0.02f; } return false; } private static string Say(Color c) { return "rgba(" + c.r.ToString("0.##") + "," + c.g.ToString("0.##") + "," + c.b.ToString("0.##") + "," + c.a.ToString("0.##") + ")"; } private static string Canvasing(GameObject root) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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) Canvas componentInParent = root.GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null) { return " (no canvas!)"; } string text = " canvas scale " + componentInParent.scaleFactor.ToString("0.###") + " order " + componentInParent.sortingOrder; CanvasScaler component = ((Component)componentInParent).GetComponent(); if ((Object)(object)component != (Object)null) { text = text + " scaler " + ((object)component.uiScaleMode/*cast due to .constrained prefix*/).ToString() + " ref " + component.referenceResolution.x.ToString("0") + "x" + component.referenceResolution.y.ToString("0") + " match " + component.matchWidthOrHeight.ToString("0.##"); } return text; } private static void Walk(Transform t, int depth, List faults) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if (depth > 8) { return; } RectTransform rt = (RectTransform)(object)((t is RectTransform) ? t : null); string text = new string(' ', depth * 2); Rect r = ScreenRect(rt); if (_lines++ < 800) { Plugin.Log.LogInfo((object)("[Audit] " + text + ((Object)t).name + " [" + ((Rect)(ref r)).x.ToString("0") + "," + ((Rect)(ref r)).y.ToString("0") + " " + ((Rect)(ref r)).width.ToString("0") + "x" + ((Rect)(ref r)).height.ToString("0") + "] " + Layouting(t) + " | " + string.Join(", ", (from c in ((Component)t).GetComponents() where (Object)(object)c != (Object)null select ((object)c).GetType().Name).ToArray()))); } Check(t, rt, r, faults); for (int num = 0; num < t.childCount; num++) { Walk(t.GetChild(num), depth + 1, faults); } } private static string Layouting(Transform t) { //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_0469: Unknown result type (might be due to invalid IL or missing references) //IL_046e: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_048f: Unknown result type (might be due to invalid IL or missing references) //IL_04ab: Unknown result type (might be due to invalid IL or missing references) //IL_04b0: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) List list = new List(); HorizontalOrVerticalLayoutGroup component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null) { list.Add(((component is VerticalLayoutGroup) ? "VLG" : "HLG") + " space " + component.spacing.ToString("0.#") + " pad " + ((LayoutGroup)component).padding.left + "/" + ((LayoutGroup)component).padding.right + "/" + ((LayoutGroup)component).padding.top + "/" + ((LayoutGroup)component).padding.bottom + " ctrl " + (component.childControlWidth ? "W" : "-") + (component.childControlHeight ? "H" : "-") + " expand " + (component.childForceExpandWidth ? "W" : "-") + (component.childForceExpandHeight ? "H" : "-")); } LayoutElement[] components = ((Component)t).GetComponents(); foreach (LayoutElement val in components) { list.Add("LE min " + val.minWidth.ToString("0.#") + "/" + val.minHeight.ToString("0.#") + " pref " + val.preferredWidth.ToString("0.#") + "/" + val.preferredHeight.ToString("0.#") + " flex " + val.flexibleWidth.ToString("0.##") + "/" + val.flexibleHeight.ToString("0.##") + (val.ignoreLayout ? " IGNORED" : "")); } ContentSizeFitter component2 = ((Component)t).GetComponent(); if ((Object)(object)component2 != (Object)null) { list.Add("CSF h:" + ((object)component2.horizontalFit/*cast due to .constrained prefix*/).ToString() + " v:" + ((object)component2.verticalFit/*cast due to .constrained prefix*/).ToString()); } Graphic component3 = ((Component)t).GetComponent(); if ((Object)(object)component3 != (Object)null && !(component3 is TMP_Text)) { bool flag = (Object)(object)component3.material != (Object)null && (Object)(object)component3.material != (Object)(object)Graphic.defaultGraphicMaterial; list.Add("rgba " + component3.color.r.ToString("0.##") + "," + component3.color.g.ToString("0.##") + "," + component3.color.b.ToString("0.##") + "," + component3.color.a.ToString("0.##") + (flag ? (" mat:" + ((Object)component3.material).name) : "") + (component3.raycastTarget ? "" : " noraycast")); } TMP_Text component4 = ((Component)t).GetComponent(); if ((Object)(object)component4 != (Object)null) { list.Add("TMP font " + component4.fontSize.ToString("0.#") + (component4.enableAutoSizing ? (" auto " + component4.fontSizeMin.ToString("0.#") + "-" + component4.fontSizeMax.ToString("0.#")) : "") + " " + ((object)component4.alignment/*cast due to .constrained prefix*/).ToString() + " wrap:" + ((object)component4.textWrappingMode/*cast due to .constrained prefix*/).ToString() + " over:" + ((object)component4.overflowMode/*cast due to .constrained prefix*/).ToString() + " pref " + component4.preferredWidth.ToString("0") + "x" + component4.preferredHeight.ToString("0") + " " + Quote(component4.text)); } if (list.Count != 0) { return string.Join(" ", list.ToArray()); } return "-"; } private static void Check(Transform t, RectTransform rt, Rect r, List faults) { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) string text = Path(t); LayoutElement[] components = ((Component)t).GetComponents(); if (components.Length > 1) { faults.Add(text + ": " + components.Length + " LayoutElements on one object - Unity takes the largest, so the value set in code is not necessarily the one in force."); } if ((Object)(object)rt != (Object)null && !Ignorable(t)) { if (((Rect)(ref r)).width <= 0.5f || ((Rect)(ref r)).height <= 0.5f) { faults.Add(text + ": no size (" + ((Rect)(ref r)).width.ToString("0.#") + "x" + ((Rect)(ref r)).height.ToString("0.#") + ")."); } Transform parent = t.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val != (Object)null && HasGroup(t.parent)) { string text2 = Escapes(r, ScreenRect(val)); if (text2 != null) { faults.Add(text + ": hangs outside its parent " + text2 + " - this is what gets cut off."); } } } if ((Object)(object)((Component)t).GetComponent() != (Object)null) { return; } List list = new List(); for (int i = 0; i < t.childCount; i++) { Transform child = t.GetChild(i); RectTransform val2 = (RectTransform)(object)((child is RectTransform) ? child : null); if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeInHierarchy && !Ignorable(((Component)val2).transform)) { list.Add(val2); } } for (int j = 0; j < list.Count; j++) { for (int k = j + 1; k < list.Count; k++) { Rect val3 = ScreenRect(list[j]); Rect val4 = ScreenRect(list[k]); float num = Mathf.Min(((Rect)(ref val3)).xMax, ((Rect)(ref val4)).xMax) - Mathf.Max(((Rect)(ref val3)).xMin, ((Rect)(ref val4)).xMin); float num2 = Mathf.Min(((Rect)(ref val3)).yMax, ((Rect)(ref val4)).yMax) - Mathf.Max(((Rect)(ref val3)).yMin, ((Rect)(ref val4)).yMin); if (!(num <= 1f) && !(num2 <= 1f) && !Inside(val3, val4) && !Inside(val4, val3)) { faults.Add(text + ": " + Quote(((Object)list[j]).name) + " and " + Quote(((Object)list[k]).name) + " overlap by " + num.ToString("0") + "x" + num2.ToString("0") + "px."); } } } } private static bool Inside(Rect inner, Rect outer) { if (((Rect)(ref inner)).xMin >= ((Rect)(ref outer)).xMin - 1f && ((Rect)(ref inner)).xMax <= ((Rect)(ref outer)).xMax + 1f && ((Rect)(ref inner)).yMin >= ((Rect)(ref outer)).yMin - 1f) { return ((Rect)(ref inner)).yMax <= ((Rect)(ref outer)).yMax + 1f; } return false; } private static string Escapes(Rect r, Rect p) { List list = new List(); if (((Rect)(ref r)).xMin < ((Rect)(ref p)).xMin - 1f) { list.Add("left by " + (((Rect)(ref p)).xMin - ((Rect)(ref r)).xMin).ToString("0")); } if (((Rect)(ref r)).xMax > ((Rect)(ref p)).xMax + 1f) { list.Add("right by " + (((Rect)(ref r)).xMax - ((Rect)(ref p)).xMax).ToString("0")); } if (((Rect)(ref r)).yMin < ((Rect)(ref p)).yMin - 1f) { list.Add("bottom by " + (((Rect)(ref p)).yMin - ((Rect)(ref r)).yMin).ToString("0")); } if (((Rect)(ref r)).yMax > ((Rect)(ref p)).yMax + 1f) { list.Add("top by " + (((Rect)(ref r)).yMax - ((Rect)(ref p)).yMax).ToString("0")); } if (list.Count != 0) { return "(" + string.Join(", ", list.ToArray()) + "px)"; } return null; } private static bool Ignorable(Transform t) { if ((Object)(object)t == (Object)null) { return true; } if ((Object)(object)((Component)t).GetComponent() != (Object)null) { return true; } if (((Object)t).name.StartsWith("TMP SubMesh")) { return true; } LayoutElement component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null) { return component.ignoreLayout; } return false; } private static bool Clipped(RectTransform rt, Vector2 point) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)rt).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); while ((Object)(object)val != (Object)null) { if (!((Object)(object)((Component)val).GetComponent() == (Object)null) || !((Object)(object)((Component)val).GetComponent() == (Object)null)) { Rect val2 = ScreenRect(val); if (!((Rect)(ref val2)).Contains(point)) { return true; } } Transform parent2 = ((Transform)val).parent; val = (RectTransform)(object)((parent2 is RectTransform) ? parent2 : null); } return false; } private static bool HasGroup(Transform t) { if ((Object)(object)t != (Object)null) { return (Object)(object)((Component)t).GetComponent() != (Object)null; } return false; } private static Rect ScreenRect(RectTransform rt) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rt == (Object)null) { return new Rect(0f, 0f, 0f, 0f); } rt.GetWorldCorners(_corners); Vector3 val = _corners[0]; Vector3 val2 = _corners[2]; return new Rect(val.x, val.y, val2.x - val.x, val2.y - val.y); } private static string Path(Transform t) { string text = ((Object)t).name; Transform parent = t.parent; for (int i = 0; i < 2; i++) { if (!((Object)(object)parent != (Object)null)) { break; } text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private static string Quote(string s) { if (string.IsNullOrEmpty(s)) { return "\"\""; } s = s.Replace("\r", "").Replace("\n", "\\n"); if (s.Length > 28) { s = s.Substring(0, 28) + "..."; } return "\"" + s + "\""; } } internal static class NativeConsole { private enum Tab { Warden, Settings, Tweaks } private static GameObject _root; private static Transform _whoCol; private static Transform _wardenCol; private static Transform _toolCol; private static Transform _wardenInner; private static Transform _toolInner; private static Transform _guardCol; private static Transform _whoInner; private static Transform _guardInner; private static Transform _setInner; private static Transform _railRow; private static ScrollRect _guardScroll; private static TMP_InputField _search; private static string _spawnQuery = ""; private static bool _refocus; private static int _spawnPage; private static bool _fresh = true; private static bool _showRules; private static float _searchSettles; private static Tab _tab = Tab.Warden; private static Tab _builtTab = Tab.Tweaks; private static Transform _tabRow; private static Transform _body; private static readonly List _scrolls = new List(); private static readonly List _keep = new List(); private static readonly Transform[] _tools = (Transform[])(object)new Transform[3]; private static readonly float[] _fill = new float[3]; private static int _at; private static ScrollRect _whoScroll; private static ScrollRect _wardenScroll; private static ScrollRect _toolScroll; private static Transform _levelRow; private static TMP_Text _title; private static TMP_Text _dossier; private static TMP_Text _toast; private static float _toastUntil; private static float _nextRefresh; private static ulong _selected; private const int PageSize = 40; private const int SearchMax = 25; private static bool _dirty; private static ulong _builtFor = ulong.MaxValue; private static int _builtPlayers = -1; private static string _armed; private static float _armedUntil; public static bool IsOpen { get; private set; } public static bool Open() { if (!NativeUI.Prepare()) { return false; } if (IsOpen) { return true; } try { Build(); IsOpen = true; NativeAudit.Request(_root, "console layout"); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[NativeConsole] could not build, falling back to the old one: " + ex)); Close(); return false; } } public static void Close() { IsOpen = false; if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = null; _whoCol = (_wardenCol = (_toolCol = (_levelRow = (_railRow = null)))); _wardenInner = (_toolInner = null); _tabRow = (_body = null); _scrolls.Clear(); _keep.Clear(); for (int i = 0; i < _tools.Length; i++) { _tools[i] = null; _fill[i] = 0f; } _search = null; _refocus = false; _fresh = true; _searchSettles = 0f; _whoScroll = (_wardenScroll = (_toolScroll = null)); _title = (_dossier = (_toast = null)); NativeUI.OnHover = null; NativeUI.ForgetLabels(); _builtFor = ulong.MaxValue; _builtPlayers = -1; _dirty = false; } public static void Toast(string msg) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_toast == (Object)null)) { NativeUI.Say(_toast, msg ?? ""); ((Graphic)_toast).color = Theme.Ink; _toastUntil = Time.realtimeSinceStartup + 6f; } } private static void Hint(string text) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_toast == (Object)null) && !string.IsNullOrEmpty(text) && !(_toastUntil > Time.realtimeSinceStartup)) { NativeUI.Say(_toast, text); ((Graphic)_toast).color = Theme.Muted; } } private static void Build() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) _root = NativeUI.Sheet("EndersGambit", 0.9f, 0.88f); VerticalLayoutGroup obj = _root.AddComponent(); ((LayoutGroup)obj).padding = new RectOffset(22, 22, 18, 16); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; _title = NativeUI.Label(_root.transform, "Fish Warden", Vector2.zero); Fix(_title, 36f, (TextAlignmentOptions)513); _railRow = Strip(_root.transform, "Rail", 0f).transform; _levelRow = Strip(_root.transform, "Levels", 34f).transform; _tabRow = Strip(_root.transform, "Tabs", 34f).transform; GameObject obj2 = Strip(_root.transform, "Body", 0f); LayoutElement component = obj2.GetComponent(); component.preferredHeight = 0f; component.flexibleHeight = 1f; _body = obj2.transform; _toast = NativeUI.Label(_root.transform, "Point at anything to see what it does.", Vector2.zero); Fix(_toast, 40f, (TextAlignmentOptions)513); ((Graphic)_toast).color = Theme.Muted; NativeUI.OnHover = Hint; BuildLevels(); BuildTabs(); Refresh(rebuild: true); } private static void BuildTabs() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Clear(_tabRow); Page("WARDEN", Tab.Warden, Theme.EdgeWarden, "Everyone in the lobby and what you can do about them. This is the tab for when something is happening."); Page("SETTINGS", Tab.Settings, Theme.EdgeWarden, "How the guard behaves - what a finding costs, which checks run, how big the lobby is. Set once, then forgotten."); Page("ENDER'S TWEAKS", Tab.Tweaks, Theme.EdgeTweaks, "Your own kit. None of this touches anyone else's game."); } private static void Page(string label, Tab which, Color edge, string hint) { //IL_004b: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) bool flag = _tab == which; GameObject val = NativeUI.Button(_tabRow, (flag ? "✓ " : " ") + label, delegate { if (_tab != which) { _tab = which; Mark(); } }, new Vector2(0f, 40f), Vector2.zero, hint); (val.GetComponent() ?? val.AddComponent()).flexibleWidth = 1f; NativeUI.Mark(val, flag); NativeUI.Edge(val, flag ? edge : Theme.EdgeIdle); } private static GameObject Strip(Transform parent, string name, float height) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(HorizontalLayoutGroup), typeof(LayoutElement) }); val.transform.SetParent(parent, false); HorizontalLayoutGroup component = val.GetComponent(); ((HorizontalOrVerticalLayoutGroup)component).spacing = 14f; ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false; LayoutElement component2 = val.GetComponent(); if (height > 0f) { component2.preferredHeight = height; component2.flexibleHeight = 0f; } return val; } private static Transform Column(Transform parent, string title, float weight, out ScrollRect scroll) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: 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) GameObject val = new GameObject(title, new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(LayoutElement) }); val.transform.SetParent(parent, false); VerticalLayoutGroup component = val.GetComponent(); ((HorizontalOrVerticalLayoutGroup)component).spacing = 4f; ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = true; LayoutElement component2 = val.GetComponent(); component2.flexibleWidth = weight; component2.preferredWidth = 0f; if (!string.IsNullOrEmpty(title)) { NativeUI.Header(val.transform, title); TMP_Text val2 = NativeUI.Label(val.transform, Blurb(title), Vector2.zero); if ((Object)(object)val2 != (Object)null) { ((Graphic)val2).color = Theme.Faint; Fix(val2, 34f, (TextAlignmentOptions)257); } } if (title == "ON THIS PERSON") { _dossier = NativeUI.Label(val.transform, "", Vector2.zero); Fix(_dossier, 116f, (TextAlignmentOptions)257); } Transform val3 = NativeUI.Scroll(val.transform, 0f); scroll = ((Component)val3.parent).GetComponent(); _scrolls.Add(scroll); return val3; } private static string Blurb(string title) { return title switch { "WHO IS HERE" => "Everyone in the lobby, most-caught first. Click somebody to work on them.", "ON THIS PERSON" => "What they have done, and what you can do about it.", "THE GUARD" => "How it behaves, what it has seen, and the lobby as a whole.", "ENDER'S TWEAKS" => "Your own kit. None of this touches anyone else's game.", _ => "Settings for how the guard behaves.", }; } private static void BuildLevels() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) Clear(_levelRow); foreach (Level value in Enum.GetValues(typeof(Level))) { Level mine = value; bool flag = Policy.Matches(value); GameObject val = NativeUI.Button(_levelRow, (flag ? "✓ " : " ") + Policy.Name(value).ToUpperInvariant(), delegate { Plugin.SetLevel(mine); Mark(); }, new Vector2(0f, 40f), Vector2.zero, Policy.Explain(value)); (val.GetComponent() ?? val.AddComponent()).flexibleWidth = 1f; NativeUI.Mark(val, flag); NativeUI.Edge(val, flag ? Theme.EdgeLive : Theme.EdgeIdle); } } private static void Mark() { _dirty = true; } public static void Tick() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (IsOpen && !((Object)(object)_root == (Object)null)) { NativeAudit.Tick(); if (_searchSettles > 0f && Time.realtimeSinceStartup >= _searchSettles) { _searchSettles = 0f; _refocus = true; Mark(); } if ((Object)(object)_toast != (Object)null && _toastUntil > 0f && Time.realtimeSinceStartup > _toastUntil) { NativeUI.Say(_toast, "Point at anything to see what it does."); ((Graphic)_toast).color = Theme.Muted; _toastUntil = 0f; } NativeUI.HoldLabels(); int count = Others().Count; if (_dirty || _selected != _builtFor || count != _builtPlayers) { _dirty = false; Refresh(rebuild: true); } else if (!(Time.realtimeSinceStartup < _nextRefresh)) { _nextRefresh = Time.realtimeSinceStartup + 1f; Refresh(rebuild: false); } } } private static void Refresh(bool rebuild) { List list = Others(); if (rebuild) { _builtFor = _selected; _builtPlayers = list.Count; bool num = !_fresh && _builtTab == _tab; _keep.Clear(); if (num) { foreach (ScrollRect scroll in _scrolls) { _keep.Add(Where(scroll)); } } _fresh = false; _builtTab = _tab; NativeUI.ForgetLabels(); Clear(_body); _scrolls.Clear(); _whoCol = (_wardenCol = (_toolCol = null)); _wardenInner = (_toolInner = null); if (_tab == Tab.Warden) { BuildWardenTab(list); } else if (_tab == Tab.Settings) { BuildSettingsTab(); } else { BuildTweaksTab(); } BuildLevels(); BuildTabs(); BuildRail(); Canvas.ForceUpdateCanvases(); for (int i = 0; i < _scrolls.Count && i < _keep.Count; i++) { Restore(_scrolls[i], _keep[i]); } if (_refocus && (Object)(object)_search != (Object)null) { _refocus = false; _search.ActivateInputField(); _search.caretPosition = _search.text.Length; } } UpdateDossier(); if ((Object)(object)_title != (Object)null) { string text = Actions.Status(); NativeUI.Say(_title, "Fish Warden build " + Plugin.BuiltAt() + " - " + Policy.CurrentName() + (string.IsNullOrEmpty(text) ? "" : (" - " + text))); } } private static GameObject Urgent(Transform parent, string label, string hint, Func run, bool loud) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_0094: Unknown result type (might be due to invalid IL or missing references) GameObject val = NativeUI.Button(parent, label, delegate { string text = run(); Plugin.Log.LogInfo((object)("[Console] rail \"" + label + "\" -> " + (text ?? "(no message)"))); Toast(text); Mark(); }, new Vector2(0f, 40f), Vector2.zero, hint); (val.GetComponent() ?? val.AddComponent()).flexibleWidth = (loud ? 1.6f : 1f); NativeUI.Back(val, loud ? Theme.RowOn : Theme.Row); NativeUI.Edge(val, loud ? Theme.EdgeGrave : Theme.EdgeWarden); NativeUI.Paint(val, Theme.Ink); return val; } private static void BuildRail() { Clear(_railRow); LayoutElement component = ((Component)_railRow).GetComponent(); int num = 0; foreach (Cases.Case item in Cases.Open.ToList()) { int id = item.Id; string name = item.Name; Urgent(_railRow, "REMOVE " + name.ToUpperInvariant() + " (" + item.Strikes + ")", "What the guard caught: " + ((item.Rules.Count == 0) ? "nothing named" : string.Join(", ", item.Rules.ToArray())) + (item.Proven ? ". Proven, not guessed." : ". Suspicious rather than proven."), () => Cases.Accept(id), loud: true); Urgent(_railRow, "let " + name + " off", "Clears their strikes and undoes the punishment, and marks the rule as having produced a false alarm so a noisy one can be spotted.", () => Cases.Dismiss(id), loud: false); num += 2; if (num >= 6) { break; } } if (Safety.Tripped) { Urgent(_railRow, "GUARD STOOD DOWN - put it back", "Too many different people were flagged at once, so the guard decided the rules were wrong rather than the lobby and stopped enforcing. Nothing is being blocked until you re-arm it.", delegate { Safety.Rearm(); return "Re-armed."; }, loud: true); num++; } int disabledCount = Rules.DisabledCount; if (disabledCount > 0 && num < 8) { Urgent(_railRow, disabledCount + " check(s) switched off", "Either you switched them off, or one fired so often the guard decided it was broken. Opens SETTINGS, where they can go back on.", delegate { _tab = Tab.Settings; Mark(); return "Every check is in SETTINGS."; }, loud: false); num++; } component.preferredHeight = ((num == 0) ? 0f : 40f); component.flexibleHeight = 0f; ((Component)_railRow).gameObject.SetActive(num > 0); } private static void BuildWardenTab(List players) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) _whoCol = Column(_body, "WHO IS HERE", 0.8f, out _whoScroll); _wardenCol = Column(_body, "ON THIS PERSON", 1.15f, out _wardenScroll); _guardCol = Column(_body, "THE GUARD", 1.05f, out _guardScroll); _whoInner = NativeUI.Group(_whoCol, Theme.EdgeWarden); _wardenInner = NativeUI.Group(_wardenCol, Theme.EdgeWarden); _guardInner = NativeUI.Group(_guardCol, Theme.EdgeWarden); BuildDecide(); BuildWho(players); BuildWarden(); BuildGuard(); } private static void BuildDecide() { } private static void BuildTweaksTab() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) string text = "ENDER'S TWEAKS"; for (int i = 0; i < _tools.Length; i++) { ScrollRect scroll; Transform parent = Column(_body, (i == 0) ? text : "", 1f, out scroll); if (i == 0) { _toolScroll = scroll; } _tools[i] = NativeUI.Group(parent, Theme.EdgeTweaks); _fill[i] = 0f; } _fill[0] = 60f; _at = 0; _toolCol = _tools[0]; _toolInner = _tools[0]; BuildTools(); } private static void Section(string title) { if (_tab == Tab.Warden) { NativeUI.Header(_toolInner ?? _guardInner, title); return; } int num = 0; for (int i = 1; i < _tools.Length; i++) { if (_fill[i] < _fill[num]) { num = i; } } _at = num; _toolInner = _tools[num]; _setInner = _tools[num]; _fill[num] += 30f; NativeUI.Header(_tools[num], title); } private static void Grew(Transform parent, float height) { if (_tab == Tab.Warden) { return; } for (int i = 0; i < _tools.Length; i++) { if ((Object)(object)_tools[i] == (Object)(object)parent) { _fill[i] += height; break; } } } private static void BuildWho(List players) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) if (players.Count == 0) { NativeUI.Label(_whoInner, "nobody else in the lobby", Vector2.zero); return; } Dictionary dictionary = new Dictionary(); foreach (Player player in players) { try { dictionary[player.SteamID] = ((!dictionary.TryGetValue(player.SteamID, out var value)) ? 1 : (value + 1)); } catch { } } Dictionary tally = Logbook.Tally(); HashSet hashSet = new HashSet(); foreach (Player item in players.OrderByDescending((Player x) => Strikes(tally, x))) { ulong steamID; try { steamID = item.SteamID; } catch { continue; } if (hashSet.Add(steamID)) { int value2; int num = (tally.TryGetValue(steamID, out value2) ? value2 : 0); int value3; int num2 = ((!dictionary.TryGetValue(steamID, out value3)) ? 1 : value3); string label = item.SteamName + ((num2 > 1) ? (" x" + num2) : "") + ((num > 0) ? (" " + num) : "") + (Troll.Is(steamID) ? " troll" : "") + (Actions.IsMuted(steamID) ? " muted" : "") + (Actions.IsFrozen(steamID) ? " frozen" : ""); ulong target = steamID; string hint = ((num == 0) ? (item.SteamName + " has not been caught doing anything. Click to select them anyway.") : (item.SteamName + " has " + num + " strikes. Click to see what they did and what you can do about it.")); GameObject go = NativeUI.Button(_whoInner, label, delegate { _selected = target; Mark(); }, new Vector2(0f, 40f), Vector2.zero, hint); NativeUI.Mark(go, steamID == _selected); if (steamID != _selected) { NativeUI.Paint(go, Theme.ForStrikes(num, Plugin.KickThreshold.Value)); } } } } private static int Strikes(Dictionary tally, Player p) { try { int value; return tally.TryGetValue(p.SteamID, out value) ? value : 0; } catch { return 0; } } private static void BuildWarden() { Player p = Selected(); if (!((Object)(object)p != (Object)null)) { return; } NativeUI.Header(_wardenInner, "ON " + p.SteamName.ToUpperInvariant()); Charges(p); Act(_wardenInner, Troll.Is(_selected) ? "Stop trolling them" : "Troll mode", "Every rule they break gets its own reply in chat. They are not stopped, they are mocked.", () => Troll.Toggle(p), Troll.Is(_selected)); Act(_wardenInner, Actions.IsFrozen(_selected) ? "Let them move" : "Freeze", "Teleports them back to one spot several times a second. They can press anything they like and end up where they started.", () => Actions.ToggleFreeze(p), Actions.IsFrozen(_selected)); Act(_wardenInner, Actions.IsMuted(_selected) ? "Let them talk" : "Mute", "Drops their chat and their voice for the WHOLE lobby, not just for you. Voice is relayed through you, so nobody hears them.", () => Actions.ToggleMute(p), Actions.IsMuted(_selected)); if (Actions.IsMuted(_selected)) { Act(_wardenInner, "Muted for: " + MuteLines.Name(Actions.ReasonFor(_selected)), "Why they were muted. This picks which lines the lobby sees when they try to talk - swearing, racism, politics and so on.", () => Actions.CycleReason(p)); } Act(_wardenInner, "Take their things", "Drops everything they are carrying on the floor where they stand.", () => Actions.Strip(p)); Act(_wardenInner, "Remove their duplicates", "Despawns their spare bodies but leaves the one they are playing.", () => Actions.Purge(p)); Act(_wardenInner, "Blow up when they attack", "Next time they shoot or punch, it goes off in their hands.", () => Extras.Blowback(p), Extras.HasBlowback(_selected)); Act(_wardenInner, "Worthless catches", "Everything they reel in sells for nothing.", () => Extras.Skunk(p), Extras.IsSkunked(_selected)); Act(_wardenInner, Extras.IsButterfingers(_selected) ? "Let them hold things" : "Butterfingers", "They drop whatever they pick up, over and over. Useless to a cheat and very funny to watch.", () => Extras.Butterfingers(p), Extras.IsButterfingers(_selected)); Act(_wardenInner, Extras.InCooler(_selected) ? "Let them up" : "Keep knocking them down", "Puts them on the floor again every time they stand up.", () => (!Extras.InCooler(_selected)) ? Extras.Cool(p) : Extras.Uncool(p), Extras.InCooler(_selected)); Act(_wardenInner, "Out to sea", "Puts them a long way out in the water. They have to swim back.", () => Extras.Locker(p)); Act(_wardenInner, "Smite" + Plugin.KeyLabel("Smite"), "A bolt from nowhere. Knocks them down hard, from here rather than by looking at them.", () => Smite.Strike(p)); if (Rollback.HasDamage(_selected)) { Act(_wardenInner, "Undo their damage", "Puts back what they broke, spawned or handed out while they were cheating.", () => Rollback.Gut(_selected)); } Grave(_wardenInner, "REMOVE FROM LOBBY", "Remove " + p.SteamName + "?", "Kicks them now. If bans are on they cannot come back.", delegate { int value; int strikes = (Logbook.Tally().TryGetValue(_selected, out value) ? value : 0); string steamName = p.SteamName; Guard.RemoveNow(((NetworkBehaviour)p).Owner, strikes, "removed by hand"); _selected = 0uL; return steamName + " removed."; }); Act(_wardenInner, Crew.Has(_selected) ? "Revoke their fishing license" : "Issue a fishing license", "A license is the standing you have: no rule is run against them at all, so they can use whatever they like without being flagged, blocked, punished or removed, and they still get in during lockdown. The lobby is told when it is given and when it is taken back - a silent exemption looks exactly like a broken guard. It does not survive a restart.", () => Crew.Toggle(p), Crew.Has(_selected)); Act(_wardenInner, "Pardon", "Wipes their strikes and undoes every punishment. Use this when the guard was wrong.", delegate { Logbook.Pardon(_selected); Rollback.Clear(_selected); Safety.Forget(_selected); Extras.Release(_selected); return p.SteamName + " pardoned."; }); } private static void BuildGuard() { NativeUI.Header(_guardInner, "HOW IT BEHAVES"); Act(_guardInner, "Remove on " + Plugin.KickThreshold.Value + " strikes: " + (Plugin.AutoKick.Value ? "yes" : "ask me first"), "Whether the guard kicks on its own once somebody hits the strike limit, or waits and asks you first.", delegate { Plugin.AutoKick.Value = !Plugin.AutoKick.Value; return (!Plugin.AutoKick.Value) ? "You will be asked before anyone is removed." : "Removals happen on their own."; }, Plugin.AutoKick.Value); Act(_guardInner, "Announce findings: " + (Plugin.AnnounceFindings.Value ? "yes" : "quietly"), "Whether the lobby is told in chat when somebody is caught, or whether it only goes to your log.", delegate { Plugin.AnnounceFindings.Value = !Plugin.AnnounceFindings.Value; return (!Plugin.AnnounceFindings.Value) ? "Findings stay in the log." : "Findings are announced in chat."; }, Plugin.AnnounceFindings.Value); Act(_guardInner, "Auto troll: " + AutoTroll.Describe(AutoTroll.Mode), "When troll mode should arm itself without you clicking: never, the moment anything proven lands, after enough strikes, or only for rules you pick.", delegate { AutoTroll.Trigger trigger = ((AutoTroll.Mode != AutoTroll.Trigger.ChosenRules) ? (AutoTroll.Mode + 1) : AutoTroll.Trigger.Off); AutoTroll.SetMode(trigger); return "Troll mode arms itself: " + AutoTroll.Describe(trigger); }, AutoTroll.Mode != AutoTroll.Trigger.Off); if (AutoTroll.Mode == AutoTroll.Trigger.AfterStrikes) { Dial(_guardInner, "Arm troll mode after", Plugin.AutoTrollStrikes.Value, 1f, 40f, delegate(float x) { Plugin.AutoTrollStrikes.Value = Mathf.RoundToInt(x); }, "0", whole: true); Note(_guardInner, "strikes on one person, before troll mode arms itself."); } else if (AutoTroll.Mode == AutoTroll.Trigger.ChosenRules) { List list = AutoTroll.Chosen.ToList(); Note(_guardInner, (list.Count == 0) ? "No rule arms it yet - pick at least one, or nothing will ever fire." : (list.Count + " rule(s) arm it on their own.")); Transform[] array = Tiles(_guardInner, 2); List list2 = Rules.Known.ToList(); for (int num = 0; num < list2.Count; num++) { string name = list2[num]; bool flag = AutoTroll.Arms(name); Act(array[num % array.Length], name.Replace("-", " "), flag ? "Catching this arms troll mode on its own. Click to stop that." : "Click to make this one arm troll mode by itself.", () => AutoTroll.ToggleRule(name), flag); } } Act(_guardInner, "Undo damage on removal: " + (Plugin.GutOnRemoval.Value ? "yes" : "no"), "Whether what somebody spawned, broke or handed out is put back when they are kicked.", delegate { Plugin.GutOnRemoval.Value = !Plugin.GutOnRemoval.Value; return (!Plugin.GutOnRemoval.Value) ? "Their damage stays when they go." : "What they broke is put back when they go."; }, Plugin.GutOnRemoval.Value); Dial(_guardInner, "Strikes before removal", Plugin.KickThreshold.Value, 1f, 20f, delegate(float x) { Plugin.KickThreshold.Value = Mathf.RoundToInt(x); }, "0", whole: true); NativeUI.Header(_guardInner, "THE WHOLE LOBBY"); Act(_guardInner, Actions.Lockdown ? "End lockdown" : "Lock the lobby", Actions.Lockdown ? "Opens the lobby back up to anyone." : "Nobody new gets in except people you have trusted. For when a lobby is being raided.", () => Actions.ToggleLockdown(), Actions.Lockdown); Grave(_guardInner, "Undo what everyone did", "Undo everything, for everyone?", "Takes back everything the guard recorded anyone spawning, inflating or killing this session - not just the person you have selected. Nothing this console spawned is touched; that is the row above.", () => Rollback.GutAll()); Grave(_guardInner, "Put the world back", "Rewrite the whole session?", "The heavier version: every recorded spawn removed, every inflated item returned to its real worth, and everyone they killed put back on their feet. For after a raid rather than after one person.", () => Rollback.RestoreWorld()); Act(_guardInner, HostTools.FriendlyFire ? "Friendly fire: ON" : "Friendly fire: OFF", HostTools.FriendlyFire ? "Players can hurt each other. Punches between people do damage." : "Players CANNOT hurt each other - punching somebody does nothing at all. This is the game's own lobby setting, not the guard blocking anything.", () => HostTools.ToggleFriendlyFire(), HostTools.FriendlyFire); Act(_guardInner, "Lobby holds " + ((Plugin.LobbySize.Value > 0) ? (Plugin.LobbySize.Value + " (raised)") : "whatever the game says"), "Click to raise it to 12, or back to the game's own number. Both gates get raised - FishNet's client cap and the Steam lobby's member limit - because whichever is lower is the real one. Takes effect on this lobby immediately; the game was balanced around its own number, so watch for missing spawn points or boat seats.", delegate { Plugin.LobbySize.Value = ((Plugin.LobbySize.Value <= 0) ? 12 : 0); return (Plugin.LobbySize.Value == 0) ? "Back to the game's own lobby size (needs a new lobby)." : (Lobby.Widen() ?? "Lobby size set to 12."); }, Plugin.LobbySize.Value > 0); Act(_guardInner, "Send the boss away", "Dismisses whatever boss is up, however it got there - somebody summoned it to grief the lobby, or the guard dropped it on them. Goes through the game's own KillBoss, so the health bar and the fight state go with it.", () => Actions.SendBossAway()); Act(_guardInner, "Clear all duplicates", "Despawns every spare body in the lobby. Clone spammers leave copies behind that make names impossible to tell apart.", () => Actions.PurgeAll()); Act(_guardInner, Extras.ChumOut ? "Pick the chum back up" : "Drop chum", "Bait dropped somewhere only a flyer can reach. Anyone who gets to it was not walking.", delegate { if (!Extras.ChumOut) { return Extras.DropChum(); } Extras.ClearChum(); return "Chum picked back up."; }, Extras.ChumOut); } private static void BuildSettingsTab() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) string text = "SETTINGS"; for (int i = 0; i < _tools.Length; i++) { ScrollRect scroll; Transform parent = Column(_body, (i == 0) ? text : "", 1f, out scroll); _tools[i] = NativeUI.Group(parent, Theme.EdgeWarden); _fill[i] = 0f; } _fill[0] = 60f; _at = 0; _setInner = _tools[0]; BuildSettings(); } private static void BuildSettings() { Section("WHAT IT COSTS"); Choice(_setInner, "Proven cheating", Policy.Proven, Policy.SetProven, "Something the vanilla client could not have sent. There is no innocent explanation for these."); Choice(_setInner, "Suspicious", Policy.Suspicious, Policy.SetSuspicious, "Something a laggy or unlucky player could trip. Removing on these is how an innocent stranger gets kicked."); Note(_setInner, "A hunch is only ever written down, whatever these say."); List list = Cases.NoisyRules().ToList(); List list2 = Rules.Disabled.ToList(); if (list.Count > 0 || list2.Count > 0) { Section("RULES"); foreach (string item in list) { string name = item; Act(_setInner, "Stop checking " + name, "This rule has been dismissed more than once, which usually means it is catching something innocent. Switching it off stops the false alarms.", () => (!Rules.Disable(name)) ? (name + " was already off.") : (name + " switched off.")); } foreach (string item2 in list2) { string name2 = item2; Act(_setInner, "Check " + name2 + " again", "Switches this rule back on.", () => (!Rules.Enable(name2)) ? (name2 + " was already on.") : (name2 + " switched back on."), on: true); } } Section("LOOK THINGS UP"); Act(_setInner, "What everyone has tried", "A summary of every rule that has fired this session and who set it off. Goes to chat.", delegate { foreach (string item3 in Summary.Lines()) { Chat.Info(item3); } return "Summary is in the chat."; }); Act(_setInner, "Recent findings", "The last few things the guard wrote down. Goes to chat.", delegate { List list4 = Logbook.Tail(8); if (list4.Count == 0) { return "The logbook is empty."; } foreach (Entry item4 in list4) { Chat.Info(item4.Line); } return list4.Count + " in the chat."; }); Act(_setInner, "Everyone banned (" + Bans.Count + ")", "The trophy wall - everyone the guard has landed, kept across sessions. Goes to chat.", delegate { if (Bans.Count == 0) { return "The trophy wall is empty."; } foreach (Bans.Record item5 in Bans.All.Take(12)) { Chat.Info(" " + item5.Name + " - " + item5.Reason); } return Bans.Count + " on the wall, in the chat."; }); foreach (Bans.Record item6 in Bans.All.Take(6).ToList()) { ulong id = item6.SteamId; string nm = item6.Name; Act(_setInner, "Let " + nm + " back in", "Takes them off the trophy wall so they can rejoin.", delegate { Bans.Remove(id); return nm + " can rejoin."; }); } Act(_setInner, "Save a file for the devs", "Writes everything this session saw to a text file, for reporting a hole in the game rather than a cheat.", delegate { 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()); return "Written to " + text; } catch (Exception ex) { return "Could not write the report: " + ex.Message; } }); Act(_setInner, "Where is everybody", "Writes each player's position, read three different ways, to the log. This answers whether freeze, strip and blowback are reading the right one - the spawn code was reading the wrong one and put everything at the map centre.", () => Body.Survey()); Act(_setInner, "Check the guard is behaving", "Runs the self-test: every rule against known-good and known-bad input, so you can see it is not just quiet.", delegate { foreach (string item7 in SelfTest.Run()) { Chat.Info(item7); } return "Self-test is in the chat."; }); List list3 = Rules.Known.ToList(); int disabledCount = Rules.DisabledCount; Act(_setInner, (_showRules ? "Hide the checks" : ("Every check (" + list3.Count + ")")) + ((disabledCount > 0) ? (" - " + disabledCount + " switched off") : ""), "Each of these is one thing the guard looks for. Switching one off stops that check and nothing else, which is what you want when a single rule is crying wolf.", delegate { _showRules = !_showRules; return (!_showRules) ? "Folded away." : "Showing every check."; }, disabledCount > 0); if (_showRules) { Transform[] array = Tiles(_setInner, 3); for (int num = 0; num < list3.Count; num++) { string name3 = list3[num]; bool on = Rules.Enabled(name3); bool flag = Response.Blocks(name3); bool flag2 = Response.Trolls(name3); Transform parent = array[num % array.Length]; Act(parent, name3.Replace("-", " ") + (on ? (" " + (flag ? "refused" : "watched") + (flag2 ? " + answered" : "")) : ""), on ? ("Checked. " + (flag ? "The packet is refused. " : "Recorded only. ") + (flag2 ? "They get answered for it." : "No response is aimed at them.") + " Click to stop checking it entirely.") : "NOT checked - this is switched off. Click to switch it back on.", delegate { if (!on) { if (!Rules.Enable(name3)) { return name3 + " was already on."; } return name3 + " switched back on."; } return (!Rules.Disable(name3)) ? (name3 + " was already off.") : (name3 + " switched off."); }, on); if (on) { Act(parent, flag ? " refuse it: yes" : " refuse it: no", "Whether the packet is dropped, or the finding is only written down. Refusing costs an innocent player one lost action; it is the cheap half.", () => Response.Toggle(name3, troll: false), flag); Act(parent, flag2 ? " answer them: yes" : " answer them: no", "Whether they get a matched response in front of the lobby - the gun goes off in their hands, they drop what they hold, they end up out to sea. This is the expensive half: it costs them their standing, so it is off by default for anything measured.", () => Response.Toggle(name3, troll: true), flag2); } } } Act(_setInner, Plugin.TraceUnguarded.Value ? "Stop logging everything" : "Log everything, to find a gap", "Writes down every packet the guard has no rule for. This is how a new exploit gets found - it is noisy, so it is off unless you are hunting.", delegate { Plugin.TraceUnguarded.Value = !Plugin.TraceUnguarded.Value; return (!Plugin.TraceUnguarded.Value) ? "Tracing off." : "Tracing. Check the log."; }, Plugin.TraceUnguarded.Value); } private static void BuildTools() { //IL_0acd: Unknown result type (might be due to invalid IL or missing references) //IL_0ae7: Unknown result type (might be due to invalid IL or missing references) //IL_0c1f: Unknown result type (might be due to invalid IL or missing references) //IL_0c39: Unknown result type (might be due to invalid IL or missing references) Section("PRIVATE - NOT IN THE PUBLIC BUILD"); Act(_toolInner, "God mode", "Refuses damage aimed at you at the server, so you are not hit at all. Only works while you are the host, because that is the machine applying it.", delegate { Cheats.GodMode = !Cheats.GodMode; return (!Cheats.GodMode) ? "You can be hurt again." : "Nothing can hurt you."; }, Cheats.GodMode); Act(_toolInner, "Oops I Did It Again Jesus mode", "You still take the hit and still drop - this stands you back up a moment later. Works as a guest too, since resurrection never checks who asked.", delegate { Cheats.Immortal = !Cheats.Immortal; return (!Cheats.Immortal) ? "Staying down now." : "You will keep getting back up."; }, Cheats.Immortal); Act(_toolInner, "Endless ammo", "Never reload.", delegate { Cheats.InfiniteAmmo = !Cheats.InfiniteAmmo; return (!Cheats.InfiniteAmmo) ? "Reloading again." : "No reloading."; }, Cheats.InfiniteAmmo); Act(_toolInner, "One punch", "Your punches kill in one hit.", delegate { Cheats.OnePunch = !Cheats.OnePunch; return (!Cheats.OnePunch) ? "Normal punches." : "One punch."; }, Cheats.OnePunch); if (Cheats.OnePunch) { Dial(_toolInner, "Punch damage", Cheats.OnePunchForce, 1f, 999999f, delegate(float x) { Cheats.OnePunchForce = x; }, "0"); Note(_toolInner, "A normal punch does whatever the game says - check the log for the vanilla number. This replaces it, for your punches only."); } Act(_toolInner, "Mr. Fantastic", "Punch from much further away than you should be able to.", delegate { Cheats.Stretchy = !Cheats.Stretchy; return (!Cheats.Stretchy) ? "Normal reach." : "Reach extended."; }, Cheats.Stretchy); Act(_toolInner, "Jesus mode", "The surface holds your weight.", delegate { Cheats.WalkOnWater = !Cheats.WalkOnWater; return (!Cheats.WalkOnWater) ? "You sink again." : "The surface holds."; }, Cheats.WalkOnWater); if (Cheats.Stretchy) { Dial(_toolInner, "Arm reach", Cheats.Reach, 2f, 60f, delegate(float x) { Cheats.Reach = x; }, "0"); } Dial(_toolInner, "Money per sale", Cheats.MoneyMultiplier, 1f, 50f, delegate(float x) { Cheats.MoneyMultiplier = x; }); Note(_toolInner, "Pays the extra when you sell. Nothing happens until then."); Dial(_toolInner, "Luck on the next catch", Cheats.LuckMultiplier, 1f, 20f, delegate(float x) { Cheats.LuckMultiplier = x; }); Note(_toolInner, "Bends the catch table toward the rare end. Applies as you fish."); Dial(_toolInner, "Walking speed", Cheats.SpeedMultiplier, 1f, 20f, delegate(float x) { Cheats.SpeedMultiplier = x; }); Section("AIM"); Act(_toolInner, "Fish aimbot", "Turns the game's own controller aim assist into a full auto-aim on mouse and keyboard.", delegate { Kit.Aimbot = !Kit.Aimbot; return (!Kit.Aimbot) ? "Aim assist back to normal." : "Aim assist is yours now."; }, Kit.Aimbot); if (Kit.Aimbot) { Dial(_toolInner, "Cone", Kit.AimFov, 1f, 90f, delegate(float x) { Kit.AimFov = x; }, "0"); Dial(_toolInner, "Range", Kit.AimRange, 10f, 600f, delegate(float x) { Kit.AimRange = x; }, "0"); Dial(_toolInner, "Snap", Kit.AimSnap, 1f, 60f, delegate(float x) { Kit.AimSnap = x; }, "0"); } Act(_toolInner, "Fisherman aimbot", "Steers your aim at PEOPLE instead of fish while aiming down sights.", delegate { Kit.ManHunt = !Kit.ManHunt; return (!Kit.ManHunt) ? "People are safe again." : "Tracking people, not fish."; }, Kit.ManHunt); if (Kit.ManHunt) { Dial(_toolInner, "Cone", Kit.ManFov, 1f, 180f, delegate(float x) { Kit.ManFov = x; }, "0"); Dial(_toolInner, "Snap", Kit.ManSnap, 1f, 60f, delegate(float x) { Kit.ManSnap = x; }, "0"); } Act(_toolInner, "Laser gun", "No cooldown, no recoil, dead straight. Every gun becomes a continuous stream.", delegate { Kit.Laser = !Kit.Laser; return (!Kit.Laser) ? "Guns behave themselves." : "No cooldown, no recoil, straight line."; }, Kit.Laser); Act(_toolInner, "Smite whoever you are looking at", "Kills whoever is in your crosshair, even with friendly fire off.", () => Smite.Strike()); Section("MOVEMENT"); Act(_toolInner, "Bunny hop", "Hold space to keep bouncing instead of jumping once.", delegate { Kit.BunnyHop = !Kit.BunnyHop; return (!Kit.BunnyHop) ? "Normal jumping." : "Hold space to keep bouncing."; }, Kit.BunnyHop); Section("BOSSES"); Act(_toolInner, "Boss on every cast", "Every single cast hooks the boss instead of a fish.", delegate { Kit.BossRush = !Kit.BossRush; return (!Kit.BossRush) ? "Fishing is fishing again." : ("Every cast hooks " + Kit.BossName + "."); }, Kit.BossRush); if (Kit.BossRush) { List bosses = Kit.BossNames(); if (bosses.Count > 0) { Act(_toolInner, "Target: " + Kit.BossName + " - next", "Which boss every cast should hook. Click to move to the next one.", delegate { int num = bosses.FindIndex((string b) => b.IndexOf(Kit.BossName, StringComparison.OrdinalIgnoreCase) >= 0); Kit.BossName = bosses[(num + 1 + bosses.Count) % bosses.Count]; return "Now hooking " + Kit.BossName + "."; }); } } Act(_toolInner, "Summon the magma whale", "Spawns the final boss in front of you. Erupts the volcano properly if you are on the lava island.", () => Kit.SummonWhale()); Act(_toolInner, "Take back everything I spawned (" + Cheats.SpawnedCount + ")", "Despawns every creature and item spawned from this console, boss or not. Sending the boss away only removes the one the fight is built around; spawn three whales and the other two are not bosses as far as the game is concerned, and nothing but this knows they are there.", () => Cheats.ClearSpawned()); Act(_toolInner, "Send the boss away", "Despawns a boss you summoned, through the game's own KillBoss - so the health bar and the fight state go with it.", () => Actions.SendBossAway()); Dial(_toolInner, "Whale distance", Kit.WhaleDistance, 10f, 150f, delegate(float x) { Kit.WhaleDistance = x; }, "0"); Section("MONEY"); Dial(_toolInner, "Worth multiplier", Kit.WorthMultiplier, 1f, 10000f, delegate(float x) { Kit.WorthMultiplier = x; }, "0"); Act(_toolInner, "Inflate everything you carry x" + Kit.WorthMultiplier.ToString("0"), "Sets what everything you are carrying sells for - what is in your hands and everything in the inventory behind it - using the multiplier above. The slider on its own does nothing; this is the row that applies it. Works once per item, so sell them before inflating more.", () => Kit.InflateHeld()); Act(_toolInner, "Dripper: " + Kit.CasinoLabel(), "Rigs the slot machine to land on the rarity you pick. Click to cycle: off, green, rare, legendary.", () => Kit.CycleCasino(), !Kit.CasinoLabel().Equals("off", StringComparison.OrdinalIgnoreCase)); Act(_toolInner, "Roulette: " + Kit.RouletteLabel(), "Rigs the red/black/green wheel. Green pays 35x. Click to cycle the winning colour.", () => Kit.CycleRoulette(), Kit.Roulette); Act(_toolInner, "Dripper takes anything", "The slot machine will swallow clams and ordinary items, not just drip creatures.", delegate { Kit.DripperTakesAnything = !Kit.DripperTakesAnything; return (!Kit.DripperTakesAnything) ? "Drip creatures only, as intended." : "It will swallow clams now."; }, Kit.DripperTakesAnything); Section("SEEING AND OWNING"); Act(_toolInner, "See further", "Thins the fog and pushes the view distance out. Useful for spotting people who should not be where they are.", delegate { Kit.Visibility = !Kit.Visibility; return (!Kit.Visibility) ? "Fog back to normal." : "Fog thinned, view distance pushed out."; }, Kit.Visibility); if (Kit.Visibility) { Dial(_toolInner, "Fog", Kit.FogMultiplier, 0f, 1f, delegate(float x) { Kit.FogMultiplier = x; }, "0.00"); Dial(_toolInner, "View distance", Kit.ViewDistance, 500f, 10000f, delegate(float x) { Kit.ViewDistance = x; }, "0"); } Act(_toolInner, "Show every island", "Puts every island on the radar, including the hidden dev one.", delegate { Kit.RevealIslands = !Kit.RevealIslands; return (!Kit.RevealIslands) ? "Radar back to what you have unlocked." : "Every island on the radar, and you can sail to them."; }, Kit.RevealIslands); if (Kit.RevealIslands) { Act(_toolInner, "Let you sail there too", "Showing a dot is not permission to travel; this unlocks the travel gate as well.", delegate { Kit.UnlockTravel = !Kit.UnlockTravel; return (!Kit.UnlockTravel) ? "Dots only." : "Travel unlocked."; }, Kit.UnlockTravel); } Act(_toolInner, "Unlock every skin", "Unlocks all clothing, item and boat skins. Adds without wiping, so nothing you own is lost.", () => Kit.UnlockEverything()); Section("YOUR TOOLS"); Act(_toolInner, (HostTools.Flying ? "Stop flying" : "Fly") + Plugin.KeyLabel("Fly"), "Free movement in three dimensions. Space and control for up and down.", () => HostTools.ToggleFly(), HostTools.Flying); Act(_toolInner, "Previous island" + Plugin.KeyLabel("PrevIsland"), "Travels the WHOLE lobby to the island before this one.", () => HostTools.Next(backwards: true)); Act(_toolInner, "Next island" + Plugin.KeyLabel("NextIsland"), "Travels the WHOLE lobby to the next island.", () => HostTools.Next(backwards: false)); TMP_Text val = NativeUI.Label(_toolInner, HostTools.Where(), Vector2.zero); if ((Object)(object)val != (Object)null) { ((Graphic)val).color = Theme.Faint; Fix(val, 20f, (TextAlignmentOptions)513); } Dial(_toolInner, "Fly speed", Plugin.FlySpeed.Value, 5f, 80f, delegate(float x) { Plugin.FlySpeed.Value = x; }, "0"); Dial(_toolInner, "Shift boost", Plugin.FlyBoost.Value, 1f, 10f, delegate(float x) { Plugin.FlyBoost.Value = x; }); Section("VANILLA BUG FIXES"); Act(_toolInner, "Fix black screen on join", "A bug in the base game where joining players see nothing. Leave it on.", delegate { Plugin.FixJoining.Value = !Plugin.FixJoining.Value; return (!Plugin.FixJoining.Value) ? "Join fix off." : "Join fix on."; }, Plugin.FixJoining.Value); Act(_toolInner, "Fix the sinking boat", "A bug in the base game where a broken player drags the boat under. Leave it on.", delegate { Plugin.PruneBrokenPlayers.Value = !Plugin.PruneBrokenPlayers.Value; return (!Plugin.PruneBrokenPlayers.Value) ? "Boat fix off." : "Boat fix on."; }, Plugin.PruneBrokenPlayers.Value); TMP_Text val2 = NativeUI.Label(_toolInner, "Both are bugs in the base game. Leave them on.", Vector2.zero); if ((Object)(object)val2 != (Object)null) { ((Graphic)val2).color = Theme.Faint; Fix(val2, 22f, (TextAlignmentOptions)513); } List list = Cheats.Names(""); List list2 = Cheats.Names(_spawnQuery); Section(string.IsNullOrEmpty(_spawnQuery) ? ("SPAWN (" + list.Count + ")") : ("SPAWN (" + list2.Count + " of " + list.Count + ")")); _search = NativeUI.Field(_toolInner, "Type to search " + list.Count + " things...", _spawnQuery, delegate(string s) { if (!(s == _spawnQuery)) { _spawnQuery = s; _spawnPage = 0; _searchSettles = Time.realtimeSinceStartup + 0.25f; } }); if ((Object)(object)_search == (Object)null) { int pages = Mathf.Max(1, Mathf.CeilToInt((float)list2.Count / 40f)); _spawnPage = Mathf.Clamp(_spawnPage, 0, pages - 1); if (pages > 1) { Act(_toolInner, "Page " + (_spawnPage + 1) + " of " + pages + " - next", "The spawn list is long, so it is shown forty at a time. Click for the next forty.", delegate { _spawnPage = (_spawnPage + 1) % pages; return "Showing page " + (_spawnPage + 1) + "."; }); } { foreach (string item in list2.Skip(_spawnPage * 40).Take(40)) { string pick = item; Act(_toolInner, pick, "Spawns one of these in front of you.", () => Cheats.Spawn(pick)); } return; } } if (string.IsNullOrEmpty(_spawnQuery)) { Note(_toolInner, "Start typing above and matches appear here."); return; } if (list2.Count == 0) { Note(_toolInner, "Nothing matches \"" + _spawnQuery + "\"."); return; } foreach (string item2 in list2.Take(25)) { string pick2 = item2; Act(_toolInner, pick2, "Spawns one of these in front of you.", () => Cheats.Spawn(pick2)); } if (list2.Count > 25) { Note(_toolInner, "...and " + (list2.Count - 25) + " more. Type a bit more to narrow it down."); } } private static void UpdateDossier() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_dossier == (Object)null) { return; } Player val = Selected(); if ((Object)(object)val == (Object)null) { NativeUI.Say(_dossier, "Pick somebody on the left to see what they have been doing."); ((Graphic)_dossier).color = Theme.Faint; return; } int value; int num = (Logbook.Tally().TryGetValue(_selected, out value) ? value : 0); List list = Summary.LinesFor(_selected); string text = val.SteamName + "\n" + ((num == 0) ? "nothing against them" : (num + " strikes, " + Summary.ProvenCount(_selected) + " proven")); if (list.Count > 0) { text = text + "\n" + string.Join("\n", list.Take(4)); } NativeUI.Say(_dossier, text); ((Graphic)_dossier).color = ((num == 0) ? Theme.Muted : Theme.ForStrikes(num, Plugin.KickThreshold.Value)); } private static void Grave(Transform parent, string label, string ask, string hint, Func run) { //IL_0066: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) bool flag = _armed == label && Time.realtimeSinceStartup < _armedUntil; GameObject go = NativeUI.Button(parent, flag ? ask : label, delegate { if (_armed == label && Time.realtimeSinceStartup < _armedUntil) { _armed = null; string text = run(); Plugin.Log.LogInfo((object)("[Console] confirmed \"" + label + "\" -> " + (text ?? "(no message)"))); Toast(text); Mark(); } else { _armed = label; _armedUntil = Time.realtimeSinceStartup + 5f; Toast(ask + " Click again to confirm."); Mark(); } }, new Vector2(0f, 40f), Vector2.zero, hint + " This cannot be undone, so it asks once first."); NativeUI.Edge(go, flag ? Theme.EdgeGrave : Theme.EdgeGrave); NativeUI.Back(go, flag ? Theme.RowArmed : Theme.Row); if (flag) { NativeUI.Paint(go, Theme.Ink); } Grew(parent, 44f); } private static void Act(Transform parent, string label, string hint, Func run, bool on = false) { //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) GameObject go = NativeUI.Button(parent, (on ? "✓ " : "") + label, delegate { string text = run(); Plugin.Log.LogInfo((object)("[Console] clicked \"" + label + "\" -> " + (string.IsNullOrEmpty(text) ? "(no message)" : text))); Toast(text); Mark(); }, new Vector2(0f, 40f), Vector2.zero, hint); if (on) { NativeUI.Mark(go, on: true); } Grew(parent, 44f); } private static void Choice(Transform parent, string what, Action now, Action set, string hint) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) TMP_Text val = NativeUI.Label(parent, what, Vector2.zero); if ((Object)(object)val != (Object)null) { ((Graphic)val).color = Theme.Muted; Fix(val, 22f, (TextAlignmentOptions)513); } GameObject val2 = Strip(parent, what, 40f); foreach (Action value in Enum.GetValues(typeof(Action))) { Action mine = value; bool flag = now == value; GameObject val3 = NativeUI.Button(val2.transform, (flag ? "✓ " : " ") + Policy.Costs(value), delegate { set(mine); Toast(what + ": " + Policy.Costs(mine).ToLowerInvariant() + "."); Mark(); }, new Vector2(0f, 40f), Vector2.zero, hint); (val3.GetComponent() ?? val3.AddComponent()).flexibleWidth = 1f; NativeUI.Mark(val3, flag); NativeUI.Edge(val3, flag ? Theme.EdgeLive : Theme.EdgeIdle); } Grew(parent, 88f); } private static Transform[] Tiles(Transform parent, int n) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown GameObject val = Strip(parent, "Tiles", 0f); LayoutElement component = val.GetComponent(); component.preferredHeight = -1f; component.flexibleHeight = 0f; HorizontalLayoutGroup component2 = val.GetComponent(); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; ((LayoutGroup)component2).childAlignment = (TextAnchor)0; Transform[] array = (Transform[])(object)new Transform[n]; for (int i = 0; i < n; i++) { GameObject val2 = new GameObject("Tile", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(LayoutElement) }); val2.transform.SetParent(val.transform, false); VerticalLayoutGroup component3 = val2.GetComponent(); ((HorizontalOrVerticalLayoutGroup)component3).spacing = 4f; ((HorizontalOrVerticalLayoutGroup)component3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandWidth = true; LayoutElement component4 = val2.GetComponent(); component4.flexibleWidth = 1f; component4.preferredWidth = 0f; array[i] = val2.transform; } return array; } private static void Charges(Player p) { ulong steamID; try { steamID = p.SteamID; } catch { return; } List list; try { list = Logbook.EntriesFor(steamID, Plugin.ChargesShown.Value); } catch { return; } if (list == null || list.Count == 0) { Note(_wardenInner, "Nothing on them. The guard has not caught them at anything."); return; } for (int num = list.Count - 1; num >= 0; num--) { Entry entry = list[num]; if (entry != null) { string text = (string.IsNullOrEmpty(entry.Verdict.Rule) ? entry.Rpc : entry.Verdict.Rule); Note(_wardenInner, entry.When.ToString("HH:mm", CultureInfo.InvariantCulture) + " " + text + (entry.Blocked ? " (blocked)" : " (allowed)")); if (!string.IsNullOrEmpty(entry.Verdict.Detail)) { Note(_wardenInner, " " + entry.Verdict.Detail); } } } Entry entry2 = list[list.Count - 1]; if (entry2 != null && entry2.Verdict.Rule != null) { Note(_wardenInner, "Latest is " + Response.FamilyOf(entry2.Verdict.Rule).ToString().ToLowerInvariant() + (Response.Trolls(entry2.Verdict.Rule) ? ", answers back." : ", never answers back.")); } } private static void Note(Transform parent, string text) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) TMP_Text val = NativeUI.Label(parent, text, Vector2.zero); if (!((Object)(object)val == (Object)null)) { ((Graphic)val).color = Theme.Faint; Fix(val, 24f, (TextAlignmentOptions)513); Grew(parent, 28f); } } private static void Dial(Transform parent, string label, float value, float min, float max, Action set, string format = "0.0", bool whole = false) { NativeUI.Slider(parent, label, value, min, max, set, format, whole); Grew(parent, 66f); } private static float Where(ScrollRect s) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) try { return ((Object)(object)s == (Object)null || (Object)(object)s.content == (Object)null) ? 0f : s.content.anchoredPosition.y; } catch { return 0f; } } private static void Restore(ScrollRect s, float at) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)s == (Object)null) && !((Object)(object)s.content == (Object)null)) { float num; Rect rect; if (!((Object)(object)s.viewport != (Object)null)) { num = 0f; } else { rect = s.viewport.rect; num = ((Rect)(ref rect)).height; } float num2 = num; rect = s.content.rect; float num3 = Mathf.Max(0f, ((Rect)(ref rect)).height - num2); Vector2 anchoredPosition = s.content.anchoredPosition; anchoredPosition.y = Mathf.Clamp(at, 0f, num3); s.content.anchoredPosition = anchoredPosition; } } catch { } } private static void Clear(Transform t) { if (!((Object)(object)t == (Object)null)) { for (int num = t.childCount - 1; num >= 0; num--) { GameObject gameObject = ((Component)t.GetChild(num)).gameObject; gameObject.transform.SetParent((Transform)null, false); Object.Destroy((Object)(object)gameObject); } } } private static void Fix(TMP_Text t, float height, TextAlignmentOptions align) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)t == (Object)null)) { NativeUI.Sizing(((Component)t).gameObject, height); t.textWrappingMode = (TextWrappingModes)1; t.alignment = align; } } private static List Others() { List list = new List(); try { foreach (Player player in PlayerManager.Players) { if ((Object)(object)player != (Object)null && (Object)(object)player != (Object)(object)Player.LocalPlayer) { list.Add(player); } } } catch { } return list; } private static Player Selected() { if (_selected == 0L) { return null; } foreach (Player item in Others()) { try { if (item.SteamID == _selected) { return item; } } catch { } } return null; } } internal static class NativeLook { private static bool _looked; private static Font _font; private static bool _sampled; public static Font Font { get { Look(); return _font; } } public static string Source { get; private set; } = "not looked yet"; public static Color MenuText { get; private set; } = Color.white; public static bool MenuBold { get; private set; } = true; public static int MenuSize { get; private set; } = 16; public static void SampleMenu() { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Invalid comparison between Unknown and I4 //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) if (_sampled) { return; } try { PauseManager val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null) { return; } object value = Traverse.Create((object)val).Field("_pauseHolder").GetValue(); GameObject val2 = (GameObject)(((value is GameObject) ? value : null) ?? ((object)/*isinst with value type is only supported in some contexts*/)); if ((Object)(object)val2 == (Object)null) { return; } TMP_Text[] componentsInChildren = val2.GetComponentsInChildren(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } IGrouping grouping = (from t in componentsInChildren where (Object)(object)t != (Object)null && t.fontSize > 4f group t by Mathf.RoundToInt(t.fontSize) into g orderby g.Count() descending select g).FirstOrDefault(); if (grouping != null) { TMP_Text val3 = grouping.First(); MenuSize = Mathf.Clamp(Mathf.RoundToInt(val3.fontSize), 11, 22); MenuBold = (val3.fontStyle & 1) > 0; Color color = ((Graphic)val3).color; if (color.a > 0.2f) { MenuText = new Color(color.r, color.g, color.b, 1f); } if ((Object)(object)val3.font != (Object)null && (Object)(object)val3.font.sourceFontFile != (Object)null) { _font = val3.font.sourceFontFile; _looked = true; Source = "pause menu"; } _sampled = true; Plugin.Log.LogInfo((object)($"[Theme] matched the escape menu: size {MenuSize}, " + (MenuBold ? "bold" : "regular") + ", " + ColorUtility.ToHtmlStringRGB(MenuText))); } } catch { } } private static void Look() { if (_looked) { return; } try { _font = FromLocalization() ?? FromAnyTmpAsset() ?? FromLoadedFonts(); if ((Object)(object)_font != (Object)null) { _looked = true; Plugin.Log.LogInfo((object)("[Theme] using the game's font: " + ((Object)_font).name + " (" + Source + ")")); } } catch (Exception ex) { _looked = true; Source = "failed: " + ex.Message; Plugin.Log.LogWarning((object)("[Theme] could not borrow a font, using the default: " + ex.Message)); } } private static Font FromLocalization() { try { LocalizationManager val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null) { return null; } string[] array = new string[2] { "_defaultFontAsset", "_backdropFontAsset" }; foreach (string text in array) { Traverse val2 = Traverse.Create((object)val).Field(text); if (val2 != null && val2.FieldExists()) { object value = val2.GetValue(); TMP_FontAsset val3 = (TMP_FontAsset)((value is TMP_FontAsset) ? value : null); Font val4 = (((Object)(object)val3 != (Object)null) ? val3.sourceFontFile : null); if ((Object)(object)val4 != (Object)null) { Source = "LocalizationManager." + text; return val4; } } } } catch { } return null; } private static Font FromAnyTmpAsset() { try { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val in array) { if (!((Object)(object)val == (Object)null)) { Font sourceFontFile = val.sourceFontFile; if ((Object)(object)sourceFontFile != (Object)null) { Source = "TMP asset " + ((Object)val).name; return sourceFontFile; } } } } catch { } return null; } private static Font FromLoadedFonts() { try { List list = (from f in Resources.FindObjectsOfTypeAll() where (Object)(object)f != (Object)null && !string.IsNullOrEmpty(((Object)f).name) where ((Object)f).name.IndexOf("Arial", StringComparison.OrdinalIgnoreCase) < 0 select f).ToList(); if (list.Count > 0) { Source = "loaded font " + ((Object)list[0]).name; return list[0]; } } catch { } return null; } } internal sealed class NativeScroll : MonoBehaviour, IScrollHandler, IEventSystemHandler { public ScrollRect Rect; public void OnScroll(PointerEventData e) { //IL_0023: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Rect == (Object)null || (Object)(object)Rect.content == (Object)null) { return; } float y = e.scrollDelta.y; if (!(Mathf.Abs(y) < 0.0001f)) { float num; Rect rect; if (!((Object)(object)Rect.viewport != (Object)null)) { num = 0f; } else { rect = Rect.viewport.rect; num = ((Rect)(ref rect)).height; } float num2 = num; rect = Rect.content.rect; float num3 = Mathf.Max(0f, ((Rect)(ref rect)).height - num2); if (!(num3 <= 0f)) { float num4 = Mathf.Max(120f, num2 * 0.25f); Vector2 anchoredPosition = Rect.content.anchoredPosition; anchoredPosition.y = Mathf.Clamp(anchoredPosition.y - Mathf.Sign(y) * num4, 0f, num3); Rect.content.anchoredPosition = anchoredPosition; Rect.velocity = Vector2.zero; } } } } internal static class NativeUI { internal sealed class Dial { public TMP_Text Caption; public Slider Bar; public string Format = "0.0"; public string Label = ""; public float Value; public void Sync() { Say(Caption, Label + " " + Value.ToString(Format)); } } public const float Row = 40f; private static GameObject _buttonTemplate; private static Transform _canvas; private static GameObject _root; private static GameObject _sliderTemplate; private static GameObject _ours; private static Material _glass; private static readonly Dictionary _pinned = new Dictionary(); public static Action OnHover; private static GameObject _fieldTemplate; private const float BarWidth = 12f; public static bool Available { get; private set; } public static string Problem { get; private set; } = "not looked yet"; public static bool Prepare() { //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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_0198: Unknown result type (might be due to invalid IL or missing references) if (Available) { return true; } try { CanvasManager val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null) { Problem = "CanvasManager not in the scene yet"; return false; } _buttonTemplate = PickButton(Traverse.Create((object)val).Field("_allMainMenuButtons").GetValue() as List); if ((Object)(object)_buttonTemplate == (Object)null) { _buttonTemplate = PickButton(from b in Resources.FindObjectsOfTypeAll