Skip to content

Commit dcd7ccb

Browse files
committed
Cleaned up mudlog initialization checks and added command cleanup
- Removed redundant mudlog.IsInitialized() checks throughout codebase - Added unregisterCommands() methods to combat modules for proper cleanup - Added RemoveUserCommand/RemoveMobCommand methods to plugin API - Added architectural documentation for combat registry initialization - Fixed BaseTimer initialization to create stopChan in Start() - Documented combat config integration with main config system
1 parent e559771 commit dcd7ccb

File tree

11 files changed

+82
-38
lines changed

11 files changed

+82
-38
lines changed

internal/combat/common_timer.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func NewBaseTimer(name string, updateFunc func()) *BaseTimer {
2323
return &BaseTimer{
2424
name: name,
2525
updateFunc: updateFunc,
26-
stopChan: make(chan bool),
26+
// stopChan created in Start() as buffered channel
2727
}
2828
}
2929

@@ -45,9 +45,7 @@ func (bt *BaseTimer) Start() error {
4545
bt.wg.Add(1)
4646
go bt.runUpdateLoop()
4747

48-
if mudlog.IsInitialized() {
49-
mudlog.Info("BaseTimer started", "name", bt.name)
50-
}
48+
mudlog.Info("BaseTimer started", "name", bt.name)
5149
return nil
5250
}
5351

@@ -85,15 +83,11 @@ func (bt *BaseTimer) Stop() error {
8583
select {
8684
case <-done:
8785
// Timer stopped cleanly
88-
if mudlog.IsInitialized() {
89-
mudlog.Info("BaseTimer stopped", "name", bt.name)
90-
}
86+
mudlog.Info("BaseTimer stopped", "name", bt.name)
9187
case <-time.After(2 * time.Second):
9288
// Timeout - log warning and continue
93-
if mudlog.IsInitialized() {
94-
mudlog.Error("BaseTimer stop timeout", "name", bt.name,
95-
"message", "Timer goroutine did not stop within 2 seconds")
96-
}
89+
mudlog.Error("BaseTimer stop timeout", "name", bt.name,
90+
"message", "Timer goroutine did not stop within 2 seconds")
9791
}
9892

9993
return nil

internal/combat/delegator.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ import (
2020
func AttackPlayerVsMob(user *users.UserRecord, mob *mobs.Mob) AttackResult {
2121
// Validate inputs
2222
if user == nil || mob == nil {
23-
if mudlog.IsInitialized() {
24-
mudlog.Error("AttackPlayerVsMob", "error", "nil combatant", "user", user != nil, "mob", mob != nil)
25-
}
23+
mudlog.Error("AttackPlayerVsMob", "error", "nil combatant", "user", user != nil, "mob", mob != nil)
2624
return AttackResult{
2725
Hit: false,
2826
MessagesToSource: []string{"Combat error: invalid target"},

internal/combat/registry.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@ func RegisterCombatSystem(name string, system ICombatSystem) error {
2828
}
2929

3030
combatRegistry[name] = system
31-
// Only log if mudlog is initialized (it might not be during tests)
32-
if mudlog.IsInitialized() {
33-
mudlog.Info("Combat Registry", "action", "registered", "system", name)
34-
}
31+
// NOTE: Cannot use mudlog here as this may be called during init()
32+
// before mudlog is initialized. Logging happens later when systems are activated.
3533
return nil
3634
}
3735

@@ -79,10 +77,7 @@ func SetActiveCombatSystem(name string) error {
7977
// Shutdown current system if active (outside of lock)
8078
if oldSystem != nil {
8179
if err := oldSystem.Shutdown(); err != nil {
82-
// Only log if mudlog is initialized (it might not be during tests)
83-
if mudlog.IsInitialized() {
84-
mudlog.Error("Combat Registry", "action", "shutdown failed", "system", oldName, "error", err)
85-
}
80+
mudlog.Error("Combat Registry", "action", "shutdown failed", "system", oldName, "error", err)
8681
// Continue anyway - we don't want to block switching due to shutdown errors
8782
}
8883
}
@@ -189,11 +184,13 @@ func ClearRegistry() {
189184
}
190185

191186
// InitializeRegistry sets up the combat registry event handlers
187+
// ARCHITECTURAL NOTE: This must be called explicitly from main() rather than
188+
// using init() to avoid event handler registration during init phase, which
189+
// could cause initialization order issues. The combat system needs the event
190+
// system to be fully initialized before registering handlers.
192191
func InitializeRegistry() {
193192
events.RegisterListener(SwitchCombatSystemEvent{}, handleCombatSystemSwitch)
194-
if mudlog.IsInitialized() {
195-
mudlog.Info("Combat Registry", "action", "event handler registered")
196-
}
193+
mudlog.Info("Combat Registry", "action", "event handler registered")
197194
}
198195

199196
// handleCombatSystemSwitch processes combat system switch events
@@ -236,7 +233,11 @@ func handleCombatSystemSwitch(e events.Event) events.ListenerReturn {
236233
return events.Continue
237234
}
238235

239-
// Save the combat system to the main config
236+
// ARCHITECTURAL NOTE: Combat configuration is integrated into the main config
237+
// rather than using plugin-specific config. This is intentional as combat
238+
// is a core system that needs to be initialized before plugins are loaded.
239+
// The active combat system must be persisted globally to ensure correct
240+
// initialization on server restart.
240241
if err := configs.SetVal("GamePlay.Combat.Style", evt.NewSystem); err != nil {
241242
user.SendText(fmt.Sprintf(`<ansi fg="red">Failed to save combat system setting: %s</ansi>`, err.Error()))
242243
}

internal/mapper/mapper.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -998,15 +998,15 @@ func PreCacheMaps() {
998998
func validateRoomBiomes() {
999999
missingBiomeCount := 0
10001000
invalidBiomeCount := 0
1001-
1001+
10021002
for _, roomId := range rooms.GetAllRoomIds() {
10031003
room := rooms.LoadRoom(roomId)
10041004
if room == nil {
10051005
continue
10061006
}
1007-
1007+
10081008
originalBiome := room.Biome
1009-
1009+
10101010
// Check if room has no biome
10111011
if originalBiome == "" {
10121012
zoneBiome := rooms.GetZoneBiome(room.Zone)
@@ -1022,7 +1022,7 @@ func validateRoomBiomes() {
10221022
}
10231023
}
10241024
}
1025-
1025+
10261026
if missingBiomeCount > 0 || invalidBiomeCount > 0 {
10271027
mudlog.Info("Biome validation complete", "missing", missingBiomeCount, "invalid", invalidBiomeCount)
10281028
}

internal/plugins/plugins.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,20 @@ func (p *Plugin) AddMobCommand(command string, handlerFunc mobcommands.MobComman
283283

284284
}
285285

286+
// RemoveUserCommand removes a previously registered user command
287+
func (p *Plugin) RemoveUserCommand(command string) {
288+
if p.Callbacks.userCommands != nil {
289+
delete(p.Callbacks.userCommands, command)
290+
}
291+
}
292+
293+
// RemoveMobCommand removes a previously registered mob command
294+
func (p *Plugin) RemoveMobCommand(command string) {
295+
if p.Callbacks.mobCommands != nil {
296+
delete(p.Callbacks.mobCommands, command)
297+
}
298+
}
299+
286300
// Adds an embedded file system to the plugin
287301
func (p *Plugin) AttachFileSystem(f embed.FS) error {
288302

internal/rooms/rooms.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2198,7 +2198,6 @@ func (r *Room) Validate() error {
21982198
}
21992199
}
22002200

2201-
22022201
// Make sure all items are validated (and have uids)
22032202
for i := range r.Items {
22042203
r.Items[i].Validate()

modules/combat-rounds/combat.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ func NewRoundBasedCombat(plug *plugins.Plugin) *RoundBasedCombat {
2828

2929
// Initialize sets up the combat system
3030
func (rbc *RoundBasedCombat) Initialize() error {
31-
if mudlog.IsInitialized() {
32-
mudlog.Info("Combat System", "module", "combat-rounds", "action", "initializing commands")
33-
}
31+
mudlog.Info("Combat System", "module", "combat-rounds", "action", "initializing commands")
3432

3533
// Register combat commands
3634
rbc.registerCommands()
@@ -62,6 +60,9 @@ func (rbc *RoundBasedCombat) Shutdown() error {
6260
}
6361
mudlog.Info("Combat System", "module", "combat-rounds", "action", "timer stopped")
6462

63+
// Unregister commands
64+
rbc.unregisterCommands()
65+
6566
rbc.active = false
6667
if mudlog.IsInitialized() {
6768
mudlog.Info("Combat System", "module", "combat-rounds", "status", "shutdown")

modules/combat-rounds/commands.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,24 @@ func (rbc *RoundBasedCombat) registerCommands() {
3232
rbc.plug.AddMobCommand("attack", rbc.mobAttackCommand, false)
3333
rbc.plug.AddMobCommand("flee", rbc.mobFleeCommand, false)
3434

35-
if mudlog.IsInitialized() {
36-
mudlog.Info("Combat Commands", "action", "registered", "module", "combat-rounds")
37-
}
35+
mudlog.Info("Combat Commands", "action", "registered", "module", "combat-rounds")
36+
}
37+
38+
// unregisterCommands removes all combat-related commands
39+
func (rbc *RoundBasedCombat) unregisterCommands() {
40+
// Remove user commands
41+
rbc.plug.RemoveUserCommand("attack")
42+
rbc.plug.RemoveUserCommand("kill")
43+
rbc.plug.RemoveUserCommand("flee")
44+
rbc.plug.RemoveUserCommand("consider")
45+
rbc.plug.RemoveUserCommand("combatinfo")
46+
rbc.plug.RemoveUserCommand("config")
47+
48+
// Remove mob commands
49+
rbc.plug.RemoveMobCommand("attack")
50+
rbc.plug.RemoveMobCommand("flee")
51+
52+
mudlog.Info("Combat Commands", "action", "unregistered", "module", "combat-rounds")
3853
}
3954

4055
// fleeCommand handles player flee commands

modules/combat-rounds/timer.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ func NewRoundBasedTimer(c *RoundBasedCombat) *RoundBasedTimer {
5555

5656
// Start implements ICombatTimer
5757
func (rt *RoundBasedTimer) Start() error {
58-
// Defer registration to avoid deadlock when called from event handler
58+
// ARCHITECTURAL NOTE: Event registration is deferred to prevent deadlocks
59+
// The combat system switching uses events, and registering listeners during
60+
// event processing (when combat system initializes) would cause a deadlock
61+
// in the event system. This pattern is unique to combat modules due to their
62+
// dynamic initialization/shutdown during runtime.
5963
go func() {
6064
// Small delay to ensure we're out of the event handler
6165
time.Sleep(1 * time.Millisecond)

modules/combat-twitch/combat.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ func (tc *TwitchCombat) Shutdown() error {
7171
}
7272
}
7373

74+
// Unregister commands
75+
tc.unregisterCommands()
76+
7477
mudlog.Info("Combat System", "module", "combat-twitch", "status", "shutdown")
7578
return nil
7679
}

0 commit comments

Comments
 (0)