Fix hot reload: prevent duplicate event handlers and timer leaks
Unsubscribe all event handlers and stop/dispose timers at the start of Startup() before re-creating objects. On first load the -= calls are no-ops; on hot reload they remove stale handlers that would otherwise compound with each reload. Also adds LoginComplete unsubscription to Shutdown() for completeness. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1ffa163501
commit
aed74984c6
2 changed files with 37 additions and 0 deletions
|
|
@ -163,6 +163,42 @@ namespace MosswartMassacre
|
|||
var isCharacterLoaded = CoreManager.Current.CharacterFilter.LoginStatus == 3;
|
||||
var needsHotReload = IsHotReload || isCharacterLoaded;
|
||||
|
||||
// Clean up old event subscriptions to prevent duplicates on hot reload.
|
||||
// C# -= with a non-subscribed handler is a no-op, so safe on first load.
|
||||
if (_chatEventRouter != null)
|
||||
CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(_chatEventRouter.OnChatText);
|
||||
CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(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;
|
||||
if (_inventoryMonitor != null)
|
||||
{
|
||||
CoreManager.Current.WorldFilter.CreateObject -= _inventoryMonitor.OnInventoryCreate;
|
||||
CoreManager.Current.WorldFilter.ReleaseObject -= _inventoryMonitor.OnInventoryRelease;
|
||||
CoreManager.Current.WorldFilter.ChangeObject -= _inventoryMonitor.OnInventoryChange;
|
||||
}
|
||||
if (_gameEventRouter != null)
|
||||
CoreManager.Current.EchoFilter.ServerDispatch -= _gameEventRouter.OnServerDispatch;
|
||||
WebSocket.OnServerCommand -= HandleServerCommand;
|
||||
|
||||
// Stop old timers before recreating (prevents timer leaks on hot reload)
|
||||
_killTracker?.Stop();
|
||||
if (vitalsTimer != null)
|
||||
{
|
||||
vitalsTimer.Stop();
|
||||
vitalsTimer.Dispose();
|
||||
vitalsTimer = null;
|
||||
}
|
||||
if (commandTimer != null)
|
||||
{
|
||||
commandTimer.Stop();
|
||||
commandTimer.Dispose();
|
||||
commandTimer = null;
|
||||
}
|
||||
|
||||
// Initialize kill tracker (owns the 1-sec stats timer)
|
||||
_killTracker = new KillTracker(
|
||||
this,
|
||||
|
|
@ -280,6 +316,7 @@ namespace MosswartMassacre
|
|||
CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(_chatEventRouter.OnChatText);
|
||||
CoreManager.Current.CommandLineText -= OnChatCommand;
|
||||
CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(ChatEventRouter.AllChatText);
|
||||
CoreManager.Current.CharacterFilter.LoginComplete -= CharacterFilter_LoginComplete;
|
||||
CoreManager.Current.CharacterFilter.Death -= OnCharacterDeath;
|
||||
CoreManager.Current.WorldFilter.CreateObject -= OnSpawn;
|
||||
CoreManager.Current.WorldFilter.CreateObject -= OnPortalDetected;
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue