using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Pigeon.Movement; using Scoreboard.Config; using Scoreboard.Data; using Scoreboard.Networking; using Scoreboard.Tracking; using Scoreboard.UI; using Sparroh.UI; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Sparroh")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.2.0")] [assembly: AssemblyInformationalVersion("1.2.2")] [assembly: AssemblyProduct("Scoreboard")] [assembly: AssemblyTitle("Scoreboard")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.2.0")] [module: UnverifiableCode] [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; } } } [BepInPlugin("sparroh.scoreboard", "Scoreboard", "1.2.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [MycoMod(/*Could not decode attribute arguments.*/)] public class ScoreboardPlugin : BaseUnityPlugin { public const string PluginGUID = "sparroh.scoreboard"; public const string PluginName = "Scoreboard"; public const string PluginVersion = "1.2.2"; internal static ManualLogSource Log; internal static ScoreboardPlugin Instance; private Harmony _harmony; private ScoreboardUI _ui; private bool _wasHeld; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Log); _harmony = new Harmony("sparroh.scoreboard"); try { _harmony.PatchAll(typeof(MissionStatPatches).Assembly); Log.LogInfo((object)"Scoreboard v1.2.2 loaded."); } catch (Exception arg) { Log.LogError((object)$"Harmony patch failed: {arg}"); } ScoreboardNetwork.Initialize(); } private void Update() { ConfigManager.Tick(); if (ConfigManager.Enabled == null || !ConfigManager.Enabled.Value) { if (_wasHeld) { _ui?.Hide(); _wasHeld = false; } return; } bool flag = IsHoldKeyPressed(); if (flag && !_wasHeld) { if (_ui == null) { _ui = new ScoreboardUI(); } _ui.Show(); } else if (!flag && _wasHeld) { _ui?.Hide(); } else if (flag) { _ui?.Tick(); } _wasHeld = flag; if (ConfigManager.NetworkingEnabled != null && ConfigManager.NetworkingEnabled.Value) { ScoreboardNetwork.Tick(); } } private void OnDestroy() { try { _ui?.Destroy(); _ui = null; ScoreboardNetwork.Shutdown(); ConfigManager.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"Cleanup error: {arg}"); } } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private bool IsHoldKeyPressed() { //IL_0029: 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_003d: Unknown result type (might be due to invalid IL or missing references) try { if ((GameManager.players == null || GameManager.players.Count == 0) && (Object)(object)Player.LocalPlayer == (Object)null) { return false; } Key value = ConfigManager.HoldKey.Value; Keyboard current = Keyboard.current; if (current == null) { return false; } return ((ButtonControl)current[value]).isPressed; } catch { return false; } } } namespace Scoreboard { public static class MyPluginInfo { public const string PLUGIN_GUID = "Scoreboard"; public const string PLUGIN_NAME = "Scoreboard"; public const string PLUGIN_VERSION = "1.2.2"; } } namespace Scoreboard.UI { internal static class ScoreboardDataBuilder { public static void Build(List buffer) { buffer.Clear(); try { List players = GameManager.players; if (players == null || players.Count == 0) { return; } for (int i = 0; i < players.Count; i++) { Player val = players[i]; if (!((Object)(object)val == (Object)null)) { ScoreboardRow scoreboardRow = new ScoreboardRow { ClientId = ((NetworkBehaviour)val).OwnerClientId, UserId = val.UserID, PlayerNumber = val.PlayerNumber, Name = (string.IsNullOrEmpty(val.Name) ? $"Player {val.PlayerNumber + 1}" : val.Name), IsLocal = ((NetworkBehaviour)val).IsLocalPlayer, IsAlive = val.IsAlive }; int pingMs = PlayerPresentation.GetPingMs(val); int pingMs2; if (pingMs >= 0) { scoreboardRow.PingMs = pingMs; } else if (ScoreboardNetwork.TryGetRemotePing(((NetworkBehaviour)val).OwnerClientId, out pingMs2)) { scoreboardRow.PingMs = pingMs2; } else { scoreboardRow.PingMs = -1; } PlayerPresentation.FillCharacterVisuals(val, scoreboardRow); PlayerPresentation.FillGearIcons(val, scoreboardRow); PlayerStatsSnapshot snapshot; if (scoreboardRow.IsLocal) { MissionStatTracker.FillLocalStats(scoreboardRow); } else if (ScoreboardNetwork.TryGetRemoteStats(scoreboardRow.ClientId, out snapshot)) { snapshot.ApplyTo(scoreboardRow); } else { scoreboardRow.HasStats = false; } buffer.Add(scoreboardRow); } } buffer.Sort(delegate(ScoreboardRow a, ScoreboardRow b) { int num = b.CoreKills.CompareTo(a.CoreKills); if (num != 0) { return num; } num = a.PlayerNumber.CompareTo(b.PlayerNumber); return (num != 0) ? num : string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); }); } catch (Exception arg) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogError((object)$"Scoreboard data build failed: {arg}"); } } } public static void Apply(List dataBuffer, List rows, UIText subtitleText) { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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_019a: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0350: 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) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_0460: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_0459: Unknown result type (might be due to invalid IL or missing references) bool flag = false; try { flag = (Object)(object)MissionManager.Instance != (Object)null && MissionManager.Instance.IsMissionActive; } catch { } string text = (flag ? "MISSION" : ((!MissionStatTracker.HasSession) ? "LOBBY" : "LAST MISSION")); if (subtitleText != null) { int modPeerCount = ScoreboardNetwork.ModPeerCount; string text2 = ((!ScoreboardNetwork.IsNetworkingEnabled) ? string.Empty : ((modPeerCount > 1) ? $" · {modPeerCount} synced" : " · solo/net")); subtitleText.Text = string.Format("{0} · {1} player{2}{3}", text, dataBuffer.Count, (dataBuffer.Count == 1) ? "" : "s", text2); } for (int i = 0; i < rows.Count; i++) { if (i >= dataBuffer.Count) { rows[i].Root.SetActive(false); continue; } ScoreboardRow scoreboardRow = dataBuffer[i]; ScoreboardRowView scoreboardRowView = rows[i]; scoreboardRowView.Root.SetActive(true); Color color = (scoreboardRow.IsLocal ? UIColors.Sky : UIColors.TextPrimary); if (!scoreboardRow.IsAlive) { color = UIColors.TextMuted; } string value = (scoreboardRow.IsLocal ? ("> " + scoreboardRow.Name) : scoreboardRow.Name); SetText(scoreboardRowView.NameText, value, color); ApplyIcon(scoreboardRowView.ClassIcon, scoreboardRow.CharacterIcon, (Color)(scoreboardRow.IsAlive ? new Color(scoreboardRow.CharacterColorR, scoreboardRow.CharacterColorG, scoreboardRow.CharacterColorB, scoreboardRow.CharacterColorA) : UIColors.TextMuted)); object obj2; if (!((Object)(object)scoreboardRowView.ClassIcon != (Object)null) || !((Object)(object)((Component)scoreboardRowView.ClassIcon).transform.parent != (Object)null)) { Image classIcon = scoreboardRowView.ClassIcon; obj2 = ((classIcon != null) ? ((Component)classIcon).gameObject : null); } else { obj2 = ((Component)((Component)scoreboardRowView.ClassIcon).transform.parent).gameObject; } GameObject val = (GameObject)obj2; if ((Object)(object)val != (Object)null) { SetTooltip(val, string.IsNullOrEmpty(scoreboardRow.CharacterTooltip) ? scoreboardRow.CharacterName : scoreboardRow.CharacterTooltip); } if ((Object)(object)scoreboardRow.CharacterIcon == (Object)null) { SetText(scoreboardRowView.ClassText, string.IsNullOrEmpty(scoreboardRow.CharacterName) ? "—" : scoreboardRow.CharacterName, (Color)(scoreboardRow.IsAlive ? new Color(scoreboardRow.CharacterColorR, scoreboardRow.CharacterColorG, scoreboardRow.CharacterColorB, scoreboardRow.CharacterColorA) : UIColors.TextMuted)); scoreboardRowView.ClassText.GameObject.SetActive(true); } else { scoreboardRowView.ClassText.GameObject.SetActive(false); } ApplyGearSlot(scoreboardRowView.Gear0, scoreboardRow.GearIcon0, scoreboardRow.GearName0); ApplyGearSlot(scoreboardRowView.Gear1, scoreboardRow.GearIcon1, scoreboardRow.GearName1); ApplyGearSlot(scoreboardRowView.Gear3, scoreboardRow.GearIcon3, scoreboardRow.GearName3); SetText(scoreboardRowView.StatKills, ScoreboardRow.FormatStat(scoreboardRow.CoreKills, scoreboardRow.HasStats), StatColor(scoreboardRow, UIColors.Rose)); SetText(scoreboardRowView.StatTgt, ScoreboardRow.FormatStat(scoreboardRow.TargetKills, scoreboardRow.HasStats), StatColor(scoreboardRow, UIColors.Orchid)); SetText(scoreboardRowView.StatDmg, ScoreboardRow.FormatStat(scoreboardRow.Damage, scoreboardRow.HasStats), StatColor(scoreboardRow, UIColors.Macaroon)); SetText(scoreboardRowView.StatHeal, ScoreboardRow.FormatStat(scoreboardRow.Healing, scoreboardRow.HasStats), StatColor(scoreboardRow, UIColors.Shamrock)); SetText(scoreboardRowView.StatDth, ScoreboardRow.FormatStat(scoreboardRow.Deaths, scoreboardRow.HasStats), StatColor(scoreboardRow, UIColors.TextSecondary)); SetText(scoreboardRowView.StatRev, ScoreboardRow.FormatStat(scoreboardRow.Revives, scoreboardRow.HasStats), StatColor(scoreboardRow, UIColors.Electric)); SetText(scoreboardRowView.StatPing, ScoreboardRow.FormatPing(scoreboardRow.PingMs), PingColor(scoreboardRow.PingMs)); if ((Object)(object)scoreboardRowView.Background != (Object)null) { ((Graphic)scoreboardRowView.Background).color = (scoreboardRow.IsLocal ? UIColors.WithAlpha(UIColors.BorderAccent, 0.28f) : ((i % 2 == 0) ? UIColors.EntryBg : UIColors.WithAlpha(UIColors.Surface, 0.9f))); } if ((Object)(object)scoreboardRowView.Accent != (Object)null) { ((Behaviour)scoreboardRowView.Accent).enabled = scoreboardRow.IsLocal; if (scoreboardRow.IsLocal) { ((Graphic)scoreboardRowView.Accent).color = UIColors.BorderAccent; } } } } private static void ApplyGearSlot(Image img, Sprite sprite, string tooltip) { //IL_001e: 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_00e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)img == (Object)null) { return; } if ((Object)(object)sprite != (Object)null) { img.sprite = sprite; ((Graphic)img).color = Color.white; ((Behaviour)img).enabled = true; ((Component)img).gameObject.SetActive(true); if ((Object)(object)((Component)img).transform.parent != (Object)null) { ((Component)((Component)img).transform.parent).gameObject.SetActive(true); } SetTooltip(((Object)(object)((Component)img).transform.parent != (Object)null) ? ((Component)((Component)img).transform.parent).gameObject : ((Component)img).gameObject, tooltip); } else { img.sprite = null; ((Behaviour)img).enabled = false; if ((Object)(object)((Component)img).transform.parent != (Object)null) { ((Component)((Component)img).transform.parent).gameObject.SetActive(true); } ((Component)img).gameObject.SetActive(true); ((Graphic)img).color = UIColors.WithAlpha(UIColors.TextMuted, 0.2f); UIFactory.ApplyWhiteSprite(img); ((Behaviour)img).enabled = true; img.preserveAspect = false; SetTooltip(((Object)(object)((Component)img).transform.parent != (Object)null) ? ((Component)((Component)img).transform.parent).gameObject : ((Component)img).gameObject, string.IsNullOrEmpty(tooltip) ? "—" : tooltip); } if ((Object)(object)sprite != (Object)null) { img.preserveAspect = true; } } private static void ApplyIcon(Image img, Sprite sprite, Color tint) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)img == (Object)null) { return; } if ((Object)(object)sprite != (Object)null) { img.sprite = sprite; ((Graphic)img).color = tint; ((Behaviour)img).enabled = true; img.preserveAspect = true; ((Component)img).gameObject.SetActive(true); if ((Object)(object)((Component)img).transform.parent != (Object)null) { ((Component)((Component)img).transform.parent).gameObject.SetActive(true); } } else { img.sprite = null; ((Behaviour)img).enabled = false; ((Component)img).gameObject.SetActive(false); if ((Object)(object)((Component)img).transform.parent != (Object)null) { ((Component)((Component)img).transform.parent).gameObject.SetActive(false); } } } private static void SetTooltip(GameObject go, string message) { if ((Object)(object)go == (Object)null) { return; } try { Image component = go.GetComponent(); if ((Object)(object)component != (Object)null) { ((Graphic)component).raycastTarget = true; } TooltipHoverFollower component2 = go.GetComponent(); if ((Object)(object)component2 == (Object)null) { UITooltip.Attach(go, message ?? string.Empty); } else { component2.Message = message ?? string.Empty; } } catch { } } private static void SetText(UIText text, string value, Color color) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (text != null && text.IsAlive) { text.Text = value ?? string.Empty; text.Color = color; } } private static Color StatColor(ScoreboardRow data, Color accent) { //IL_0008: 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_0016: 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) if (!data.HasStats) { return UIColors.TextMuted; } if (!data.IsAlive) { return UIColors.WithAlpha(accent, 0.55f); } return accent; } private static Color PingColor(int ping) { //IL_0004: 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_0020: 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) if (ping < 0) { return UIColors.TextMuted; } if (ping < 60) { return UIColors.Shamrock; } if (ping < 120) { return UIColors.Macaroon; } return UIColors.Rose; } } internal sealed class ScoreboardRowView { public Image Accent; public Image Background; public Image ClassIcon; public UIText ClassText; public Image Gear0; public Image Gear1; public Image Gear3; public UIText NameText; public GameObject Root; public UIText StatDmg; public UIText StatDth; public UIText StatHeal; public UIText StatKills; public UIText StatPing; public UIText StatRev; public UIText StatTgt; } internal struct IconSlot { public RectTransform Root; public Image Inner; } internal static class ScoreboardRowBuilder { public static readonly string[] Headers = new string[10] { "PLAYER", "CLASS", "GEAR", "KILLS", "TGT", "DMG", "HEAL", "DTH", "REV", "PING" }; public static readonly float[] ColWeights = new float[10] { 0.19f, 0.09f, 0.18f, 0.08f, 0.07f, 0.09f, 0.08f, 0.07f, 0.07f, 0.08f }; public static void BuildHeaderColumns(RectTransform parent) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_0096: 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) float num = 0f; float scaledFontSmall = UITheme.ScaledFontSmall; for (int i = 0; i < Headers.Length; i++) { float num2 = ColWeights[i]; TextAlignmentOptions val = (TextAlignmentOptions)((i <= 2) ? 513 : 514); UIText val2 = UIText.Create((Transform)(object)parent, "H" + i, Headers[i], scaledFontSmall, (Color?)UIColors.TextSecondary, val, false); RectTransform rect = val2.Rect; rect.anchorMin = new Vector2(num, 0f); rect.anchorMax = new Vector2(num + num2, 1f); rect.offsetMin = new Vector2(UITheme.S(6f), 0f); rect.offsetMax = new Vector2(0f - UITheme.S(4f), 0f); if ((Object)(object)val2.Tmp != (Object)null) { ((TMP_Text)val2.Tmp).fontStyle = (FontStyles)1; } num += num2; } } public static ScoreboardRowView CreateRowView(Transform parent, int index, float rowH) { //IL_0028: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_01ac: 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_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02db: 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_0339: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03f9: Unknown result type (might be due to invalid IL or missing references) //IL_0408: Expected O, but got Unknown Image val = UIFactory.CreateImage("Row" + index, parent, (index % 2 == 0) ? UIColors.EntryBg : UIColors.WithAlpha(UIColors.Surface, 0.9f), false); UIFactory.ApplyWhiteSprite(val); RectTransform rectTransform = ((Graphic)val).rectTransform; LayoutElement obj = ((Component)val).gameObject.AddComponent(); obj.minHeight = rowH; obj.preferredHeight = rowH; obj.flexibleHeight = 0f; obj.flexibleWidth = 1f; ScoreboardRowView scoreboardRowView = new ScoreboardRowView { Root = ((Component)val).gameObject, Background = val }; Image val2 = UIFactory.CreateImage("Accent", (Transform)(object)rectTransform, UIColors.BorderAccent, false); UIFactory.ApplyWhiteSprite(val2); RectTransform rectTransform2 = ((Graphic)val2).rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0f); rectTransform2.anchorMax = new Vector2(0f, 1f); rectTransform2.pivot = new Vector2(0f, 0.5f); rectTransform2.sizeDelta = new Vector2(UITheme.S(3f), 0f); rectTransform2.anchoredPosition = Vector2.zero; ((Behaviour)val2).enabled = false; scoreboardRowView.Accent = val2; float num = 0f; float height = UITheme.S(22f); float width = UITheme.S(64f); float num2 = UITheme.S(22f); float scaledFontBody = UITheme.ScaledFontBody; scoreboardRowView.NameText = CreateCellText((Transform)(object)rectTransform, "Name", num, ColWeights[0], scaledFontBody, (TextAlignmentOptions)513, bold: true); num += ColWeights[0]; float num3 = ColWeights[1]; RectTransform val3 = UIFactory.CreateRect("Class", (Transform)(object)rectTransform); val3.anchorMin = new Vector2(num, 0f); val3.anchorMax = new Vector2(num + num3, 1f); val3.offsetMin = new Vector2(UITheme.S(4f), UITheme.S(6f)); val3.offsetMax = new Vector2(0f - UITheme.S(2f), 0f - UITheme.S(6f)); IconSlot iconSlot = CreateFixedIconSlot((Transform)(object)val3, "IconSlot", num2, height); scoreboardRowView.ClassIcon = iconSlot.Inner; RectTransform root = iconSlot.Root; root.anchorMin = new Vector2(0f, 0.5f); root.anchorMax = new Vector2(0f, 0.5f); root.pivot = new Vector2(0f, 0.5f); root.anchoredPosition = Vector2.zero; scoreboardRowView.ClassText = UIText.Create((Transform)(object)val3, "Label", string.Empty, UITheme.ScaledFontSmall, (Color?)UIColors.TextPrimary, (TextAlignmentOptions)513, false); RectTransform rect = scoreboardRowView.ClassText.Rect; rect.anchorMin = new Vector2(0f, 0f); rect.anchorMax = new Vector2(1f, 1f); rect.offsetMin = new Vector2(num2 + UITheme.S(4f), 0f); rect.offsetMax = Vector2.zero; if ((Object)(object)scoreboardRowView.ClassText.Tmp != (Object)null) { ((TMP_Text)scoreboardRowView.ClassText.Tmp).fontStyle = (FontStyles)1; } num += num3; float num4 = ColWeights[2]; RectTransform val4 = UIFactory.CreateRect("Gear", (Transform)(object)rectTransform); val4.anchorMin = new Vector2(num, 0f); val4.anchorMax = new Vector2(num + num4, 1f); val4.offsetMin = new Vector2(UITheme.S(2f), UITheme.S(6f)); val4.offsetMax = new Vector2(0f - UITheme.S(2f), 0f - UITheme.S(6f)); Image obj2 = ((Component)val4).gameObject.AddComponent(); ((Graphic)obj2).color = new Color(0f, 0f, 0f, 0.01f); ((Graphic)obj2).raycastTarget = false; UIFactory.ApplyWhiteSprite(obj2); ((Component)val4).gameObject.AddComponent().showMaskGraphic = false; UIFactory.AddHorizontalLayout(((Component)val4).gameObject, UITheme.S(3f), new RectOffset(0, 0, 0, 0), (TextAnchor)3, false, false, false, false); scoreboardRowView.Gear0 = CreateFixedIconSlot((Transform)(object)val4, "G0", width, height).Inner; scoreboardRowView.Gear1 = CreateFixedIconSlot((Transform)(object)val4, "G1", width, height).Inner; scoreboardRowView.Gear3 = CreateFixedIconSlot((Transform)(object)val4, "G3", num2, height).Inner; num += num4; scoreboardRowView.StatKills = CreateCellText((Transform)(object)rectTransform, "Kills", num, ColWeights[3], scaledFontBody, (TextAlignmentOptions)514, bold: true); num += ColWeights[3]; scoreboardRowView.StatTgt = CreateCellText((Transform)(object)rectTransform, "Tgt", num, ColWeights[4], scaledFontBody, (TextAlignmentOptions)514, bold: true); num += ColWeights[4]; scoreboardRowView.StatDmg = CreateCellText((Transform)(object)rectTransform, "Dmg", num, ColWeights[5], scaledFontBody, (TextAlignmentOptions)514, bold: true); num += ColWeights[5]; scoreboardRowView.StatHeal = CreateCellText((Transform)(object)rectTransform, "Heal", num, ColWeights[6], scaledFontBody, (TextAlignmentOptions)514, bold: true); num += ColWeights[6]; scoreboardRowView.StatDth = CreateCellText((Transform)(object)rectTransform, "Dth", num, ColWeights[7], scaledFontBody, (TextAlignmentOptions)514, bold: true); num += ColWeights[7]; scoreboardRowView.StatRev = CreateCellText((Transform)(object)rectTransform, "Rev", num, ColWeights[8], scaledFontBody, (TextAlignmentOptions)514, bold: true); num += ColWeights[8]; scoreboardRowView.StatPing = CreateCellText((Transform)(object)rectTransform, "Ping", num, ColWeights[9], scaledFontBody, (TextAlignmentOptions)514, bold: true); return scoreboardRowView; } public static IconSlot CreateFixedIconSlot(Transform parent, string name, float width, float height) { //IL_0047: 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_0067: 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) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00ce: Unknown result type (might be due to invalid IL or missing references) RectTransform val = UIFactory.CreateRect(name, parent); LayoutElement obj = ((Component)val).gameObject.AddComponent(); obj.minWidth = width; obj.minHeight = height; obj.preferredWidth = width; obj.preferredHeight = height; obj.flexibleWidth = 0f; obj.flexibleHeight = 0f; val.sizeDelta = new Vector2(width, height); Image obj2 = ((Component)val).gameObject.AddComponent(); ((Graphic)obj2).color = UIColors.WithAlpha(UIColors.Surface, 0.35f); ((Graphic)obj2).raycastTarget = true; UIFactory.ApplyWhiteSprite(obj2); ((Component)val).gameObject.AddComponent().showMaskGraphic = false; Image val2 = UIFactory.CreateImage("Sprite", (Transform)(object)val, Color.white, false); val2.preserveAspect = true; RectTransform rectTransform = ((Graphic)val2).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; ((Behaviour)val2).enabled = false; return new IconSlot { Root = val, Inner = val2 }; } public static UIText CreateCellText(Transform parent, string name, float x, float w, float font, TextAlignmentOptions align, bool bold) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_003c: 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_0070: Unknown result type (might be due to invalid IL or missing references) UIText val = UIText.Create(parent, name, string.Empty, font, (Color?)UIColors.TextPrimary, align, false); RectTransform rect = val.Rect; rect.anchorMin = new Vector2(x, 0f); rect.anchorMax = new Vector2(x + w, 1f); rect.offsetMin = new Vector2(UITheme.S(4f), 0f); rect.offsetMax = new Vector2(0f - UITheme.S(2f), 0f); if (bold && (Object)(object)val.Tmp != (Object)null) { ((TMP_Text)val.Tmp).fontStyle = (FontStyles)1; ((TMP_Text)val.Tmp).characterSpacing = -1f; } return val; } } public sealed class ScoreboardUI { private const int MaxRows = 16; private const int VisibleRowCap = 4; private const float RefreshInterval = 0.2f; private readonly List _dataBuffer = new List(16); private readonly List _rows = new List(16); private Canvas _canvas; private GameObject _canvasGo; private float _gap; private float _headerH; private int _lastPlayerCount = -1; private float _nextRefresh; private float _pad; private UIPanel _panel; private RectTransform _panelRt; private float _panelW; private float _rowH; private UIScrollView _scroll; private UIText _subtitleText; private float _titleH; private UIText _titleText; public bool IsVisible { get; private set; } public void EnsureCreated() { //IL_004d: 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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: 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_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: 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_0227: 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) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0332: 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_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_0401: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0450: Unknown result type (might be due to invalid IL or missing references) //IL_045a: 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_0499: Expected O, but got Unknown if (!((Object)(object)_canvasGo != (Object)null)) { UITheme.Initialize(); _canvas = UIFactory.CreateOverlayCanvas("Scoreboard_Canvas", UITheme.WindowSortingOrder + 20); _canvasGo = ((Component)_canvas).gameObject; Image obj = UIFactory.CreateImage("Backdrop", ((Component)_canvas).transform, UIColors.WithAlpha(Color.black, 0.55f), false); UIFactory.ApplyWhiteSprite(obj); UIHelpers.SetFillParent(((Graphic)obj).rectTransform, 0f); _pad = UITheme.S(14f); _titleH = UITheme.S(36f); _headerH = UITheme.S(30f); _rowH = UITheme.S(40f); _gap = UITheme.S(4f); _panelW = UITheme.S(980f); _panelW = Mathf.Min(_panelW, (float)Screen.width * 0.9f); float num = ComputePanelHeight(4); num = Mathf.Min(num, (float)Screen.height * 0.7f); _panel = UIPanel.Create(((Component)_canvas).transform, "ScoreboardPanel", (Color?)UIColors.PanelBg, true); _panelRt = _panel.Rect; RectTransform panelRt = _panelRt; RectTransform panelRt2 = _panelRt; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); panelRt2.anchorMax = val; panelRt.anchorMin = val; _panelRt.pivot = new Vector2(0.5f, 0.5f); _panelRt.sizeDelta = new Vector2(_panelW, num); _panelRt.anchoredPosition = Vector2.zero; _titleText = UIText.Create(_panel.Content, "Title", "SCOREBOARD", UITheme.ScaledFontHeader, (Color?)UIColors.TextPrimary, (TextAlignmentOptions)513, false); RectTransform rect = _titleText.Rect; rect.anchorMin = new Vector2(0f, 1f); rect.anchorMax = new Vector2(1f, 1f); rect.pivot = new Vector2(0.5f, 1f); rect.sizeDelta = new Vector2((0f - _pad) * 2f, _titleH); rect.anchoredPosition = new Vector2(0f, 0f - _pad); if ((Object)(object)_titleText.Tmp != (Object)null) { ((TMP_Text)_titleText.Tmp).fontStyle = (FontStyles)1; } _subtitleText = UIText.Create(_panel.Content, "Subtitle", string.Empty, UITheme.ScaledFontSmall, (Color?)UIColors.TextSecondary, (TextAlignmentOptions)516, false); RectTransform rect2 = _subtitleText.Rect; rect2.anchorMin = new Vector2(0f, 1f); rect2.anchorMax = new Vector2(1f, 1f); rect2.pivot = new Vector2(1f, 1f); rect2.sizeDelta = new Vector2((0f - _pad) * 2f, _titleH); rect2.anchoredPosition = new Vector2(0f, 0f - _pad); RectTransform obj2 = CreateStrip(_panel.Content, "Header", _headerH, UIColors.SectionBar); PlaceBelow(obj2, _pad, _titleH + _pad + _gap); ScoreboardRowBuilder.BuildHeaderColumns(obj2); float num2 = _pad + _titleH + _gap + _headerH + _gap; _scroll = UIScrollView.Create(_panel.Content, "RowsScroll", true, false); RectTransform rect3 = _scroll.Rect; rect3.anchorMin = new Vector2(0f, 0f); rect3.anchorMax = new Vector2(1f, 1f); rect3.pivot = new Vector2(0.5f, 1f); rect3.offsetMin = new Vector2(_pad, _pad); rect3.offsetMax = new Vector2(0f - _pad, 0f - num2); Image component = _scroll.GameObject.GetComponent(); if ((Object)(object)component != (Object)null) { ((Graphic)component).color = UIColors.WithAlpha(Color.clear, 0f); } VerticalLayoutGroup component2 = ((Component)_scroll.Content).GetComponent(); if ((Object)(object)component2 != (Object)null) { ((HorizontalOrVerticalLayoutGroup)component2).spacing = _gap; ((LayoutGroup)component2).padding = new RectOffset(0, 0, 0, 0); ((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true; } for (int i = 0; i < 16; i++) { ScoreboardRowView scoreboardRowView = ScoreboardRowBuilder.CreateRowView((Transform)(object)_scroll.Content, i, _rowH); scoreboardRowView.Root.SetActive(false); _rows.Add(scoreboardRowView); } Hide(immediate: true); } } public void Show() { EnsureCreated(); if (!((Object)(object)_canvasGo == (Object)null)) { IsVisible = true; _canvasGo.SetActive(true); _nextRefresh = 0f; _lastPlayerCount = -1; Refresh(force: true); } } public void Hide(bool immediate = false) { IsVisible = false; try { UITooltip.Shared.Hide(); } catch { } if ((Object)(object)_canvasGo != (Object)null) { _canvasGo.SetActive(false); } } public void Tick() { if (IsVisible && Time.unscaledTime >= _nextRefresh) { Refresh(force: false); } } public void Destroy() { Hide(immediate: true); UIHelpers.DestroySafe((Object)(object)_canvasGo); _canvasGo = null; _canvas = null; _panelRt = null; _panel = null; _scroll = null; _rows.Clear(); _titleText = null; _subtitleText = null; } private void Refresh(bool force) { _nextRefresh = Time.unscaledTime + 0.2f; ScoreboardDataBuilder.Build(_dataBuffer); ApplyLayout(); ScoreboardDataBuilder.Apply(_dataBuffer, _rows, _subtitleText); } private float ComputePanelHeight(int visibleRows) { int num = Mathf.Max(1, visibleRows); return _pad * 2f + _titleH + _gap + _headerH + _gap + (float)num * _rowH + (float)Mathf.Max(0, num - 1) * _gap + _pad * 0.5f; } private void ApplyLayout() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panelRt == (Object)null) { return; } int count = _dataBuffer.Count; if (count == _lastPlayerCount && count > 0) { return; } _lastPlayerCount = count; int visibleRows = Mathf.Clamp((count <= 0) ? 1 : count, 1, 4); float num = ComputePanelHeight(visibleRows); num = Mathf.Min(num, (float)Screen.height * 0.7f); num = Mathf.Max(num, UITheme.S(160f)); _panelRt.sizeDelta = new Vector2(_panelW, num); if (_scroll != null && (Object)(object)_scroll.ScrollRect != (Object)null) { _scroll.ScrollRect.vertical = count > 4; if (count <= 4) { _scroll.ScrollRect.verticalNormalizedPosition = 1f; } } } private static RectTransform CreateStrip(Transform parent, string name, float height, Color bg) { //IL_0002: 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_0034: 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_005a: Unknown result type (might be due to invalid IL or missing references) Image obj = UIFactory.CreateImage(name, parent, bg, false); UIFactory.ApplyWhiteSprite(obj); RectTransform rectTransform = ((Graphic)obj).rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.sizeDelta = new Vector2(0f, height); return rectTransform; } private static void PlaceBelow(RectTransform rt, float horizontalPad, float topOffset) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_002b: 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_0043: 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_005a: 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_006a: 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) rt.anchoredPosition = new Vector2(0f, 0f - topOffset); float y = rt.sizeDelta.y; rt.offsetMin = new Vector2(horizontalPad, rt.offsetMin.y); rt.offsetMax = new Vector2(0f - horizontalPad, rt.offsetMax.y); rt.sizeDelta = new Vector2(rt.sizeDelta.x, y); Vector2 sizeDelta = rt.sizeDelta; sizeDelta.y = y; rt.sizeDelta = sizeDelta; } } } namespace Scoreboard.Tracking { [HarmonyPatch] internal static class MissionStatPatches { [HarmonyPatch(typeof(MissionManager), "OnMissionStarted_Client")] [HarmonyPostfix] private static void PostMissionStarted() { MissionStatTracker.OnMissionStarted(); } [HarmonyPatch(typeof(MissionManager), "FinalizeMissionData")] [HarmonyPostfix] private static void PostFinalizeMissionData() { MissionStatTracker.OnMissionEnded(); } [HarmonyPatch(typeof(PlayerData), "OnLocalPlayerDamageTarget")] [HarmonyPostfix] private static void PostDamageTarget(in DamageCallbackData data) { MissionStatTracker.OnDamageDealt(data.damageData.damage); } [HarmonyPatch(typeof(PlayerData), "OnLocalPlayerHealTarget")] [HarmonyPostfix] private static void PostHealTarget(float healing) { MissionStatTracker.OnHealingDealt(healing); } [HarmonyPatch(typeof(PlayerData), "OnLocalPlayerKillTarget")] [HarmonyPostfix] private static void PostKillTarget(in KillCallbackData data) { MissionStatTracker.OnKill(in data); } } public static class MissionStatTracker { private static bool _hasFrozenStats; private static int _frozenCoreKills; private static int _frozenTargetKills; private static int _frozenDamage; private static int _frozenHealing; private static int _frozenDeaths; private static int _frozenRevives; private static bool _accumActive; private static float _accDamage; private static float _accHealing; private static int _accCoreKills; private static int _accTargetKills; private static int _accDeaths; private static int _accRevives; private static int _lastSeenDeaths; private static int _lastSeenRepairs; public static bool HasSession { get { MissionManager mm; if (!_hasFrozenStats && !_accumActive) { return TryGetMissionManager(out mm); } return true; } } public static void ResetAll() { _hasFrozenStats = false; _frozenCoreKills = (_frozenTargetKills = (_frozenDamage = 0)); _frozenHealing = (_frozenDeaths = (_frozenRevives = 0)); ResetAccumulators(); } private static void ResetAccumulators() { _accumActive = false; _accDamage = (_accHealing = 0f); _accCoreKills = (_accTargetKills = (_accDeaths = (_accRevives = 0))); _lastSeenDeaths = (_lastSeenRepairs = 0); } public static void OnMissionStarted() { try { _hasFrozenStats = false; _frozenCoreKills = (_frozenTargetKills = (_frozenDamage = 0)); _frozenHealing = (_frozenDeaths = (_frozenRevives = 0)); ResetAccumulators(); _accumActive = true; PlayerData instance = PlayerData.Instance; if (instance != null) { _lastSeenDeaths = instance.deaths; _lastSeenRepairs = instance.playersRepaired; } if (TryGetMissionManager(out var mm)) { ref MissionPlayerData missionData = ref mm.MissionData; ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogInfo((object)($"[Scoreboard] Mission start. MissionData snapshot dmg={missionData.damageDealt:F0} kills={missionData.enemiesKilled} | " + $"PlayerData dmg={instance?.damageDealt:F0} kills={instance?.enemiesKilled}")); } } else { ManualLogSource log2 = ScoreboardPlugin.Log; if (log2 != null) { log2.LogInfo((object)"[Scoreboard] Mission start (no MissionManager yet). Accumulators armed."); } } } catch (Exception arg) { ManualLogSource log3 = ScoreboardPlugin.Log; if (log3 != null) { log3.LogError((object)$"[Scoreboard] OnMissionStarted failed: {arg}"); } } } public static void OnMissionEnded() { try { if (TryGetMissionManager(out var mm)) { ref MissionPlayerData missionData = ref mm.MissionData; _frozenCoreKills = Math.Max(Mathf.Max(0, missionData.enemiesKilled), _accCoreKills); _frozenTargetKills = Math.Max(Mathf.Max(0, missionData.targetsKilled), _accTargetKills); _frozenDamage = Math.Max(Mathf.Max(0, Mathf.FloorToInt(missionData.damageDealt)), Mathf.Max(0, Mathf.FloorToInt(_accDamage))); _frozenHealing = Math.Max(Mathf.Max(0, Mathf.FloorToInt(missionData.healingDealt)), Mathf.Max(0, Mathf.FloorToInt(_accHealing))); _frozenDeaths = Mathf.Max(0, missionData.deaths); _frozenRevives = Mathf.Max(0, missionData.playersRepaired); _hasFrozenStats = true; ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogInfo((object)($"[Scoreboard] Frozen: dmg={_frozenDamage} kills={_frozenCoreKills} " + $"heal={_frozenHealing} deaths={_frozenDeaths} rev={_frozenRevives} | " + $"MissionData dmg={missionData.damageDealt:F0} acc dmg={_accDamage:F0}")); } } else if (_accumActive) { FreezeFromAccumulators(); ManualLogSource log2 = ScoreboardPlugin.Log; if (log2 != null) { log2.LogInfo((object)$"[Scoreboard] Frozen from accumulators (no MM): dmg={_frozenDamage} kills={_frozenCoreKills}"); } } _accumActive = false; } catch (Exception arg) { ManualLogSource log3 = ScoreboardPlugin.Log; if (log3 != null) { log3.LogError((object)$"[Scoreboard] OnMissionEnded failed: {arg}"); } } } private static void FreezeFromAccumulators() { PollDeathsAndRevives(); _frozenCoreKills = _accCoreKills; _frozenTargetKills = _accTargetKills; _frozenDamage = Mathf.Max(0, Mathf.FloorToInt(_accDamage)); _frozenHealing = Mathf.Max(0, Mathf.FloorToInt(_accHealing)); _frozenDeaths = _accDeaths; _frozenRevives = _accRevives; _hasFrozenStats = true; } public static PlayerStatsSnapshot GetLocalSnapshot() { ScoreboardRow scoreboardRow = new ScoreboardRow(); FillLocalStats(scoreboardRow); ulong clientId = 0uL; try { if ((Object)(object)NetworkManager.Singleton != (Object)null) { clientId = NetworkManager.Singleton.LocalClientId; } else if ((Object)(object)Player.LocalPlayer != (Object)null) { clientId = ((NetworkBehaviour)Player.LocalPlayer).OwnerClientId; } } catch { } int pingMs = -1; try { if ((Object)(object)Player.LocalPlayer != (Object)null) { pingMs = PlayerPresentation.GetPingMs(Player.LocalPlayer); } } catch { } return new PlayerStatsSnapshot { ClientId = clientId, HasStats = scoreboardRow.HasStats, CoreKills = scoreboardRow.CoreKills, TargetKills = scoreboardRow.TargetKills, Damage = scoreboardRow.Damage, Healing = scoreboardRow.Healing, Deaths = scoreboardRow.Deaths, Revives = scoreboardRow.Revives, PingMs = pingMs }; } public static void FillLocalStats(ScoreboardRow row) { if (row == null) { return; } EnsureArmedIfMissionLive(); if (_accumActive) { PollDeathsAndRevives(); } int coreKills; int targetKills; int damage; int healing; int deaths; int revives; if (_hasFrozenStats) { row.HasStats = true; row.CoreKills = _frozenCoreKills; row.TargetKills = _frozenTargetKills; row.Damage = _frozenDamage; row.Healing = _frozenHealing; row.Deaths = _frozenDeaths; row.Revives = _frozenRevives; } else if (TryGetLiveFromMissionData(out coreKills, out targetKills, out damage, out healing, out deaths, out revives)) { row.HasStats = true; if (_accumActive) { row.CoreKills = Math.Max(coreKills, _accCoreKills); row.TargetKills = Math.Max(targetKills, _accTargetKills); row.Damage = Math.Max(damage, Mathf.Max(0, Mathf.FloorToInt(_accDamage))); row.Healing = Math.Max(healing, Mathf.Max(0, Mathf.FloorToInt(_accHealing))); } else { row.CoreKills = coreKills; row.TargetKills = targetKills; row.Damage = damage; row.Healing = healing; } row.Deaths = deaths; row.Revives = revives; } else if (_accumActive) { FillFromAccumulators(row); } else { row.HasStats = false; } } private static void FillFromAccumulators(ScoreboardRow row) { row.HasStats = true; row.CoreKills = _accCoreKills; row.TargetKills = _accTargetKills; row.Damage = Mathf.Max(0, Mathf.FloorToInt(_accDamage)); row.Healing = Mathf.Max(0, Mathf.FloorToInt(_accHealing)); row.Deaths = _accDeaths; row.Revives = _accRevives; } private static bool TryGetLiveFromMissionData(out int coreKills, out int targetKills, out int damage, out int healing, out int deaths, out int revives) { coreKills = (targetKills = (damage = (healing = (deaths = (revives = 0))))); if (!TryGetMissionManager(out var mm)) { return false; } if (mm.HasMissionEnded) { ref MissionPlayerData missionData = ref mm.MissionData; coreKills = Mathf.Max(0, missionData.enemiesKilled); targetKills = Mathf.Max(0, missionData.targetsKilled); damage = Mathf.Max(0, Mathf.FloorToInt(missionData.damageDealt)); healing = Mathf.Max(0, Mathf.FloorToInt(missionData.healingDealt)); deaths = Mathf.Max(0, missionData.deaths); revives = Mathf.Max(0, missionData.playersRepaired); return true; } PlayerData instance = PlayerData.Instance; if (instance == null) { return false; } ref MissionPlayerData missionData2 = ref mm.MissionData; coreKills = Mathf.Max(0, instance.enemiesKilled - missionData2.enemiesKilled); targetKills = Mathf.Max(0, instance.targetsKilled - missionData2.targetsKilled); damage = Mathf.Max(0, Mathf.FloorToInt(instance.damageDealt - missionData2.damageDealt)); healing = Mathf.Max(0, Mathf.FloorToInt(instance.healingDealt - missionData2.healingDealt)); deaths = Mathf.Max(0, instance.deaths - missionData2.deaths); revives = Mathf.Max(0, instance.playersRepaired - missionData2.playersRepaired); return true; } private static bool TryGetMissionManager(out MissionManager mm) { mm = null; try { mm = MissionManager.Instance; return (Object)(object)mm != (Object)null; } catch { return false; } } private static void EnsureArmedIfMissionLive() { if (_hasFrozenStats || _accumActive) { return; } try { if (TryGetMissionManager(out var mm) && mm.IsMissionActive && !mm.HasMissionEnded) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogInfo((object)"[Scoreboard] Late-arming trackers (missed start hook)."); } OnMissionStarted(); } } catch { } } private static void PollDeathsAndRevives() { try { PlayerData instance = PlayerData.Instance; if (instance != null) { if (instance.deaths > _lastSeenDeaths) { _accDeaths += instance.deaths - _lastSeenDeaths; _lastSeenDeaths = instance.deaths; } if (instance.playersRepaired > _lastSeenRepairs) { _accRevives += instance.playersRepaired - _lastSeenRepairs; _lastSeenRepairs = instance.playersRepaired; } } } catch { } } public static void OnDamageDealt(float amount) { if (_accumActive && !(amount <= 0f)) { _accDamage += amount; } } public static void OnHealingDealt(float amount) { if (_accumActive && !(amount <= 0f)) { _accHealing += amount; } } public static void OnKill(in KillCallbackData data) { //IL_000f: 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) if (!_accumActive) { return; } try { if ((((IFollowable)data.target).Type & 1) != 0) { _accTargetKills++; if (data.target is EnemyCore) { _accCoreKills++; } } } catch { } } } public static class PlayerPresentation { public static int GetPingMs(Player player) { try { if ((Object)(object)player == (Object)null) { return -1; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || (Object)(object)singleton.NetworkConfig?.NetworkTransport == (Object)null) { return -1; } if (((NetworkBehaviour)player).IsLocalPlayer) { if (singleton.IsHost || singleton.IsServer) { return 0; } ulong num = 0uL; int num2 = (int)singleton.NetworkConfig.NetworkTransport.GetCurrentRtt(num); return (num2 >= 0) ? num2 : (-1); } if (singleton.IsServer || singleton.IsHost) { int num3 = (int)singleton.NetworkConfig.NetworkTransport.GetCurrentRtt(((NetworkBehaviour)player).OwnerClientId); return (num3 >= 0) ? num3 : (-1); } return -1; } catch { return -1; } } public static void FillGearIcons(Player player, ScoreboardRow row) { if (row == null) { return; } row.GearIcon0 = (row.GearIcon1 = (row.GearIcon3 = null)); row.GearName0 = (row.GearName1 = (row.GearName3 = string.Empty)); try { if ((Object)(object)player == (Object)null) { return; } IGear[] array = null; try { array = player.Gear; } catch { return; } if (array != null) { if (array.Length != 0) { FillOneGear(array[0], out row.GearIcon0, out row.GearName0); } if (array.Length > 1) { FillOneGear(array[1], out row.GearIcon1, out row.GearName1); } if (array.Length > 3) { FillOneGear(array[3], out row.GearIcon3, out row.GearName3); } } } catch { } } public static void FillCharacterVisuals(Player player, ScoreboardRow row) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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) if (row == null) { return; } row.CharacterIcon = null; row.CharacterTooltip = string.Empty; try { if (!((Object)(object)((player != null) ? player.Character : null) == (Object)null)) { Character character = player.Character; row.CharacterName = ((!string.IsNullOrEmpty(((Dude)character).ClassName)) ? ((Dude)character).ClassName : ((Object)character).name); if ((Object)(object)character.MiniIcon != (Object)null) { row.CharacterIcon = character.MiniIcon; } else if ((Object)(object)character.RectIcon != (Object)null) { row.CharacterIcon = character.RectIcon; } else if ((Object)(object)((Dude)character).Info != (Object)null && (Object)(object)((Dude)character).Info.Icon != (Object)null) { row.CharacterIcon = ((Dude)character).Info.Icon; } row.CharacterTooltip = row.CharacterName; Color uIColor = character.UIColor; row.SetCharacterColor(uIColor.r, uIColor.g, uIColor.b, uIColor.a); } } catch { row.CharacterName = "—"; } } private static void FillOneGear(IGear gear, out Sprite icon, out string name) { icon = null; name = string.Empty; try { if (gear == null) { return; } GearInfo info = ((IUpgradable)gear).Info; if (!((Object)(object)info == (Object)null)) { icon = info.Icon; if (!string.IsNullOrEmpty(info.Name)) { name = info.Name; } else if (!string.IsNullOrEmpty(info.APIName)) { name = info.APIName; } } } catch { } } } } namespace Scoreboard.Networking { public static class PeerRegistry { private const float PeerTimeout = 20f; private static readonly Dictionary RemoteStats = new Dictionary(8); private static readonly Dictionary PeerLastSeen = new Dictionary(8); private static readonly Dictionary LastRelayTime = new Dictionary(8); private static readonly HashSet ModPeers = new HashSet(); public static int Count { get { PruneStale(); return ModPeers.Count; } } public static int RemoteStatsCount => RemoteStats.Count; public static IEnumerable> EnumerateRemoteStats() { return RemoteStats; } public static void Clear() { RemoteStats.Clear(); PeerLastSeen.Clear(); LastRelayTime.Clear(); ModPeers.Clear(); } public static void NotePeer(ulong clientId) { ModPeers.Add(clientId); PeerLastSeen[clientId] = Time.unscaledTime; } public static void StoreSnapshot(PlayerStatsSnapshot snap) { RemoteStats[snap.ClientId] = snap; PeerLastSeen[snap.ClientId] = Time.unscaledTime; } public static bool TryGetRemoteStats(ulong clientId, out PlayerStatsSnapshot snapshot) { PruneStale(); if (RemoteStats.TryGetValue(clientId, out snapshot) && snapshot.HasStats) { return true; } snapshot = default(PlayerStatsSnapshot); return false; } public static bool TryGetRemotePing(ulong clientId, out int pingMs) { pingMs = -1; if (RemoteStats.TryGetValue(clientId, out var value) && value.PingMs >= 0) { pingMs = value.PingMs; return true; } return false; } public static bool TryGetExisting(ulong clientId, out PlayerStatsSnapshot snapshot) { return RemoteStats.TryGetValue(clientId, out snapshot); } public static void TouchPeer(ulong clientId) { PeerLastSeen[clientId] = Time.unscaledTime; } public static bool ShouldRelay(ulong clientId, float minRelayInterval, float now) { if (!LastRelayTime.TryGetValue(clientId, out var value) || now - value >= minRelayInterval) { LastRelayTime[clientId] = now; return true; } return false; } public static void RemoveClient(ulong clientId) { RemoteStats.Remove(clientId); PeerLastSeen.Remove(clientId); LastRelayTime.Remove(clientId); ModPeers.Remove(clientId); } public static void PruneStale() { float unscaledTime = Time.unscaledTime; List list = null; foreach (KeyValuePair item in PeerLastSeen) { if (unscaledTime - item.Value > 20f) { if (list == null) { list = new List(4); } list.Add(item.Key); } } if (list == null) { return; } ulong num = ulong.MaxValue; try { if ((Object)(object)NetworkManager.Singleton != (Object)null) { num = NetworkManager.Singleton.LocalClientId; } } catch { } for (int i = 0; i < list.Count; i++) { ulong num2 = list[i]; if (num2 != num) { PeerLastSeen.Remove(num2); ModPeers.Remove(num2); LastRelayTime.Remove(num2); } } } } public static class ScoreboardNetwork { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnHelloReceived; public static HandleNamedMessageDelegate <1>__OnStatsReceived; public static HandleNamedMessageDelegate <2>__OnFullStateReceived; public static HandleNamedMessageDelegate <3>__OnRequestFullReceived; public static Action <4>__OnClientConnected; public static Action <5>__OnClientDisconnected; } public const string HelloMessage = "sparroh.scoreboard.hello"; public const string StatsMessage = "sparroh.scoreboard.stats"; public const string FullStateMessage = "sparroh.scoreboard.full"; public const string RequestFullMessage = "sparroh.scoreboard.reqfull"; private const float HelloInterval = 4f; private const float StatsInterval = 1f; private const float HostPushInterval = 1f; private const float PostConnectDelay = 1.5f; private const float MinRelayInterval = 0.5f; private static readonly List SendTargetBuffer = new List(8); private static bool _enabled = true; private static bool _debugLog; private static bool _handlersRegistered; private static bool _networkingDisabledForSession; private static bool _requestedFullOnce; private static float _readyAt = -1f; private static float _nextHelloTime; private static float _nextStatsTime; private static float _nextHostPushTime; private static float _nextDebugLogTime; private static PlayerStatsSnapshot _lastSent; private static NetworkManager _boundNm; public static bool IsNetworkingEnabled { get { if (_enabled && _handlersRegistered) { return !_networkingDisabledForSession; } return false; } } public static int ModPeerCount => PeerRegistry.Count; public static void SetEnabled(bool enabled) { _enabled = enabled; if (!enabled) { Shutdown(); } } public static void SetDebugLog(bool enabled) { _debugLog = enabled; } public static void Initialize() { } public static void Shutdown() { UnregisterHandlers(); PeerRegistry.Clear(); _lastSent = default(PlayerStatsSnapshot); _readyAt = -1f; _requestedFullOnce = false; _networkingDisabledForSession = false; } public static void Tick() { if (!_enabled || _networkingDisabledForSession) { return; } try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsListening) { if (_handlersRegistered) { UnregisterHandlers(); } _readyAt = -1f; _requestedFullOnce = false; } else { if (!singleton.IsServer && !singleton.IsConnectedClient) { return; } if (!_handlersRegistered || (Object)(object)_boundNm != (Object)(object)singleton) { if (TryRegisterHandlers(singleton)) { _readyAt = Time.unscaledTime + 1.5f; _nextHelloTime = _readyAt; _nextStatsTime = _readyAt + 0.25f; _nextHostPushTime = _readyAt + 0.5f; _requestedFullOnce = false; } } else { if (Time.unscaledTime < _readyAt) { return; } float unscaledTime = Time.unscaledTime; PeerRegistry.PruneStale(); if (unscaledTime >= _nextHelloTime) { _nextHelloTime = unscaledTime + 4f; SafeSendHello(singleton); } if (!singleton.IsServer && !_requestedFullOnce) { _requestedFullOnce = true; SafeRequestFull(singleton); } if (unscaledTime >= _nextStatsTime) { _nextStatsTime = unscaledTime + 1f; if (ShouldPushStats(singleton)) { SafeSendLocalStats(singleton, forceHostPush: false); } } if (singleton.IsServer && unscaledTime >= _nextHostPushTime) { _nextHostPushTime = unscaledTime + 1f; if (HasRemoteClients(singleton)) { SafeSendLocalStats(singleton, forceHostPush: true); } } } } } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] Network tick warning: " + ex.Message)); } } } public static bool TryGetRemoteStats(ulong clientId, out PlayerStatsSnapshot snapshot) { return PeerRegistry.TryGetRemoteStats(clientId, out snapshot); } public static bool TryGetRemotePing(ulong clientId, out int pingMs) { return PeerRegistry.TryGetRemotePing(clientId, out pingMs); } private static bool ShouldPushStats(NetworkManager nm) { if (MissionStatTracker.HasSession) { return true; } if (PeerRegistry.Count > 0) { return true; } return HasRemoteClients(nm); } private static bool HasRemoteClients(NetworkManager nm) { try { IReadOnlyList connectedClientsIds = nm.ConnectedClientsIds; return connectedClientsIds != null && connectedClientsIds.Count > 1; } catch { return false; } } private static bool TryRegisterHandlers(NetworkManager nm) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_009c: 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_00a7: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown try { if ((Object)(object)_boundNm != (Object)null && (Object)(object)_boundNm != (Object)(object)nm) { UnregisterHandlers(); } CustomMessagingManager customMessagingManager = nm.CustomMessagingManager; if (customMessagingManager == null) { DisableNetworkingForSession("CustomMessagingManager is null"); return false; } object obj = <>O.<0>__OnHelloReceived; if (obj == null) { HandleNamedMessageDelegate val = OnHelloReceived; <>O.<0>__OnHelloReceived = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("sparroh.scoreboard.hello", (HandleNamedMessageDelegate)obj); object obj2 = <>O.<1>__OnStatsReceived; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnStatsReceived; <>O.<1>__OnStatsReceived = val2; obj2 = (object)val2; } customMessagingManager.RegisterNamedMessageHandler("sparroh.scoreboard.stats", (HandleNamedMessageDelegate)obj2); object obj3 = <>O.<2>__OnFullStateReceived; if (obj3 == null) { HandleNamedMessageDelegate val3 = OnFullStateReceived; <>O.<2>__OnFullStateReceived = val3; obj3 = (object)val3; } customMessagingManager.RegisterNamedMessageHandler("sparroh.scoreboard.full", (HandleNamedMessageDelegate)obj3); object obj4 = <>O.<3>__OnRequestFullReceived; if (obj4 == null) { HandleNamedMessageDelegate val4 = OnRequestFullReceived; <>O.<3>__OnRequestFullReceived = val4; obj4 = (object)val4; } customMessagingManager.RegisterNamedMessageHandler("sparroh.scoreboard.reqfull", (HandleNamedMessageDelegate)obj4); nm.OnClientConnectedCallback += OnClientConnected; nm.OnClientDisconnectCallback += OnClientDisconnected; _handlersRegistered = true; _boundNm = nm; PeerRegistry.NotePeer(nm.LocalClientId); ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogInfo((object)"[Scoreboard] Named message handlers registered."); } return true; } catch (Exception ex) { DisableNetworkingForSession("Register failed: " + ex.Message); ManualLogSource log2 = ScoreboardPlugin.Log; if (log2 != null) { log2.LogError((object)$"[Scoreboard] Failed to register named messages: {ex}"); } return false; } } private static void UnregisterHandlers() { try { if ((Object)(object)_boundNm != (Object)null) { _boundNm.OnClientConnectedCallback -= OnClientConnected; _boundNm.OnClientDisconnectCallback -= OnClientDisconnected; CustomMessagingManager customMessagingManager = _boundNm.CustomMessagingManager; if (customMessagingManager != null) { customMessagingManager.UnregisterNamedMessageHandler("sparroh.scoreboard.hello"); customMessagingManager.UnregisterNamedMessageHandler("sparroh.scoreboard.stats"); customMessagingManager.UnregisterNamedMessageHandler("sparroh.scoreboard.full"); customMessagingManager.UnregisterNamedMessageHandler("sparroh.scoreboard.reqfull"); } } } catch { } _handlersRegistered = false; _boundNm = null; } private static void OnClientConnected(ulong clientId) { try { float num = Time.unscaledTime + 1.5f; if (_nextHelloTime < 0f || _nextHelloTime > num) { _nextHelloTime = num; } if (_nextStatsTime < 0f || _nextStatsTime > num + 0.25f) { _nextStatsTime = num + 0.25f; } if (_nextHostPushTime < 0f || _nextHostPushTime > num + 0.5f) { _nextHostPushTime = num + 0.5f; } LogDebug($"Client connected: {clientId}"); } catch { } } private static void OnClientDisconnected(ulong clientId) { PeerRegistry.RemoveClient(clientId); LogDebug($"Client disconnected: {clientId}"); } private static void SafeSendHello(NetworkManager nm) { //IL_001e: 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_0038: 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_0053: 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) if (!TryGetMessenger(nm, out var cmm)) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); try { byte b = 2; ((FastBufferWriter)(ref val)).WriteValueSafe(ref b, default(ForPrimitives)); ulong localClientId = nm.LocalClientId; ((FastBufferWriter)(ref val)).WriteValueSafe(ref localClientId, default(ForPrimitives)); if (nm.IsServer) { SendToRemoteClients(nm, cmm, "sparroh.scoreboard.hello", val); LogDebug($"Hello → remotes as host id={nm.LocalClientId}"); } else if (nm.IsConnectedClient) { cmm.SendNamedMessage("sparroh.scoreboard.hello", 0uL, val, (NetworkDelivery)2); LogDebug($"Hello → server id={nm.LocalClientId}"); } } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] SendHello failed: " + ex.Message)); } } finally { ((FastBufferWriter)(ref val)).Dispose(); } } private static void SafeRequestFull(NetworkManager nm) { //IL_002a: 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_003e: Unknown result type (might be due to invalid IL or missing references) if (nm.IsServer || !TryGetMessenger(nm, out var cmm)) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1); try { ulong localClientId = nm.LocalClientId; ((FastBufferWriter)(ref val)).WriteValueSafe(ref localClientId, default(ForPrimitives)); cmm.SendNamedMessage("sparroh.scoreboard.reqfull", 0uL, val, (NetworkDelivery)2); LogDebug("Requested full state from server"); } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] RequestFull failed: " + ex.Message)); } } finally { ((FastBufferWriter)(ref val)).Dispose(); } } private static void SafeSendLocalStats(NetworkManager nm, bool forceHostPush) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (!TryGetMessenger(nm, out var cmm)) { return; } PlayerStatsSnapshot snap; try { snap = MissionStatTracker.GetLocalSnapshot(); snap.ClientId = nm.LocalClientId; } catch { return; } PeerRegistry.StoreSnapshot(snap); PeerRegistry.NotePeer(snap.ClientId); if (!forceHostPush && snap.Equals(_lastSent)) { return; } try { if (nm.IsServer) { SendSnapshotToRemotes(nm, cmm, in snap); SendFullStateToRemotes(nm, cmm); LogDebug($"Host push → remotes dmg={snap.Damage} has={snap.HasStats} ping={snap.PingMs} rows={PeerRegistry.RemoteStatsCount}"); } else if (nm.IsConnectedClient) { FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(64, (Allocator)2, -1); try { SnapshotSerializer.Write(ref writer, in snap); cmm.SendNamedMessage("sparroh.scoreboard.stats", 0uL, writer, (NetworkDelivery)2); LogDebug($"Stats → server id={snap.ClientId} dmg={snap.Damage} rev={snap.Revives} has={snap.HasStats}"); } finally { ((FastBufferWriter)(ref writer)).Dispose(); } } _lastSent = snap; } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] SendLocalStats failed: " + ex.Message)); } } } private static void SendHostStatsTo(NetworkManager nm, CustomMessagingManager cmm, ulong targetClientId) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (targetClientId == nm.LocalClientId) { return; } try { PlayerStatsSnapshot snap = MissionStatTracker.GetLocalSnapshot(); snap.ClientId = nm.LocalClientId; PeerRegistry.StoreSnapshot(snap); PeerRegistry.NotePeer(snap.ClientId); FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(64, (Allocator)2, -1); try { SnapshotSerializer.Write(ref writer, in snap); cmm.SendNamedMessage("sparroh.scoreboard.stats", targetClientId, writer, (NetworkDelivery)2); LogDebug($"Host stats → {targetClientId} dmg={snap.Damage} has={snap.HasStats}"); } finally { ((FastBufferWriter)(ref writer)).Dispose(); } SendFullStateTo(nm, cmm, targetClientId); } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] SendHostStatsTo failed: " + ex.Message)); } } } private static void SendSnapshotToRemotes(NetworkManager nm, CustomMessagingManager cmm, in PlayerStatsSnapshot snap) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(64, (Allocator)2, -1); try { SnapshotSerializer.Write(ref writer, in snap); SendToRemoteClients(nm, cmm, "sparroh.scoreboard.stats", writer); } finally { ((FastBufferWriter)(ref writer)).Dispose(); } } private static void SendToRemoteClients(NetworkManager nm, CustomMessagingManager cmm, string messageName, FastBufferWriter writer) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) BuildRemoteClientList(nm, nm.LocalClientId); if (SendTargetBuffer.Count == 0) { return; } for (int i = 0; i < SendTargetBuffer.Count; i++) { try { cmm.SendNamedMessage(messageName, SendTargetBuffer[i], writer, (NetworkDelivery)2); } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)$"[Scoreboard] Send {messageName} to {SendTargetBuffer[i]} failed: {ex.Message}"); } } } } private static void SendToRemoteClientsExcept(NetworkManager nm, CustomMessagingManager cmm, string messageName, FastBufferWriter writer, ulong alsoExclude) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) BuildRemoteClientList(nm, nm.LocalClientId); if (SendTargetBuffer.Count == 0) { return; } for (int i = 0; i < SendTargetBuffer.Count; i++) { ulong num = SendTargetBuffer[i]; if (num == alsoExclude) { continue; } try { cmm.SendNamedMessage(messageName, num, writer, (NetworkDelivery)2); } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)$"[Scoreboard] Send {messageName} to {num} failed: {ex.Message}"); } } } } private static void BuildRemoteClientList(NetworkManager nm, ulong excludeClientId) { SendTargetBuffer.Clear(); try { IReadOnlyList connectedClientsIds = nm.ConnectedClientsIds; if (connectedClientsIds == null) { return; } for (int i = 0; i < connectedClientsIds.Count; i++) { ulong num = connectedClientsIds[i]; if (num != excludeClientId) { SendTargetBuffer.Add(num); } } } catch { } } private static void SendFullStateToRemotes(NetworkManager nm, CustomMessagingManager cmm) { //IL_004e: 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) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) try { PlayerStatsSnapshot localSnapshot = MissionStatTracker.GetLocalSnapshot(); localSnapshot.ClientId = nm.LocalClientId; PeerRegistry.StoreSnapshot(localSnapshot); PeerRegistry.NotePeer(localSnapshot.ClientId); } catch { } int remoteStatsCount = PeerRegistry.RemoteStatsCount; FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(Mathf.Max(16, 8 + remoteStatsCount * 52), (Allocator)2, -1); try { ushort num = (ushort)remoteStatsCount; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); foreach (KeyValuePair item in PeerRegistry.EnumerateRemoteStats()) { SnapshotSerializer.Write(ref writer, item.Value); } SendToRemoteClients(nm, cmm, "sparroh.scoreboard.full", writer); } finally { ((FastBufferWriter)(ref writer)).Dispose(); } } private static void SendFullStateTo(NetworkManager nm, CustomMessagingManager cmm, ulong targetClientId) { //IL_0059: 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_00ad: Unknown result type (might be due to invalid IL or missing references) if (targetClientId == nm.LocalClientId) { return; } try { PlayerStatsSnapshot localSnapshot = MissionStatTracker.GetLocalSnapshot(); localSnapshot.ClientId = nm.LocalClientId; PeerRegistry.StoreSnapshot(localSnapshot); PeerRegistry.NotePeer(localSnapshot.ClientId); } catch { } int remoteStatsCount = PeerRegistry.RemoteStatsCount; FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(Mathf.Max(16, 8 + remoteStatsCount * 52), (Allocator)2, -1); try { ushort num = (ushort)remoteStatsCount; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref num, default(ForPrimitives)); foreach (KeyValuePair item in PeerRegistry.EnumerateRemoteStats()) { SnapshotSerializer.Write(ref writer, item.Value); } cmm.SendNamedMessage("sparroh.scoreboard.full", targetClientId, writer, (NetworkDelivery)2); LogDebug($"Full state → client {targetClientId} rows={remoteStatsCount}"); } finally { ((FastBufferWriter)(ref writer)).Dispose(); } } private static void OnHelloReceived(ulong senderId, FastBufferReader reader) { //IL_0023: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && senderId == singleton.LocalClientId) { return; } byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe(ref b, default(ForPrimitives)); ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); if (b != 1 && b != 2) { return; } ulong num2 = ((senderId != 0L) ? senderId : num); if ((Object)(object)singleton != (Object)null && num2 == singleton.LocalClientId) { return; } PeerRegistry.NotePeer(num2); LogDebug($"Hello ← {senderId} claimed={num} modPeers={PeerRegistry.Count}"); if ((Object)(object)singleton == (Object)null) { return; } if (singleton.IsServer && TryGetMessenger(singleton, out var cmm)) { SendHostStatsTo(singleton, cmm, senderId); _nextHostPushTime = Time.unscaledTime + 1f; } else if (!singleton.IsServer) { _nextStatsTime = Mathf.Min(_nextStatsTime, Time.unscaledTime + 0.25f); if (!_requestedFullOnce) { _requestedFullOnce = true; SafeRequestFull(singleton); } } } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] Hello handler: " + ex.Message)); } } } private static void OnStatsReceived(ulong senderId, FastBufferReader reader) { //IL_014d: Unknown result type (might be due to invalid IL or missing references) try { NetworkManager singleton = NetworkManager.Singleton; if (((Object)(object)singleton != (Object)null && senderId == singleton.LocalClientId) || !SnapshotSerializer.TryRead(ref reader, out var snap)) { return; } if ((senderId != 0L || snap.ClientId == 0L) && senderId != 0L) { snap.ClientId = senderId; } if ((Object)(object)singleton != (Object)null && snap.ClientId == singleton.LocalClientId) { return; } PeerRegistry.NotePeer(snap.ClientId); PlayerStatsSnapshot snapshot; bool flag = PeerRegistry.TryGetExisting(snap.ClientId, out snapshot) && snapshot.Equals(snap); if (flag) { PeerRegistry.TouchPeer(snap.ClientId); } else { PeerRegistry.StoreSnapshot(snap); LogDebug($"Stats ← id={snap.ClientId} dmg={snap.Damage} rev={snap.Revives} ping={snap.PingMs} has={snap.HasStats}"); } if ((Object)(object)singleton == (Object)null || !singleton.IsServer || !TryGetMessenger(singleton, out var cmm)) { return; } float unscaledTime = Time.unscaledTime; if (!flag && PeerRegistry.ShouldRelay(snap.ClientId, 0.5f, unscaledTime)) { FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(64, (Allocator)2, -1); try { SnapshotSerializer.Write(ref writer, in snap); SendToRemoteClientsExcept(singleton, cmm, "sparroh.scoreboard.stats", writer, senderId); } finally { ((FastBufferWriter)(ref writer)).Dispose(); } } SendHostStatsTo(singleton, cmm, senderId); } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] Stats handler: " + ex.Message)); } } } private static void OnFullStateReceived(ulong senderId, FastBufferReader reader) { //IL_0023: 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) try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && senderId == singleton.LocalClientId) { return; } ushort num = default(ushort); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); int num2 = 0; for (int i = 0; i < num; i++) { if (!SnapshotSerializer.TryRead(ref reader, out var snap)) { break; } if (!((Object)(object)singleton != (Object)null) || snap.ClientId != singleton.LocalClientId) { PeerRegistry.NotePeer(snap.ClientId); PeerRegistry.StoreSnapshot(snap); num2++; } } LogDebug($"Full state ← from={senderId} rows={num2}/{num}"); } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] FullState handler: " + ex.Message)); } } } private static void OnRequestFullReceived(ulong senderId, FastBufferReader reader) { //IL_0035: 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) try { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && TryGetMessenger(singleton, out var cmm) && senderId != singleton.LocalClientId) { try { ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); } catch { } PeerRegistry.NotePeer(senderId); LogDebug($"Full state requested by {senderId}"); SendHostStatsTo(singleton, cmm, senderId); } } catch (Exception ex) { ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] RequestFull handler: " + ex.Message)); } } } private static void DisableNetworkingForSession(string reason) { _networkingDisabledForSession = true; try { UnregisterHandlers(); } catch { } ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogWarning((object)("[Scoreboard] Networking disabled for this session: " + reason)); } } private static bool TryGetMessenger(NetworkManager nm, out CustomMessagingManager cmm) { cmm = null; if (_networkingDisabledForSession || (Object)(object)nm == (Object)null || !nm.IsListening || !_handlersRegistered) { return false; } try { cmm = nm.CustomMessagingManager; return cmm != null; } catch { return false; } } private static void LogDebug(string msg) { if (!_debugLog) { return; } float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextDebugLogTime)) { _nextDebugLogTime = unscaledTime + 0.05f; ManualLogSource log = ScoreboardPlugin.Log; if (log != null) { log.LogInfo((object)("[Scoreboard] " + msg)); } } } } public static class SnapshotSerializer { public static void Write(ref FastBufferWriter writer, in PlayerStatsSnapshot snap) { //IL_0049: 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_005b: 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_006d: 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_007f: 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_0091: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) ulong clientId = snap.ClientId; int coreKills = snap.CoreKills; int targetKills = snap.TargetKills; int damage = snap.Damage; int healing = snap.Healing; int deaths = snap.Deaths; int revives = snap.Revives; int pingMs = snap.PingMs; bool hasStats = snap.HasStats; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref clientId, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref coreKills, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref targetKills, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref damage, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref healing, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref deaths, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref revives, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref pingMs, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref hasStats, default(ForPrimitives)); } public static bool TryRead(ref FastBufferReader reader, out PlayerStatsSnapshot snap) { //IL_000c: 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_001e: 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_0030: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_008d: 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_009f: 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) snap = default(PlayerStatsSnapshot); try { ulong clientId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe(ref clientId, default(ForPrimitives)); int coreKills = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref coreKills, default(ForPrimitives)); int targetKills = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref targetKills, default(ForPrimitives)); int damage = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref damage, default(ForPrimitives)); int healing = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref healing, default(ForPrimitives)); int deaths = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref deaths, default(ForPrimitives)); int revives = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref revives, default(ForPrimitives)); int pingMs = -1; bool hasStats = default(bool); try { ((FastBufferReader)(ref reader)).ReadValueSafe(ref pingMs, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref hasStats, default(ForPrimitives)); } catch { hasStats = false; try { ((FastBufferReader)(ref reader)).ReadValueSafe(ref hasStats, default(ForPrimitives)); } catch { } } snap = new PlayerStatsSnapshot { ClientId = clientId, CoreKills = coreKills, TargetKills = targetKills, Damage = damage, Healing = healing, Deaths = deaths, Revives = revives, PingMs = pingMs, HasStats = hasStats }; return true; } catch { return false; } } } } namespace Scoreboard.Data { public struct PlayerStatsSnapshot : IEquatable { public const byte ProtocolVersion = 2; public ulong ClientId; public int CoreKills; public int TargetKills; public int Damage; public int Healing; public int Deaths; public int Revives; public int PingMs; public bool HasStats; public bool Equals(PlayerStatsSnapshot other) { if (ClientId == other.ClientId && CoreKills == other.CoreKills && TargetKills == other.TargetKills && Damage == other.Damage && Healing == other.Healing && Deaths == other.Deaths && Revives == other.Revives && PingMs == other.PingMs) { return HasStats == other.HasStats; } return false; } public override bool Equals(object obj) { if (obj is PlayerStatsSnapshot other) { return Equals(other); } return false; } public override int GetHashCode() { return ((((((((((((((((int)ClientId * 397) ^ CoreKills) * 397) ^ TargetKills) * 397) ^ Damage) * 397) ^ Healing) * 397) ^ Deaths) * 397) ^ Revives) * 397) ^ PingMs) * 397) ^ (HasStats ? 1 : 0); } public void ApplyTo(ScoreboardRow row) { if (row != null) { row.HasStats = HasStats; if (PingMs >= 0) { row.PingMs = PingMs; } if (HasStats) { row.CoreKills = CoreKills; row.TargetKills = TargetKills; row.Damage = Damage; row.Healing = Healing; row.Deaths = Deaths; row.Revives = Revives; } } } } public sealed class ScoreboardRow { public float CharacterColorA = 1f; public float CharacterColorB = 1f; public float CharacterColorG = 1f; public float CharacterColorR = 1f; public Sprite CharacterIcon; public string CharacterName = string.Empty; public string CharacterTooltip = string.Empty; public ulong ClientId; public int CoreKills; public int Damage; public int Deaths; public Sprite GearIcon0; public Sprite GearIcon1; public Sprite GearIcon3; public string GearName0 = string.Empty; public string GearName1 = string.Empty; public string GearName3 = string.Empty; public bool HasStats; public int Healing; public bool IsAlive = true; public bool IsLocal; public string Name = string.Empty; public int PingMs = -1; public int PlayerNumber; public int Revives; public int TargetKills; public ulong UserId; public void SetCharacterColor(float r, float g, float b, float a = 1f) { CharacterColorR = r; CharacterColorG = g; CharacterColorB = b; CharacterColorA = a; } public static string FormatStat(int value, bool hasStats) { if (!hasStats) { return "—"; } return value.ToString(); } public static string FormatPing(int pingMs) { if (pingMs < 0) { return "—"; } return pingMs.ToString(); } } } namespace Scoreboard.Config { public static class ConfigManager { private const float DebounceSeconds = 0.25f; private static ConfigFile _config; private static ManualLogSource _logger; private static FileSystemWatcher _configWatcher; private static volatile bool _reloadPending; private static float _lastReloadTime; public static ConfigEntry Enabled { get; private set; } public static ConfigEntry NetworkingEnabled { get; private set; } public static ConfigEntry NetworkingDebug { get; private set; } public static ConfigEntry HoldKey { get; private set; } public static void Initialize(ConfigFile configFile, ManualLogSource log) { _config = configFile; _logger = log; Enabled = _config.Bind("General", "Enabled", true, "Master toggle for the scoreboard overlay."); NetworkingEnabled = _config.Bind("Networking", "Enabled", true, "Sync mission stats with other players who also have Scoreboard. Host must have the mod too."); NetworkingDebug = _config.Bind("Networking", "DebugLog", false, "Log hello/stats/full-state traffic to BepInEx console (off by default — was part of a log flood crash)."); HoldKey = _config.Bind("Input", "HoldKey", (Key)72, "Hold this key to show the scoreboard (default: Caps Lock)."); NetworkingEnabled.SettingChanged += OnNetworkingSettingChanged; NetworkingDebug.SettingChanged += OnNetworkingSettingChanged; ApplyNetworkingSettings(); try { SetupFileWatcher(); } catch (Exception ex) { ManualLogSource logger = _logger; if (logger != null) { logger.LogError((object)("Error setting up config file watcher: " + ex.Message)); } } } public static void Tick() { if (!_reloadPending || Time.unscaledTime - _lastReloadTime < 0.25f) { return; } _reloadPending = false; _lastReloadTime = Time.unscaledTime; try { _config.Reload(); ApplyNetworkingSettings(); ManualLogSource logger = _logger; if (logger != null) { logger.LogInfo((object)"Config reloaded from disk."); } } catch (Exception ex) { ManualLogSource logger2 = _logger; if (logger2 != null) { logger2.LogError((object)("Error reloading config: " + ex.Message)); } } } public static void Dispose() { if (NetworkingEnabled != null) { NetworkingEnabled.SettingChanged -= OnNetworkingSettingChanged; } if (NetworkingDebug != null) { NetworkingDebug.SettingChanged -= OnNetworkingSettingChanged; } if (_configWatcher != null) { _configWatcher.EnableRaisingEvents = false; _configWatcher.Changed -= OnConfigFileChanged; _configWatcher.Created -= OnConfigFileChanged; _configWatcher.Renamed -= OnConfigFileChanged; _configWatcher.Dispose(); _configWatcher = null; } } private static void ApplyNetworkingSettings() { if (NetworkingEnabled != null) { ScoreboardNetwork.SetEnabled(NetworkingEnabled.Value); } if (NetworkingDebug != null) { ScoreboardNetwork.SetDebugLog(NetworkingDebug.Value); } } private static void OnNetworkingSettingChanged(object sender, EventArgs e) { ApplyNetworkingSettings(); } private static void SetupFileWatcher() { _configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.scoreboard.cfg"); _configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileChanged; _configWatcher.EnableRaisingEvents = true; } private static void OnConfigFileChanged(object sender, FileSystemEventArgs e) { _reloadPending = true; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }