From f9264f27675d45172eca4711ce3d1331cdfac68f Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 27 Feb 2026 07:50:41 +0000 Subject: [PATCH] Phase 4: Extract ChatEventRouter and GameEventRouter - ChatEventRouter.cs: routes chat events to KillTracker, RareTracker, handles allegiance report trigger and WebSocket chat streaming - GameEventRouter.cs: routes ServerDispatch messages (0xF7B0, 0x02CF) to CharacterStats - PluginCore no longer contains OnChatText, AllChatText, NormalizeChatLine, or EchoFilter_ServerDispatch methods Co-Authored-By: Claude Opus 4.6 --- MosswartMassacre/ChatEventRouter.cs | 90 +++++++++++++ MosswartMassacre/GameEventRouter.cs | 55 ++++++++ MosswartMassacre/MosswartMassacre.csproj | 2 + MosswartMassacre/PluginCore.cs | 162 ++++++----------------- 4 files changed, 185 insertions(+), 124 deletions(-) create mode 100644 MosswartMassacre/ChatEventRouter.cs create mode 100644 MosswartMassacre/GameEventRouter.cs diff --git a/MosswartMassacre/ChatEventRouter.cs b/MosswartMassacre/ChatEventRouter.cs new file mode 100644 index 0000000..9729ffa --- /dev/null +++ b/MosswartMassacre/ChatEventRouter.cs @@ -0,0 +1,90 @@ +using System; +using System.Text.RegularExpressions; +using Decal.Adapter; +using Decal.Adapter.Wrappers; + +namespace MosswartMassacre +{ + /// + /// Routes chat events to the appropriate handler (KillTracker, RareTracker, etc.) + /// Replaces the big if/else chain in PluginCore.OnChatText. + /// + internal class ChatEventRouter + { + private readonly IPluginLogger _logger; + private readonly KillTracker _killTracker; + private RareTracker _rareTracker; + private readonly Action _onRareCountChanged; + private readonly Action _onAllegianceReport; + + internal void SetRareTracker(RareTracker rareTracker) => _rareTracker = rareTracker; + + internal ChatEventRouter( + IPluginLogger logger, + KillTracker killTracker, + RareTracker rareTracker, + Action onRareCountChanged, + Action onAllegianceReport) + { + _logger = logger; + _killTracker = killTracker; + _rareTracker = rareTracker; + _onRareCountChanged = onRareCountChanged; + _onAllegianceReport = onAllegianceReport; + } + + internal void OnChatText(object sender, ChatTextInterceptEventArgs e) + { + try + { + _killTracker.CheckForKill(e.Text); + + if (_rareTracker != null && _rareTracker.CheckForRare(e.Text, out string rareText)) + { + _killTracker.RareCount = _rareTracker.RareCount; + _onRareCountChanged?.Invoke(_rareTracker.RareCount); + } + + if (e.Color == 18 && e.Text.EndsWith("!report\"")) + { + TimeSpan elapsed = DateTime.Now - _killTracker.StatsStartTime; + string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_rareTracker?.RareCount ?? 0}"; + _logger?.Log($"[Mosswart Massacre] Reporting to allegiance: {reportMessage}"); + _onAllegianceReport?.Invoke(reportMessage); + } + } + catch (Exception ex) + { + _logger?.Log("Error processing chat message: " + ex.Message); + } + } + + /// + /// Streams all chat text to WebSocket (separate handler from the filtered one above). + /// + internal static async void AllChatText(object sender, ChatTextInterceptEventArgs e) + { + try + { + string cleaned = NormalizeChatLine(e.Text); + await WebSocket.SendChatTextAsync(e.Color, cleaned); + } + catch (Exception ex) + { + PluginCore.WriteToChat($"[WS] Chat send failed: {ex}"); + } + } + + private static string NormalizeChatLine(string raw) + { + if (string.IsNullOrEmpty(raw)) + return raw; + + var noTags = Regex.Replace(raw, "<[^>]+>", ""); + var trimmed = noTags.TrimEnd('\r', '\n'); + var collapsed = Regex.Replace(trimmed, @"[ ]{2,}", " "); + + return collapsed; + } + } +} diff --git a/MosswartMassacre/GameEventRouter.cs b/MosswartMassacre/GameEventRouter.cs new file mode 100644 index 0000000..6bda3f9 --- /dev/null +++ b/MosswartMassacre/GameEventRouter.cs @@ -0,0 +1,55 @@ +using System; +using Decal.Adapter; + +namespace MosswartMassacre +{ + /// + /// Routes EchoFilter.ServerDispatch network messages to the appropriate handlers. + /// Owns the routing of 0xF7B0 sub-events and 0x02CF to CharacterStats. + /// + internal class GameEventRouter + { + private readonly IPluginLogger _logger; + + internal GameEventRouter(IPluginLogger logger) + { + _logger = logger; + } + + internal void OnServerDispatch(object sender, NetworkMessageEventArgs e) + { + try + { + if (e.Message.Type == Constants.GameEventMessageType) + { + int eventId = (int)e.Message["event"]; + + if (eventId == Constants.AllegianceInfoEvent) + { + CharacterStats.ProcessAllegianceInfoMessage(e); + } + else if (eventId == Constants.LoginCharacterEvent) + { + CharacterStats.ProcessCharacterPropertyData(e); + } + else if (eventId == Constants.TitlesListEvent) + { + CharacterStats.ProcessTitlesMessage(e); + } + else if (eventId == Constants.SetTitleEvent) + { + CharacterStats.ProcessSetTitleMessage(e); + } + } + else if (e.Message.Type == Constants.PrivateUpdatePropertyInt64) + { + CharacterStats.ProcessPropertyInt64Update(e); + } + } + catch (Exception ex) + { + _logger?.Log($"[CharStats] ServerDispatch error: {ex.Message}"); + } + } + } +} diff --git a/MosswartMassacre/MosswartMassacre.csproj b/MosswartMassacre/MosswartMassacre.csproj index 6c55cb8..360d3e0 100644 --- a/MosswartMassacre/MosswartMassacre.csproj +++ b/MosswartMassacre/MosswartMassacre.csproj @@ -304,8 +304,10 @@ Shared\VCS_Connector.cs + + diff --git a/MosswartMassacre/PluginCore.cs b/MosswartMassacre/PluginCore.cs index 5dc5331..4b876cf 100644 --- a/MosswartMassacre/PluginCore.cs +++ b/MosswartMassacre/PluginCore.cs @@ -130,6 +130,8 @@ namespace MosswartMassacre private KillTracker _killTracker; private RareTracker _rareTracker; private InventoryMonitor _inventoryMonitor; + private ChatEventRouter _chatEventRouter; + private GameEventRouter _gameEventRouter; private CommandRouter _commandRouter; protected override void Startup() @@ -171,25 +173,6 @@ namespace MosswartMassacre } } - // Note: Startup messages will appear after character login - // Subscribe to chat message event - CoreManager.Current.ChatBoxMessage += new EventHandler(OnChatText); - CoreManager.Current.ChatBoxMessage += new EventHandler(AllChatText); - CoreManager.Current.CommandLineText += OnChatCommand; - CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete; - CoreManager.Current.CharacterFilter.Death += OnCharacterDeath; - CoreManager.Current.WorldFilter.CreateObject += OnSpawn; - CoreManager.Current.WorldFilter.CreateObject += OnPortalDetected; - CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn; - // Initialize inventory monitor (taper tracking) - _inventoryMonitor = new InventoryMonitor(this); - _staticInventoryMonitor = _inventoryMonitor; - CoreManager.Current.WorldFilter.CreateObject += _inventoryMonitor.OnInventoryCreate; - CoreManager.Current.WorldFilter.ReleaseObject += _inventoryMonitor.OnInventoryRelease; - CoreManager.Current.WorldFilter.ChangeObject += _inventoryMonitor.OnInventoryChange; - // Initialize VVS view after character login - ViewManager.ViewInit(); - // Initialize kill tracker (owns the 1-sec stats timer) _killTracker = new KillTracker( this, @@ -198,6 +181,36 @@ namespace MosswartMassacre _staticKillTracker = _killTracker; _killTracker.Start(); + // Initialize inventory monitor (taper tracking) + _inventoryMonitor = new InventoryMonitor(this); + _staticInventoryMonitor = _inventoryMonitor; + + // Initialize chat event router (rareTracker set later in LoginComplete) + _chatEventRouter = new ChatEventRouter( + this, _killTracker, null, + count => ViewManager.UpdateRareCount(count), + msg => MyHost?.Actions.InvokeChatParser($"/a {msg}")); + + // Initialize game event router + _gameEventRouter = new GameEventRouter(this); + + // Note: Startup messages will appear after character login + // Subscribe to events + CoreManager.Current.ChatBoxMessage += new EventHandler(_chatEventRouter.OnChatText); + CoreManager.Current.ChatBoxMessage += new EventHandler(ChatEventRouter.AllChatText); + CoreManager.Current.CommandLineText += OnChatCommand; + CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete; + CoreManager.Current.CharacterFilter.Death += OnCharacterDeath; + CoreManager.Current.WorldFilter.CreateObject += OnSpawn; + CoreManager.Current.WorldFilter.CreateObject += OnPortalDetected; + CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn; + CoreManager.Current.WorldFilter.CreateObject += _inventoryMonitor.OnInventoryCreate; + CoreManager.Current.WorldFilter.ReleaseObject += _inventoryMonitor.OnInventoryRelease; + CoreManager.Current.WorldFilter.ChangeObject += _inventoryMonitor.OnInventoryChange; + + // Initialize VVS view after character login + ViewManager.ViewInit(); + // Initialize vitals streaming timer vitalsTimer = new Timer(Constants.VitalsUpdateIntervalMs); vitalsTimer.Elapsed += SendVitalsUpdate; @@ -215,7 +228,7 @@ namespace MosswartMassacre // 0x0013 (character properties with luminance) fires DURING login, // BEFORE LoginComplete — must hook here to catch it CharacterStats.Init(this); - CoreManager.Current.EchoFilter.ServerDispatch += EchoFilter_ServerDispatch; + CoreManager.Current.EchoFilter.ServerDispatch += _gameEventRouter.OnServerDispatch; // Enable TLS1.2 ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; @@ -258,9 +271,9 @@ namespace MosswartMassacre // Unsubscribe from chat message event - CoreManager.Current.ChatBoxMessage -= new EventHandler(OnChatText); + CoreManager.Current.ChatBoxMessage -= new EventHandler(_chatEventRouter.OnChatText); CoreManager.Current.CommandLineText -= OnChatCommand; - CoreManager.Current.ChatBoxMessage -= new EventHandler(AllChatText); + CoreManager.Current.ChatBoxMessage -= new EventHandler(ChatEventRouter.AllChatText); CoreManager.Current.CharacterFilter.Death -= OnCharacterDeath; CoreManager.Current.WorldFilter.CreateObject -= OnSpawn; CoreManager.Current.WorldFilter.CreateObject -= OnPortalDetected; @@ -273,7 +286,7 @@ namespace MosswartMassacre CoreManager.Current.WorldFilter.ChangeObject -= _inventoryMonitor.OnInventoryChange; } // Unsubscribe from server dispatch - CoreManager.Current.EchoFilter.ServerDispatch -= EchoFilter_ServerDispatch; + CoreManager.Current.EchoFilter.ServerDispatch -= _gameEventRouter.OnServerDispatch; // Stop kill tracker _killTracker?.Stop(); @@ -372,9 +385,10 @@ namespace MosswartMassacre WriteToChat($"[ChestLooter] Initialization failed: {ex.Message}"); } - // Initialize rare tracker + // Initialize rare tracker and wire to chat router _rareTracker = new RareTracker(this); _staticRareTracker = _rareTracker; + _chatEventRouter.SetRareTracker(_rareTracker); // Apply the values _rareTracker.RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled; @@ -800,39 +814,6 @@ namespace MosswartMassacre // $"[Despawn] {mob.Name} @ (NS={c.NorthSouth:F1}, EW={c.EastWest:F1})"); } - private async void AllChatText(object sender, ChatTextInterceptEventArgs e) - { - try - { - string cleaned = NormalizeChatLine(e.Text); - - // Send to WebSocket - await WebSocket.SendChatTextAsync(e.Color, cleaned); - - // Note: Plugin message analysis is now handled by Harmony patches - } - catch (Exception ex) - { - PluginCore.WriteToChat($"[WS] Chat send failed: {ex}"); - } - } - - private static string NormalizeChatLine(string raw) - { - if (string.IsNullOrEmpty(raw)) - return raw; - - // 1) Remove all <…> tags - var noTags = Regex.Replace(raw, "<[^>]+>", ""); - - // 2) Trim trailing newline or carriage-return - var trimmed = noTags.TrimEnd('\r', '\n'); - - // 3) Collapse multiple spaces into one - var collapsed = Regex.Replace(trimmed, @"[ ]{2,}", " "); - - return collapsed; - } private void OnCharacterDeath(object sender, Decal.Adapter.Wrappers.DeathEventArgs e) { @@ -874,40 +855,8 @@ namespace MosswartMassacre } } - private void OnChatText(object sender, ChatTextInterceptEventArgs e) - { - try - { - - _killTracker.CheckForKill(e.Text); - - if (_rareTracker != null && _rareTracker.CheckForRare(e.Text, out string rareText)) - { - _killTracker.RareCount = _rareTracker.RareCount; - ViewManager.UpdateRareCount(_rareTracker.RareCount); - } - - if (e.Color == 18 && e.Text.EndsWith("!report\"")) - { - TimeSpan elapsed = DateTime.Now - _killTracker.StatsStartTime; - string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_rareTracker?.RareCount ?? 0}"; - WriteToChat($"[Mosswart Massacre] Reporting to allegiance: {reportMessage}"); - MyHost.Actions.InvokeChatParser($"/a {reportMessage}"); - } - - - - - - - } - catch (Exception ex) - { - WriteToChat("Error processing chat message: " + ex.Message); - } - } private void OnChatCommand(object sender, ChatParserInterceptEventArgs e) { try @@ -983,41 +932,6 @@ namespace MosswartMassacre } } - private void EchoFilter_ServerDispatch(object sender, NetworkMessageEventArgs e) - { - try - { - if (e.Message.Type == Constants.GameEventMessageType) - { - int eventId = (int)e.Message["event"]; - - if (eventId == Constants.AllegianceInfoEvent) - { - CharacterStats.ProcessAllegianceInfoMessage(e); - } - else if (eventId == Constants.LoginCharacterEvent) - { - CharacterStats.ProcessCharacterPropertyData(e); - } - else if (eventId == Constants.TitlesListEvent) - { - CharacterStats.ProcessTitlesMessage(e); - } - else if (eventId == Constants.SetTitleEvent) - { - CharacterStats.ProcessSetTitleMessage(e); - } - } - else if (e.Message.Type == Constants.PrivateUpdatePropertyInt64) - { - CharacterStats.ProcessPropertyInt64Update(e); - } - } - catch (Exception ex) - { - WriteToChat($"[CharStats] ServerDispatch error: {ex.Message}"); - } - } public static void WriteToChat(string message) {