Phase 2: Extract IPluginLogger and KillTracker
- Create IPluginLogger interface, PluginCore implements it - CharacterStats.cs and WebSocket.cs now use IPluginLogger instead of PluginCore.WriteToChat - Extract KillTracker.cs: owns kill detection (all 36 regex patterns), death tracking, rate calculation, and the 1-sec stats update timer - Bridge properties on PluginCore maintain backward compat for WebSocket telemetry Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4845a67c1f
commit
366cca8cb6
6 changed files with 265 additions and 160 deletions
|
|
@ -18,7 +18,7 @@ using Mag.Shared.Constants;
|
|||
namespace MosswartMassacre
|
||||
{
|
||||
[FriendlyName("Mosswart Massacre")]
|
||||
public class PluginCore : PluginBase
|
||||
public class PluginCore : PluginBase, IPluginLogger
|
||||
{
|
||||
// Hot Reload Support Properties
|
||||
private static string _assemblyDirectory = null;
|
||||
|
|
@ -47,21 +47,21 @@ namespace MosswartMassacre
|
|||
public static bool IsHotReload { get; set; }
|
||||
|
||||
internal static PluginHost MyHost;
|
||||
internal static int totalKills = 0;
|
||||
internal static int rareCount = 0;
|
||||
internal static int sessionDeaths = 0; // Deaths this session
|
||||
internal static int totalDeaths = 0; // Total deaths from character
|
||||
internal static int cachedPrismaticCount = 0; // Cached Prismatic Taper count
|
||||
internal static int lastPrismaticCount = 0; // For delta calculation
|
||||
|
||||
// Track taper items and their containers for accurate release detection
|
||||
private static readonly Dictionary<int, int> trackedTaperContainers = new Dictionary<int, int>();
|
||||
private static readonly Dictionary<int, int> lastKnownStackSizes = new Dictionary<int, int>();
|
||||
internal static DateTime lastKillTime = DateTime.Now;
|
||||
internal static double killsPer5Min = 0;
|
||||
internal static double killsPerHour = 0;
|
||||
internal static DateTime statsStartTime = DateTime.Now;
|
||||
internal static Timer updateTimer;
|
||||
// Bridge properties for WebSocket telemetry until IGameStats migration (Phase 5)
|
||||
private static KillTracker _staticKillTracker;
|
||||
internal static int totalKills => _staticKillTracker?.TotalKills ?? 0;
|
||||
internal static double killsPerHour => _staticKillTracker?.KillsPerHour ?? 0;
|
||||
internal static int sessionDeaths => _staticKillTracker?.SessionDeaths ?? 0;
|
||||
internal static int totalDeaths => _staticKillTracker?.TotalDeaths ?? 0;
|
||||
internal static DateTime statsStartTime => _staticKillTracker?.StatsStartTime ?? DateTime.Now;
|
||||
internal static DateTime lastKillTime => _staticKillTracker?.LastKillTime ?? DateTime.Now;
|
||||
private static Timer vitalsTimer;
|
||||
private static System.Windows.Forms.Timer commandTimer;
|
||||
private static Timer characterStatsTimer;
|
||||
|
|
@ -127,7 +127,8 @@ namespace MosswartMassacre
|
|||
private static DateTime _lastSent = DateTime.MinValue;
|
||||
private static readonly Queue<string> _chatQueue = new Queue<string>();
|
||||
|
||||
// Command routing
|
||||
// Managers
|
||||
private KillTracker _killTracker;
|
||||
private CommandRouter _commandRouter;
|
||||
|
||||
protected override void Startup()
|
||||
|
|
@ -186,10 +187,13 @@ namespace MosswartMassacre
|
|||
// Initialize VVS view after character login
|
||||
ViewManager.ViewInit();
|
||||
|
||||
// Initialize the timer
|
||||
updateTimer = new Timer(Constants.StatsUpdateIntervalMs);
|
||||
updateTimer.Elapsed += UpdateStats;
|
||||
updateTimer.Start();
|
||||
// Initialize kill tracker (owns the 1-sec stats timer)
|
||||
_killTracker = new KillTracker(
|
||||
this,
|
||||
(kills, per5, perHr) => ViewManager.UpdateKillStats(kills, per5, perHr),
|
||||
elapsed => ViewManager.UpdateElapsedTime(elapsed));
|
||||
_staticKillTracker = _killTracker;
|
||||
_killTracker.Start();
|
||||
|
||||
// Initialize vitals streaming timer
|
||||
vitalsTimer = new Timer(Constants.VitalsUpdateIntervalMs);
|
||||
|
|
@ -207,13 +211,15 @@ namespace MosswartMassacre
|
|||
// Initialize character stats and hook ServerDispatch early
|
||||
// 0x0013 (character properties with luminance) fires DURING login,
|
||||
// BEFORE LoginComplete — must hook here to catch it
|
||||
CharacterStats.Init();
|
||||
CharacterStats.Init(this);
|
||||
CoreManager.Current.EchoFilter.ServerDispatch += EchoFilter_ServerDispatch;
|
||||
|
||||
// Enable TLS1.2
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
//Enable vTank interface
|
||||
vTank.Enable();
|
||||
// Set logger for WebSocket
|
||||
WebSocket.SetLogger(this);
|
||||
//lyssna på commands
|
||||
WebSocket.OnServerCommand += HandleServerCommand;
|
||||
//starta inventory. Hanterar subscriptions i den med
|
||||
|
|
@ -263,13 +269,8 @@ namespace MosswartMassacre
|
|||
// Unsubscribe from server dispatch
|
||||
CoreManager.Current.EchoFilter.ServerDispatch -= EchoFilter_ServerDispatch;
|
||||
|
||||
// Stop and dispose of the timers
|
||||
if (updateTimer != null)
|
||||
{
|
||||
updateTimer.Stop();
|
||||
updateTimer.Dispose();
|
||||
updateTimer = null;
|
||||
}
|
||||
// Stop kill tracker
|
||||
_killTracker?.Stop();
|
||||
|
||||
if (vitalsTimer != null)
|
||||
{
|
||||
|
|
@ -390,8 +391,7 @@ namespace MosswartMassacre
|
|||
}
|
||||
|
||||
// Initialize death tracking
|
||||
totalDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
|
||||
sessionDeaths = 0;
|
||||
_killTracker.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));
|
||||
|
||||
// Initialize cached Prismatic Taper count
|
||||
InitializePrismaticTaperCount();
|
||||
|
|
@ -604,8 +604,7 @@ namespace MosswartMassacre
|
|||
}
|
||||
|
||||
// 6. Reinitialize death tracking
|
||||
totalDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
|
||||
// Don't reset sessionDeaths - keep the current session count
|
||||
_killTracker?.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));
|
||||
|
||||
// 7. Reinitialize cached Prismatic Taper count
|
||||
InitializePrismaticTaperCount();
|
||||
|
|
@ -995,8 +994,8 @@ namespace MosswartMassacre
|
|||
|
||||
private void OnCharacterDeath(object sender, Decal.Adapter.Wrappers.DeathEventArgs e)
|
||||
{
|
||||
sessionDeaths++;
|
||||
totalDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
|
||||
_killTracker.OnDeath();
|
||||
_killTracker.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));
|
||||
}
|
||||
|
||||
private void HandleServerCommand(CommandEnvelope env)
|
||||
|
|
@ -1038,18 +1037,13 @@ namespace MosswartMassacre
|
|||
try
|
||||
{
|
||||
|
||||
if (IsKilledByMeMessage(e.Text))
|
||||
{
|
||||
totalKills++;
|
||||
lastKillTime = DateTime.Now;
|
||||
CalculateKillsPerInterval();
|
||||
ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
||||
}
|
||||
_killTracker.CheckForKill(e.Text);
|
||||
|
||||
if (IsRareDiscoveryMessage(e.Text, out string rareText))
|
||||
{
|
||||
rareCount++;
|
||||
ViewManager.UpdateRareCount(rareCount);
|
||||
_killTracker.RareCount++;
|
||||
rareCount = _killTracker.RareCount; // sync static for now
|
||||
ViewManager.UpdateRareCount(_killTracker.RareCount);
|
||||
|
||||
if (RareMetaEnabled)
|
||||
{
|
||||
|
|
@ -1063,8 +1057,8 @@ namespace MosswartMassacre
|
|||
|
||||
if (e.Color == 18 && e.Text.EndsWith("!report\""))
|
||||
{
|
||||
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
||||
string reportMessage = $"Total Kills: {totalKills}, Kills per Hour: {killsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {rareCount}";
|
||||
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: {_killTracker.RareCount}";
|
||||
WriteToChat($"[Mosswart Massacre] Reporting to allegiance: {reportMessage}");
|
||||
MyHost.Actions.InvokeChatParser($"/a {reportMessage}");
|
||||
}
|
||||
|
|
@ -1098,24 +1092,6 @@ namespace MosswartMassacre
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateStats(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Update the elapsed time
|
||||
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
||||
ViewManager.UpdateElapsedTime(elapsed);
|
||||
|
||||
// Recalculate kill rates
|
||||
CalculateKillsPerInterval();
|
||||
ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToChat("Error updating stats: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendVitalsUpdate(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
|
|
@ -1211,67 +1187,6 @@ namespace MosswartMassacre
|
|||
}
|
||||
}
|
||||
|
||||
private void CalculateKillsPerInterval()
|
||||
{
|
||||
double minutesElapsed = (DateTime.Now - statsStartTime).TotalMinutes;
|
||||
|
||||
if (minutesElapsed > 0)
|
||||
{
|
||||
killsPer5Min = (totalKills / minutesElapsed) * 5;
|
||||
killsPerHour = (totalKills / minutesElapsed) * 60;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsKilledByMeMessage(string text)
|
||||
{
|
||||
string[] killPatterns = new string[]
|
||||
{
|
||||
@"^You flatten (?<targetname>.+)'s body with the force of your assault!$",
|
||||
@"^You bring (?<targetname>.+) to a fiery end!$",
|
||||
@"^You beat (?<targetname>.+) to a lifeless pulp!$",
|
||||
@"^You smite (?<targetname>.+) mightily!$",
|
||||
@"^You obliterate (?<targetname>.+)!$",
|
||||
@"^You run (?<targetname>.+) through!$",
|
||||
@"^You reduce (?<targetname>.+) to a sizzling, oozing mass!$",
|
||||
@"^You knock (?<targetname>.+) into next Morningthaw!$",
|
||||
@"^You split (?<targetname>.+) apart!$",
|
||||
@"^You cleave (?<targetname>.+) in twain!$",
|
||||
@"^You slay (?<targetname>.+) viciously enough to impart death several times over!$",
|
||||
@"^You reduce (?<targetname>.+) to a drained, twisted corpse!$",
|
||||
@"^Your killing blow nearly turns (?<targetname>.+) inside-out!$",
|
||||
@"^Your attack stops (?<targetname>.+) cold!$",
|
||||
@"^Your lightning coruscates over (?<targetname>.+)'s mortal remains!$",
|
||||
@"^Your assault sends (?<targetname>.+) to an icy death!$",
|
||||
@"^You killed (?<targetname>.+)!$",
|
||||
@"^The thunder of crushing (?<targetname>.+) is followed by the deafening silence of death!$",
|
||||
@"^The deadly force of your attack is so strong that (?<targetname>.+)'s ancestors feel it!$",
|
||||
@"^(?<targetname>.+)'s seared corpse smolders before you!$",
|
||||
@"^(?<targetname>.+) is reduced to cinders!$",
|
||||
@"^(?<targetname>.+) is shattered by your assault!$",
|
||||
@"^(?<targetname>.+) catches your attack, with dire consequences!$",
|
||||
@"^(?<targetname>.+) is utterly destroyed by your attack!$",
|
||||
@"^(?<targetname>.+) suffers a frozen fate!$",
|
||||
@"^(?<targetname>.+)'s perforated corpse falls before you!$",
|
||||
@"^(?<targetname>.+) is fatally punctured!$",
|
||||
@"^(?<targetname>.+)'s death is preceded by a sharp, stabbing pain!$",
|
||||
@"^(?<targetname>.+) is torn to ribbons by your assault!$",
|
||||
@"^(?<targetname>.+) is liquified by your attack!$",
|
||||
@"^(?<targetname>.+)'s last strength dissolves before you!$",
|
||||
@"^Electricity tears (?<targetname>.+) apart!$",
|
||||
@"^Blistered by lightning, (?<targetname>.+) falls!$",
|
||||
@"^(?<targetname>.+)'s last strength withers before you!$",
|
||||
@"^(?<targetname>.+) is dessicated by your attack!$",
|
||||
@"^(?<targetname>.+) is incinerated by your assault!$"
|
||||
};
|
||||
|
||||
foreach (string pattern in killPatterns)
|
||||
{
|
||||
if (Regex.IsMatch(text, pattern))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private bool IsRareDiscoveryMessage(string text, out string rareTextOnly)
|
||||
{
|
||||
rareTextOnly = null;
|
||||
|
|
@ -1316,18 +1231,13 @@ namespace MosswartMassacre
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IPluginLogger.Log(string message) => WriteToChat(message);
|
||||
|
||||
public static void RestartStats()
|
||||
{
|
||||
totalKills = 0;
|
||||
rareCount = 0;
|
||||
sessionDeaths = 0; // Reset session deaths only
|
||||
statsStartTime = DateTime.Now;
|
||||
killsPer5Min = 0;
|
||||
killsPerHour = 0;
|
||||
|
||||
WriteToChat($"Stats have been reset. Session deaths: {sessionDeaths}, Total deaths: {totalDeaths}");
|
||||
ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
||||
ViewManager.UpdateRareCount(rareCount);
|
||||
_staticKillTracker?.RestartStats();
|
||||
ViewManager.UpdateRareCount(_staticKillTracker?.RareCount ?? 0);
|
||||
}
|
||||
public static void ToggleRareMeta()
|
||||
{
|
||||
|
|
@ -1396,8 +1306,8 @@ namespace MosswartMassacre
|
|||
|
||||
_commandRouter.Register("report", args =>
|
||||
{
|
||||
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
||||
string reportMessage = $"Total Kills: {totalKills}, Kills per Hour: {killsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {rareCount}, Session Deaths: {sessionDeaths}, Total Deaths: {totalDeaths}";
|
||||
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: {_killTracker.RareCount}, Session Deaths: {_killTracker.SessionDeaths}, Total Deaths: {_killTracker.TotalDeaths}";
|
||||
WriteToChat(reportMessage);
|
||||
}, "Show current stats");
|
||||
|
||||
|
|
@ -1656,17 +1566,17 @@ namespace MosswartMassacre
|
|||
try
|
||||
{
|
||||
WriteToChat("=== Death Tracking Statistics ===");
|
||||
WriteToChat($"Session Deaths: {sessionDeaths}");
|
||||
WriteToChat($"Total Deaths: {totalDeaths}");
|
||||
WriteToChat($"Session Deaths: {_killTracker.SessionDeaths}");
|
||||
WriteToChat($"Total Deaths: {_killTracker.TotalDeaths}");
|
||||
|
||||
int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
|
||||
WriteToChat($"Character Property NumDeaths: {currentCharDeaths}");
|
||||
|
||||
if (currentCharDeaths != totalDeaths)
|
||||
if (currentCharDeaths != _killTracker.TotalDeaths)
|
||||
{
|
||||
WriteToChat($"[WARNING] Death count sync issue detected!");
|
||||
WriteToChat($"Updating totalDeaths from {totalDeaths} to {currentCharDeaths}");
|
||||
totalDeaths = currentCharDeaths;
|
||||
WriteToChat($"Updating totalDeaths from {_killTracker.TotalDeaths} to {currentCharDeaths}");
|
||||
_killTracker.SetTotalDeaths(currentCharDeaths);
|
||||
}
|
||||
|
||||
WriteToChat("Death tracking is active and will increment on character death.");
|
||||
|
|
@ -1682,14 +1592,14 @@ namespace MosswartMassacre
|
|||
try
|
||||
{
|
||||
WriteToChat("=== Manual Death Test ===");
|
||||
WriteToChat($"Current sessionDeaths variable: {sessionDeaths}");
|
||||
WriteToChat($"Current totalDeaths variable: {totalDeaths}");
|
||||
WriteToChat($"Current sessionDeaths variable: {_killTracker.SessionDeaths}");
|
||||
WriteToChat($"Current totalDeaths variable: {_killTracker.TotalDeaths}");
|
||||
|
||||
int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
|
||||
WriteToChat($"Character Property NumDeaths (43): {currentCharDeaths}");
|
||||
|
||||
sessionDeaths++;
|
||||
WriteToChat($"Manually incremented sessionDeaths to: {sessionDeaths}");
|
||||
_killTracker.OnDeath();
|
||||
WriteToChat($"Manually incremented sessionDeaths to: {_killTracker.SessionDeaths}");
|
||||
WriteToChat("Note: This doesn't simulate a real death, just tests the tracking variables.");
|
||||
|
||||
WriteToChat($"Death event subscription check:");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue