diff --git a/MosswartMassacre/DelayedCommandManager.cs b/MosswartMassacre/DelayedCommandManager.cs index bf7a400..4976e4b 100644 --- a/MosswartMassacre/DelayedCommandManager.cs +++ b/MosswartMassacre/DelayedCommandManager.cs @@ -28,8 +28,8 @@ namespace MosswartMassacre { while (delayedCommands.Count > 0 && delayedCommands[0].RunAt <= DateTime.UtcNow) { - // Use Decal_DispatchOnChatCommand to ensure other plugins can intercept - PluginCore.DispatchChatToBoxWithPluginIntercept(delayedCommands[0].Command); + PluginCore.WriteToChat($"[Debug] Executing delayed: {delayedCommands[0].Command}"); + CoreManager.Current.Actions.InvokeChatParser(delayedCommands[0].Command); delayedCommands.RemoveAt(0); } diff --git a/MosswartMassacre/PluginCore.cs b/MosswartMassacre/PluginCore.cs index 8a56b54..93f2343 100644 --- a/MosswartMassacre/PluginCore.cs +++ b/MosswartMassacre/PluginCore.cs @@ -25,20 +25,12 @@ namespace MosswartMassacre 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 trackedTaperContainers = new Dictionary(); - private static readonly Dictionary lastKnownStackSizes = new Dictionary(); 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; private static Timer vitalsTimer; - private static System.Windows.Forms.Timer commandTimer; - private static readonly Queue pendingCommands = new Queue(); public static bool RareMetaEnabled { get; set; } = true; // VVS View Management @@ -109,10 +101,6 @@ namespace MosswartMassacre CoreManager.Current.CharacterFilter.Death += OnCharacterDeath; CoreManager.Current.WorldFilter.CreateObject += OnSpawn; CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn; - // Subscribe to inventory change events for taper tracking - CoreManager.Current.WorldFilter.CreateObject += OnInventoryCreate; - CoreManager.Current.WorldFilter.ReleaseObject += OnInventoryRelease; - CoreManager.Current.WorldFilter.ChangeObject += OnInventoryChange; // Initialize VVS view after character login ViewManager.ViewInit(); @@ -126,12 +114,6 @@ namespace MosswartMassacre vitalsTimer.Elapsed += SendVitalsUpdate; vitalsTimer.Start(); - // Initialize command processing timer (Windows Forms timer for main thread) - commandTimer = new System.Windows.Forms.Timer(); - commandTimer.Interval = 10; // Process commands every 10ms - commandTimer.Tick += ProcessPendingCommands; - commandTimer.Start(); - // Note: View initialization moved to LoginComplete for VVS compatibility // Enable TLS1.2 @@ -173,10 +155,6 @@ namespace MosswartMassacre CoreManager.Current.CharacterFilter.Death -= OnCharacterDeath; CoreManager.Current.WorldFilter.CreateObject -= OnSpawn; CoreManager.Current.WorldFilter.ReleaseObject -= OnDespawn; - // Unsubscribe from inventory change events - CoreManager.Current.WorldFilter.CreateObject -= OnInventoryCreate; - CoreManager.Current.WorldFilter.ReleaseObject -= OnInventoryRelease; - CoreManager.Current.WorldFilter.ChangeObject -= OnInventoryChange; // Stop and dispose of the timers @@ -194,13 +172,6 @@ namespace MosswartMassacre vitalsTimer = null; } - if (commandTimer != null) - { - commandTimer.Stop(); - commandTimer.Dispose(); - commandTimer = null; - } - // Clean up the view ViewManager.ViewDestroy(); //Disable vtank interface @@ -218,10 +189,6 @@ namespace MosswartMassacre navVisualization = null; } - // Clean up taper tracking - trackedTaperContainers.Clear(); - lastKnownStackSizes.Clear(); - // Clean up Harmony patches DecalHarmonyClean.Cleanup(); @@ -274,178 +241,8 @@ namespace MosswartMassacre totalDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths); sessionDeaths = 0; - // Initialize cached Prismatic Taper count - InitializePrismaticTaperCount(); - } - private void InitializePrismaticTaperCount() - { - try - { - lastPrismaticCount = cachedPrismaticCount; - cachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper"); - - // Initialize tracking for existing tapers - trackedTaperContainers.Clear(); - lastKnownStackSizes.Clear(); - - foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory()) - { - if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase) && - IsPlayerOwnedContainer(wo.Container)) - { - int stackCount = wo.Values(LongValueKey.StackCount, 1); - trackedTaperContainers[wo.Id] = wo.Container; - lastKnownStackSizes[wo.Id] = stackCount; - } - } - - } - catch (Exception ex) - { - WriteToChat($"[TAPER] Error initializing count: {ex.Message}"); - cachedPrismaticCount = 0; - lastPrismaticCount = 0; - trackedTaperContainers.Clear(); - lastKnownStackSizes.Clear(); - } - } - - private bool IsPlayerOwnedContainer(int containerId) - { - try - { - // Check if it's the player's main inventory - if (containerId == CoreManager.Current.CharacterFilter.Id) - return true; - - // Check if it's one of the player's containers (side packs) - // Get the container object to verify it belongs to the player - WorldObject container = CoreManager.Current.WorldFilter[containerId]; - if (container != null && - container.ObjectClass == ObjectClass.Container && - container.Container == CoreManager.Current.CharacterFilter.Id) - { - return true; - } - - return false; - } - catch - { - return false; - } - } - - private void OnInventoryCreate(object sender, CreateObjectEventArgs e) - { - try - { - var item = e.New; - if (IsPlayerOwnedContainer(item.Container) && - item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase)) - { - lastPrismaticCount = cachedPrismaticCount; - int stackCount = item.Values(LongValueKey.StackCount, 1); - cachedPrismaticCount += stackCount; - int delta = cachedPrismaticCount - lastPrismaticCount; - - // Initialize tracking for this new taper - trackedTaperContainers[item.Id] = item.Container; - lastKnownStackSizes[item.Id] = stackCount; - - } - } - catch (Exception ex) - { - WriteToChat($"[TAPER] Error in OnInventoryCreate: {ex.Message}"); - } - } - - private void OnInventoryRelease(object sender, ReleaseObjectEventArgs e) - { - try - { - var item = e.Released; - if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase)) - { - // Check where this taper WAS before being released (not where it's going) - if (trackedTaperContainers.TryGetValue(item.Id, out int previousContainer)) - { - if (IsPlayerOwnedContainer(previousContainer)) - { - // This taper was in our inventory and is now being released - lastPrismaticCount = cachedPrismaticCount; - int stackCount = item.Values(LongValueKey.StackCount, 1); - cachedPrismaticCount -= stackCount; - } - - // Clean up tracking - trackedTaperContainers.Remove(item.Id); - lastKnownStackSizes.Remove(item.Id); - } - else - { - // Fallback: recalculate total count when untracked taper is released - lastPrismaticCount = cachedPrismaticCount; - cachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper"); - } - } - } - catch (Exception ex) - { - WriteToChat($"[TAPER] Error in OnInventoryRelease: {ex.Message}"); - } - } - - private void OnInventoryChange(object sender, ChangeObjectEventArgs e) - { - try - { - var item = e.Changed; - if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase)) - { - bool isInPlayerContainer = IsPlayerOwnedContainer(item.Container); - - // Track container location for release detection - if (isInPlayerContainer) - { - bool wasAlreadyTracked = trackedTaperContainers.ContainsKey(item.Id); - trackedTaperContainers[item.Id] = item.Container; - - // Handle stack size changes with pure delta math - int currentStack = item.Values(LongValueKey.StackCount, 1); - - // Check if this is a pickup from ground (item not previously tracked) - if (!wasAlreadyTracked) - { - // This is likely a pickup from ground - increment count - lastPrismaticCount = cachedPrismaticCount; - cachedPrismaticCount += currentStack; - } - else if (lastKnownStackSizes.TryGetValue(item.Id, out int previousStack)) - { - int stackDelta = currentStack - previousStack; - if (stackDelta != 0) - { - lastPrismaticCount = cachedPrismaticCount; - cachedPrismaticCount += stackDelta; - } - } - - lastKnownStackSizes[item.Id] = currentStack; - } - // Item is no longer in player containers - // DON'T clean up tracking here - let OnInventoryRelease handle cleanup - // This ensures tracking data is available for the Release event - } - } - catch (Exception ex) - { - WriteToChat($"[TAPER] Error in OnInventoryChange: {ex.Message}"); - } - } private async void OnSpawn(object sender, CreateObjectEventArgs e) { @@ -521,38 +318,10 @@ namespace MosswartMassacre private void HandleServerCommand(CommandEnvelope env) { - // This is called from WebSocket thread - queue for main thread execution - lock (pendingCommands) - { - pendingCommands.Enqueue(env.Command); - } + // Skicka commands + DispatchChatToBoxWithPluginIntercept(env.Command); + CoreManager.Current.Actions.InvokeChatParser($"/a Executed '{env.Command}' from Mosswart Overlord"); } - - private void ProcessPendingCommands(object sender, EventArgs e) - { - // This runs on the main UI thread via Windows Forms timer - string command = null; - - lock (pendingCommands) - { - if (pendingCommands.Count > 0) - command = pendingCommands.Dequeue(); - } - - if (command != null) - { - try - { - // Execute ALL WebSocket commands on main thread - fast and reliable - DispatchChatToBoxWithPluginIntercept(command); - } - catch (Exception ex) - { - WriteToChat($"[WS] Command execution error: {ex.Message}"); - } - } - } - private void OnChatText(object sender, ChatTextInterceptEventArgs e) { try @@ -919,8 +688,6 @@ namespace MosswartMassacre WriteToChat("/mm testprismatic - Test Prismatic Taper detection and icon lookup"); WriteToChat("/mm deathstats - Show current death tracking statistics"); WriteToChat("/mm testdeath - Manual death tracking test and diagnostics"); - WriteToChat("/mm testtaper - Test cached Prismatic Taper tracking"); - WriteToChat("/mm debugtaper - Show detailed taper tracking debug info"); WriteToChat("/mm gui - Manually initialize/reinitialize GUI"); break; case "report": @@ -946,6 +713,7 @@ namespace MosswartMassacre WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}"); ViewManager.SetRareMetaToggleState(RareMetaEnabled); // <-- sync the UI break; + case "http": if (args.Length > 1) { @@ -1232,111 +1000,6 @@ namespace MosswartMassacre } break; - case "testtaper": - try - { - WriteToChat("=== Cached Taper Tracking Test ==="); - WriteToChat($"Cached Count: {cachedPrismaticCount}"); - WriteToChat($"Last Count: {lastPrismaticCount}"); - - // Compare with Utils function - int utilsCount = Utils.GetItemStackSize("Prismatic Taper"); - WriteToChat($"Utils Count: {utilsCount}"); - - if (cachedPrismaticCount == utilsCount) - { - WriteToChat("[OK] Cached count matches Utils count"); - } - else - { - WriteToChat($"[WARNING] Count mismatch! Cached: {cachedPrismaticCount}, Utils: {utilsCount}"); - WriteToChat("Refreshing cached count..."); - InitializePrismaticTaperCount(); - } - - WriteToChat("=== Container Analysis ==="); - int mainPackCount = 0; - int sidePackCount = 0; - int playerId = CoreManager.Current.CharacterFilter.Id; - - foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory()) - { - if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase)) - { - int stackCount = wo.Values(LongValueKey.StackCount, 1); - if (wo.Container == playerId) - { - mainPackCount += stackCount; - } - else - { - sidePackCount += stackCount; - } - } - } - - WriteToChat($"Main Pack Tapers: {mainPackCount}"); - WriteToChat($"Side Pack Tapers: {sidePackCount}"); - WriteToChat($"Total: {mainPackCount + sidePackCount}"); - - WriteToChat("=== Event System Status ==="); - WriteToChat($"Tracking {trackedTaperContainers.Count} taper stacks for delta detection"); - WriteToChat($"Known stack sizes: {lastKnownStackSizes.Count} items"); - WriteToChat("Pure delta tracking - NO expensive inventory scans during events!"); - WriteToChat("Now tracks: consumption, drops, trades, container moves"); - WriteToChat("Try moving tapers between containers and casting spells!"); - } - catch (Exception ex) - { - WriteToChat($"Taper test error: {ex.Message}"); - } - break; - - case "debugtaper": - try - { - WriteToChat("=== Taper Tracking Debug Info ==="); - WriteToChat($"Cached Count: {cachedPrismaticCount}"); - WriteToChat($"Last Count: {lastPrismaticCount}"); - WriteToChat($"Tracked Containers: {trackedTaperContainers.Count}"); - WriteToChat($"Known Stack Sizes: {lastKnownStackSizes.Count}"); - - if (trackedTaperContainers.Count > 0) - { - WriteToChat("=== Tracked Taper Details ==="); - foreach (var kvp in trackedTaperContainers) - { - int itemId = kvp.Key; - int containerId = kvp.Value; - int stackSize = lastKnownStackSizes.TryGetValue(itemId, out int size) ? size : -1; - string containerType = containerId == CoreManager.Current.CharacterFilter.Id ? "main pack" : "side pack"; - WriteToChat($" Item {itemId}: {containerType} (container {containerId}), stack: {stackSize}"); - } - } - else - { - WriteToChat("No tapers currently tracked!"); - } - - // Cross-check with actual inventory - WriteToChat("=== Cross-Check with Actual Inventory ==="); - int actualCount = Utils.GetItemStackSize("Prismatic Taper"); - WriteToChat($"Utils.GetItemStackSize: {actualCount}"); - if (cachedPrismaticCount != actualCount) - { - WriteToChat($"[WARNING] Count mismatch! Cached: {cachedPrismaticCount}, Actual: {actualCount}"); - } - else - { - WriteToChat("[OK] Cached count matches actual count"); - } - } - catch (Exception ex) - { - WriteToChat($"Debug taper error: {ex.Message}"); - } - break; - case "finditem": if (args.Length > 1) { diff --git a/MosswartMassacre/Properties/AssemblyInfo.cs b/MosswartMassacre/Properties/AssemblyInfo.cs index cad7b23..d23b09e 100644 --- a/MosswartMassacre/Properties/AssemblyInfo.cs +++ b/MosswartMassacre/Properties/AssemblyInfo.cs @@ -26,5 +26,5 @@ using System.Runtime.InteropServices; // Minor Version // Build Number // Revision -[assembly: AssemblyVersion("4.0.0.2")] -[assembly: AssemblyFileVersion("4.0.0.2")] \ No newline at end of file +[assembly: AssemblyVersion("4.0.0.0")] +[assembly: AssemblyFileVersion("4.0.0.0")] \ No newline at end of file diff --git a/MosswartMassacre/Telemetry.cs b/MosswartMassacre/Telemetry.cs index b9f8e14..964a8f7 100644 --- a/MosswartMassacre/Telemetry.cs +++ b/MosswartMassacre/Telemetry.cs @@ -88,7 +88,7 @@ namespace MosswartMassacre deaths = PluginCore.sessionDeaths.ToString(), total_deaths = PluginCore.totalDeaths.ToString(), rares_found = PluginCore.rareCount, - prismatic_taper_count = PluginCore.cachedPrismaticCount.ToString(), + prismatic_taper_count = Utils.GetItemStackSize("Prismatic Taper").ToString(), vt_state = VtankControl.VtGetMetaState(), }; diff --git a/MosswartMassacre/WebSocket.cs b/MosswartMassacre/WebSocket.cs index cbad25d..37f7dc5 100644 --- a/MosswartMassacre/WebSocket.cs +++ b/MosswartMassacre/WebSocket.cs @@ -36,13 +36,17 @@ namespace MosswartMassacre private const int IntervalSec = 5; private static string SessionId = ""; - // ─── cached prismatic taper count ─── (now handled by PluginCore event system) + // ─── cached prismatic taper count ─────────── + private static int _cachedPrismaticTaperCount = 0; + private static DateTime _lastPrismaticTaperUpdate = DateTime.MinValue; + private static readonly TimeSpan PrismaticTaperCacheInterval = TimeSpan.FromMinutes(10); // ─── runtime state ────────────────────────── private static ClientWebSocket _ws; private static CancellationTokenSource _cts; private static bool _enabled; private static readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1); + private static readonly SynchronizationContext _uiCtx = SynchronizationContext.Current; /// /// Fires when a valid CommandEnvelope arrives for this character. @@ -144,8 +148,17 @@ namespace MosswartMassacre CoreManager.Current.CharacterFilter.Name, StringComparison.OrdinalIgnoreCase)) { - // Fire event immediately - let PluginCore handle threading - OnServerCommand?.Invoke(env); + _uiCtx.Post(_ => + { + try + { + OnServerCommand?.Invoke(env); // now on the correct thread + } + catch (Exception ex) + { + PluginCore.WriteToChat($"[CMD] {ex.Message}"); + } + }, null); } } }); @@ -311,7 +324,29 @@ namespace MosswartMassacre // ─── payload builder ────────────────────────────── - // Removed old cache system - now using PluginCore.cachedPrismaticCount + private static void UpdatePrismaticTaperCache() + { + try + { + _cachedPrismaticTaperCount = Utils.GetItemStackSize("Prismatic Taper"); + _lastPrismaticTaperUpdate = DateTime.Now; + PluginCore.WriteToChat($"[WebSocket] Updated prismatic taper cache: {_cachedPrismaticTaperCount}"); + } + catch (Exception ex) + { + PluginCore.WriteToChat($"[WebSocket] Failed to update prismatic taper cache: {ex.Message}"); + } + } + + private static int GetCachedPrismaticTaperCount() + { + // Update cache if it's stale (older than 2 minutes) + if (DateTime.Now - _lastPrismaticTaperUpdate > PrismaticTaperCacheInterval) + { + UpdatePrismaticTaperCache(); + } + return _cachedPrismaticTaperCount; + } private static string BuildPayloadJson() { @@ -332,7 +367,7 @@ namespace MosswartMassacre onlinetime = (DateTime.Now - PluginCore.statsStartTime).ToString(@"dd\.hh\:mm\:ss"), deaths = PluginCore.sessionDeaths.ToString(), total_deaths = PluginCore.totalDeaths.ToString(), - prismatic_taper_count = PluginCore.cachedPrismaticCount.ToString(), + prismatic_taper_count = GetCachedPrismaticTaperCount().ToString(), vt_state = VtankControl.VtGetMetaState(), mem_mb = tele.MemoryBytes, cpu_pct = tele.GetCpuUsage(), diff --git a/MosswartMassacre/bin/Release/MosswartMassacre.dll b/MosswartMassacre/bin/Release/MosswartMassacre.dll index 9a81c05..a395bec 100644 Binary files a/MosswartMassacre/bin/Release/MosswartMassacre.dll and b/MosswartMassacre/bin/Release/MosswartMassacre.dll differ