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