Added hot reload
This commit is contained in:
parent
bb493febb4
commit
73ba7082d8
16 changed files with 1203 additions and 398 deletions
|
|
@ -20,6 +20,32 @@ namespace MosswartMassacre
|
|||
[FriendlyName("Mosswart Massacre")]
|
||||
public class PluginCore : PluginBase
|
||||
{
|
||||
// Hot Reload Support Properties
|
||||
private static string _assemblyDirectory = null;
|
||||
public static string AssemblyDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_assemblyDirectory == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_assemblyDirectory = System.IO.Path.GetDirectoryName(typeof(PluginCore).Assembly.Location);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_assemblyDirectory = Environment.CurrentDirectory;
|
||||
}
|
||||
}
|
||||
return _assemblyDirectory;
|
||||
}
|
||||
set
|
||||
{
|
||||
_assemblyDirectory = value;
|
||||
}
|
||||
}
|
||||
public static bool IsHotReload { get; set; }
|
||||
|
||||
internal static PluginHost MyHost;
|
||||
internal static int totalKills = 0;
|
||||
internal static int rareCount = 0;
|
||||
|
|
@ -88,6 +114,10 @@ namespace MosswartMassacre
|
|||
public static bool AggressiveChatStreamingEnabled { get; set; } = true;
|
||||
private MossyInventory _inventoryLogger;
|
||||
public static NavVisualization navVisualization;
|
||||
|
||||
// Quest Management for always-on quest streaming
|
||||
public static QuestManager questManager;
|
||||
private static Timer questStreamingTimer;
|
||||
|
||||
private static Queue<string> rareMessageQueue = new Queue<string>();
|
||||
private static DateTime _lastSent = DateTime.MinValue;
|
||||
|
|
@ -97,9 +127,41 @@ namespace MosswartMassacre
|
|||
{
|
||||
try
|
||||
{
|
||||
MyHost = Host;
|
||||
// Set MyHost - for hot reload scenarios, Host might be null
|
||||
if (Host != null)
|
||||
{
|
||||
MyHost = Host;
|
||||
}
|
||||
else if (MyHost == null)
|
||||
{
|
||||
// Hot reload fallback - this is okay, WriteToChat will handle it
|
||||
MyHost = null;
|
||||
}
|
||||
|
||||
// Check if this is a hot reload
|
||||
var isCharacterLoaded = CoreManager.Current.CharacterFilter.LoginStatus == 3;
|
||||
if (IsHotReload || isCharacterLoaded)
|
||||
{
|
||||
// Hot reload detected - reinitialize connections and state
|
||||
WriteToChat("[INFO] Hot reload detected - reinitializing plugin");
|
||||
|
||||
// Reload settings if character is already logged in
|
||||
if (isCharacterLoaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
WriteToChat("Hot reload - reinitializing character-dependent systems");
|
||||
// Don't call LoginComplete - create hot reload specific initialization
|
||||
InitializeForHotReload();
|
||||
WriteToChat("[INFO] Hot reload initialization complete");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToChat($"[ERROR] Hot reload initialization failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Note: Startup messages will appear after character login
|
||||
// Subscribe to chat message event
|
||||
CoreManager.Current.ChatBoxMessage += new EventHandler<ChatTextInterceptEventArgs>(OnChatText);
|
||||
|
|
@ -165,7 +227,7 @@ namespace MosswartMassacre
|
|||
PluginSettings.Save();
|
||||
if (TelemetryEnabled)
|
||||
Telemetry.Stop(); // ensure no dangling timer / HttpClient
|
||||
WriteToChat("Mosswart Massacre is shutting down...");
|
||||
WriteToChat("Mosswart Massacre is shutting down!!!!!");
|
||||
|
||||
// Unsubscribe from chat message event
|
||||
CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(OnChatText);
|
||||
|
|
@ -203,6 +265,22 @@ namespace MosswartMassacre
|
|||
commandTimer = null;
|
||||
}
|
||||
|
||||
// Stop and dispose quest streaming timer
|
||||
if (questStreamingTimer != null)
|
||||
{
|
||||
questStreamingTimer.Stop();
|
||||
questStreamingTimer.Elapsed -= OnQuestStreamingUpdate;
|
||||
questStreamingTimer.Dispose();
|
||||
questStreamingTimer = null;
|
||||
}
|
||||
|
||||
// Dispose quest manager
|
||||
if (questManager != null)
|
||||
{
|
||||
questManager.Dispose();
|
||||
questManager = null;
|
||||
}
|
||||
|
||||
// Clean up the view
|
||||
ViewManager.ViewDestroy();
|
||||
//Disable vtank interface
|
||||
|
|
@ -279,6 +357,160 @@ namespace MosswartMassacre
|
|||
// Initialize cached Prismatic Taper count
|
||||
InitializePrismaticTaperCount();
|
||||
|
||||
// Initialize quest manager for always-on quest streaming
|
||||
try
|
||||
{
|
||||
questManager = new QuestManager();
|
||||
questManager.RefreshQuests();
|
||||
|
||||
// Initialize quest streaming timer (30 seconds)
|
||||
questStreamingTimer = new Timer(30000);
|
||||
questStreamingTimer.Elapsed += OnQuestStreamingUpdate;
|
||||
questStreamingTimer.AutoReset = true;
|
||||
questStreamingTimer.Start();
|
||||
|
||||
WriteToChat("[OK] Quest streaming initialized");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToChat($"[ERROR] Quest streaming initialization failed: {ex.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region Quest Streaming Methods
|
||||
private static void OnQuestStreamingUpdate(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Stream high priority quest data via WebSocket
|
||||
if (WebSocketEnabled && questManager?.QuestList != null && questManager.QuestList.Count > 0)
|
||||
{
|
||||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
// Find and stream priority quests (deduplicated by quest ID)
|
||||
var priorityQuests = questManager.QuestList
|
||||
.Where(q => IsHighPriorityQuest(q.Id))
|
||||
.GroupBy(q => q.Id)
|
||||
.Select(g => g.First()) // Take first occurrence of each quest ID
|
||||
.ToList();
|
||||
|
||||
foreach (var quest in priorityQuests)
|
||||
{
|
||||
try
|
||||
{
|
||||
string questName = questManager.GetFriendlyQuestName(quest.Id);
|
||||
long timeRemaining = quest.ExpireTime - currentTime;
|
||||
string countdown = FormatCountdown(timeRemaining);
|
||||
|
||||
// Stream quest data
|
||||
System.Threading.Tasks.Task.Run(() => WebSocket.SendQuestDataAsync(questName, countdown));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Silently handle individual quest streaming errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Silently handle quest streaming errors to avoid spam
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsHighPriorityQuest(string questId)
|
||||
{
|
||||
return questId == "stipendtimer_0812" || // Changed from stipendtimer_monthly to stipendtimer_0812
|
||||
questId == "augmentationblankgemacquired" ||
|
||||
questId == "insatiableeaterjaw";
|
||||
}
|
||||
|
||||
private static string FormatCountdown(long seconds)
|
||||
{
|
||||
if (seconds <= 0)
|
||||
return "READY";
|
||||
|
||||
var timeSpan = TimeSpan.FromSeconds(seconds);
|
||||
|
||||
if (timeSpan.TotalDays >= 1)
|
||||
return $"{(int)timeSpan.TotalDays}d {timeSpan.Hours:D2}h";
|
||||
else if (timeSpan.TotalHours >= 1)
|
||||
return $"{timeSpan.Hours}h {timeSpan.Minutes:D2}m";
|
||||
else if (timeSpan.TotalMinutes >= 1)
|
||||
return $"{timeSpan.Minutes}m {timeSpan.Seconds:D2}s";
|
||||
else
|
||||
return $"{timeSpan.Seconds}s";
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void InitializeForHotReload()
|
||||
{
|
||||
// This method handles initialization that depends on character being logged in
|
||||
// Similar to LoginComplete but designed for hot reload scenarios
|
||||
|
||||
WriteToChat("Mosswart Massacre hot reload initialization started!");
|
||||
|
||||
// 1. Initialize settings - CRITICAL first step
|
||||
PluginSettings.Initialize();
|
||||
|
||||
// 2. Apply the values from settings
|
||||
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
|
||||
WebSocketEnabled = PluginSettings.Instance.WebSocketEnabled;
|
||||
RemoteCommandsEnabled = PluginSettings.Instance.RemoteCommandsEnabled;
|
||||
HttpServerEnabled = PluginSettings.Instance.HttpServerEnabled;
|
||||
TelemetryEnabled = PluginSettings.Instance.TelemetryEnabled;
|
||||
CharTag = PluginSettings.Instance.CharTag;
|
||||
|
||||
// 3. Update UI with current settings
|
||||
ViewManager.SetRareMetaToggleState(RareMetaEnabled);
|
||||
ViewManager.RefreshSettingsFromConfig();
|
||||
|
||||
// 4. Restart services if they were enabled (stop first, then start)
|
||||
if (TelemetryEnabled)
|
||||
{
|
||||
Telemetry.Stop(); // Stop existing
|
||||
Telemetry.Start(); // Restart
|
||||
}
|
||||
|
||||
if (WebSocketEnabled)
|
||||
{
|
||||
WebSocket.Stop(); // Stop existing
|
||||
WebSocket.Start(); // Restart
|
||||
}
|
||||
|
||||
if (HttpServerEnabled)
|
||||
{
|
||||
HttpCommandServer.Stop(); // Stop existing
|
||||
HttpCommandServer.Start(); // Restart
|
||||
}
|
||||
|
||||
// 5. Initialize Harmony patches (only if not already done)
|
||||
// Note: Harmony patches are global and don't need reinitialization
|
||||
if (!DecalHarmonyClean.IsActive())
|
||||
{
|
||||
try
|
||||
{
|
||||
bool success = DecalHarmonyClean.Initialize();
|
||||
if (success)
|
||||
WriteToChat("[OK] Plugin message interception active");
|
||||
else
|
||||
WriteToChat("[FAIL] Could not initialize message interception");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToChat($"[ERROR] Harmony initialization failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Reinitialize death tracking
|
||||
totalDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
|
||||
// Don't reset sessionDeaths - keep the current session count
|
||||
|
||||
// 7. Reinitialize cached Prismatic Taper count
|
||||
InitializePrismaticTaperCount();
|
||||
|
||||
WriteToChat("Hot reload initialization completed!");
|
||||
}
|
||||
|
||||
private void InitializePrismaticTaperCount()
|
||||
|
|
@ -658,7 +890,6 @@ namespace MosswartMassacre
|
|||
{
|
||||
try
|
||||
{
|
||||
// WriteToChat($"[Debug] Chat Color: {e.Color}, Message: {e.Text}");
|
||||
|
||||
if (IsKilledByMeMessage(e.Text))
|
||||
{
|
||||
|
|
@ -889,7 +1120,31 @@ namespace MosswartMassacre
|
|||
}
|
||||
public static void WriteToChat(string message)
|
||||
{
|
||||
MyHost.Actions.AddChatText("[Mosswart Massacre] " + message, 0, 1);
|
||||
try
|
||||
{
|
||||
// For hot reload scenarios where MyHost might be null, use CoreManager directly
|
||||
if (MyHost != null)
|
||||
{
|
||||
MyHost.Actions.AddChatText("[Mosswart Massacre] " + message, 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hot reload fallback - use CoreManager directly like the original template
|
||||
CoreManager.Current.Actions.AddChatText("[Mosswart Massacre] " + message, 1);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Last resort fallback - try CoreManager even if MyHost was supposed to work
|
||||
try
|
||||
{
|
||||
CoreManager.Current.Actions.AddChatText($"[Mosswart Massacre] {message} (WriteToChat error: {ex.Message})", 1);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Give up - can't write to chat at all
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void RestartStats()
|
||||
{
|
||||
|
|
@ -997,6 +1252,7 @@ namespace MosswartMassacre
|
|||
WriteToChat("Usage: /mm ws <enable|disable>");
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
WriteToChat("Usage: /mm ws <enable|disable>");
|
||||
|
|
@ -1019,10 +1275,9 @@ namespace MosswartMassacre
|
|||
WriteToChat("/mm harmonyraw - Show raw intercepted messages (debug output)");
|
||||
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");
|
||||
WriteToChat("/mm gui - Manually initialize/reinitialize GUI!!!");
|
||||
break;
|
||||
case "report":
|
||||
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
||||
|
|
@ -1134,7 +1389,6 @@ namespace MosswartMassacre
|
|||
WriteToChat("=== Harmony Patch Status (UtilityBelt Pattern) ===");
|
||||
WriteToChat($"Patches Active: {DecalHarmonyClean.IsActive()}");
|
||||
WriteToChat($"Messages Intercepted: {DecalHarmonyClean.GetMessagesIntercepted()}");
|
||||
WriteToChat($"Debug Streaming: {AggressiveChatStreamingEnabled}");
|
||||
WriteToChat($"WebSocket Streaming: {(AggressiveChatStreamingEnabled && WebSocketEnabled ? "ACTIVE" : "INACTIVE")}");
|
||||
|
||||
// Test Harmony availability
|
||||
|
|
@ -1186,31 +1440,7 @@ namespace MosswartMassacre
|
|||
|
||||
|
||||
case "harmonyraw":
|
||||
try
|
||||
{
|
||||
WriteToChat("=== Raw Harmony Interception Log ===");
|
||||
var debugEntries = DecalHarmonyClean.GetDebugLog();
|
||||
if (debugEntries.Length == 0)
|
||||
{
|
||||
WriteToChat("No debug entries found. Enable debug streaming first: /mm decaldebug enable");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteToChat($"Last {debugEntries.Length} intercepted messages:");
|
||||
foreach (var entry in debugEntries.Skip(Math.Max(0, debugEntries.Length - 10)))
|
||||
{
|
||||
WriteToChat($" {entry}");
|
||||
}
|
||||
if (debugEntries.Length > 10)
|
||||
{
|
||||
WriteToChat($"... ({debugEntries.Length - 10} more entries)");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToChat($"Debug log error: {ex.Message}");
|
||||
}
|
||||
// Debug functionality removed
|
||||
break;
|
||||
|
||||
case "initgui":
|
||||
|
|
@ -1394,48 +1624,7 @@ namespace MosswartMassacre
|
|||
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}");
|
||||
}
|
||||
// Debug functionality removed
|
||||
break;
|
||||
|
||||
case "finditem":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue