diff --git "a/examples/\345\217\214\346\240\270\345\277\203\351\233\206\346\210\220\347\244\272\344\276\213.go" "b/examples/\345\217\214\346\240\270\345\277\203\351\233\206\346\210\220\347\244\272\344\276\213.go" index aee62937..504e12ca 100644 --- "a/examples/\345\217\214\346\240\270\345\277\203\351\233\206\346\210\220\347\244\272\344\276\213.go" +++ "b/examples/\345\217\214\346\240\270\345\277\203\351\233\206\346\210\220\347\244\272\344\276\213.go" @@ -99,7 +99,7 @@ func exampleAsyncResult() { WorkerCount: 4, } - persistCfg := persist.Config{ + persistCfg := config.PersistConfig{ Enabled: true, Directory: "data/state", File: "server-state.json", @@ -144,7 +144,7 @@ func exampleMonitorCore2() { WorkerCount: 4, } - persistCfg := persist.Config{ + persistCfg := config.PersistConfig{ Enabled: true, Directory: "data/state", File: "server-state.json", @@ -198,7 +198,7 @@ func exampleIntegration() { WorkerCount: 4, } - persistCfg := persist.Config{ + persistCfg := config.PersistConfig{ Enabled: true, Directory: "data/state", File: "server-state.json", diff --git a/internal/logic/lexer.go b/internal/logic/lexer.go index f1dbf705..7c4ec2c9 100644 --- a/internal/logic/lexer.go +++ b/internal/logic/lexer.go @@ -231,7 +231,7 @@ func (t Token) String() string { case True, False: return fmt.Sprintf("BOOL(%s)", t.Literal) default: - return fmt.Sprintf("%s(%s)", t.Type, t.Literal) + return fmt.Sprintf("%v(%s)", t.Type, t.Literal) } } diff --git a/internal/logic/parser.go b/internal/logic/parser.go index ee618d78..cb14a7ee 100644 --- a/internal/logic/parser.go +++ b/internal/logic/parser.go @@ -181,7 +181,7 @@ func (e *BinaryExpr) nodeType() string { // String returns a string representation of the binary expression. func (e *BinaryExpr) String() string { - return fmt.Sprintf("(%s %s %s)", e.Left, e.Operator, e.Right) + return fmt.Sprintf("(%s %v %s)", e.Left, e.Operator, e.Right) } // UnaryExpr represents a unary operation. @@ -201,7 +201,7 @@ func (e *UnaryExpr) nodeType() string { // String returns a string representation of the unary expression. func (e *UnaryExpr) String() string { - return fmt.Sprintf("%s%s", e.Operator, e.Operand) + return fmt.Sprintf("%v%s", e.Operator, e.Operand) } // CallExpr represents a function call. diff --git a/internal/mods/loader.go b/internal/mods/loader.go index a8d4d6f9..de58c805 100644 --- a/internal/mods/loader.go +++ b/internal/mods/loader.go @@ -3,11 +3,11 @@ package mods import ( "encoding/json" "fmt" - "os" "path/filepath" + "strings" "mdt-server/internal/config" - "mdt-server/internal/mods/go" + gomod "mdt-server/internal/mods/go" "mdt-server/internal/mods/java" "mdt-server/internal/mods/js" "mdt-server/internal/mods/node" @@ -53,7 +53,7 @@ func (ml *ModLoader) Load(path string) error { } // Check mod type and load accordingly - ext := filepath.Ext(path) + ext := strings.ToLower(filepath.Ext(path)) switch ext { case ".jar": manager, exists := ml.managers["java"] @@ -89,7 +89,7 @@ func (ml *ModLoader) Load(path string) error { case ".go": manager, exists := ml.managers["go"] if !exists { - manager = go.NewGoModManager() + manager = gomod.NewGoModManager() ml.managers["go"] = manager } @@ -101,6 +101,21 @@ func (ml *ModLoader) Load(path string) error { ml.loadedMods[mod.Name] = mod return nil + case ".node": + manager, exists := ml.managers["node"] + if !exists { + manager = node.NewNodeModManager() + ml.managers["node"] = manager + } + + mod := &LoadedMod{ + Name: filepath.Base(path), + Path: path, + DataType: "node", + } + ml.loadedMods[mod.Name] = mod + return nil + default: return fmt.Errorf("mod loader: mod type not supported: %s", ext) } @@ -108,7 +123,7 @@ func (ml *ModLoader) Load(path string) error { // Unload unloads a mod by name func (ml *ModLoader) Unload(name string) error { - if mod, exists := ml.loadedMods[name]; exists { + if _, exists := ml.loadedMods[name]; exists { delete(ml.loadedMods, name) return nil } @@ -180,30 +195,30 @@ func (ml *ModLoader) SetConfig(cfg config.ModsConfig) { // MarshalJSON implements json.Marshaler func (ml *ModLoader) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - Enabled bool - LoadedMods []string - JavaHome string - JSDir string - GoDir string - NodeDir string + Enabled bool + LoadedMods []string + JavaHome string + JSDir string + GoDir string + NodeDir string }{ - Enabled: ml.cfg.Enabled, - LoadedMods: ml.ListMods(), - JavaHome: ml.cfg.JavaHome, - JSDir: ml.cfg.JSDir, - GoDir: ml.cfg.GoDir, - NodeDir: ml.cfg.NodeDir, + Enabled: ml.cfg.Enabled, + LoadedMods: ml.ListMods(), + JavaHome: ml.cfg.JavaHome, + JSDir: ml.cfg.JSDir, + GoDir: ml.cfg.GoDir, + NodeDir: ml.cfg.NodeDir, }) } // UnmarshalJSON implements json.Unmarshaler func (ml *ModLoader) UnmarshalJSON(data []byte) error { var cfg struct { - Enabled bool `json:"enabled"` - JavaHome string `json:"java_home"` - JSDir string `json:"js_dir"` - GoDir string `json:"go_dir"` - NodeDir string `json:"node_dir"` + Enabled bool `json:"enabled"` + JavaHome string `json:"java_home"` + JSDir string `json:"js_dir"` + GoDir string `json:"go_dir"` + NodeDir string `json:"node_dir"` } if err := json.Unmarshal(data, &cfg); err != nil { return err diff --git a/internal/mods/loader_test.go b/internal/mods/loader_test.go new file mode 100644 index 00000000..238149a5 --- /dev/null +++ b/internal/mods/loader_test.go @@ -0,0 +1,54 @@ +package mods + +import "testing" + +func TestModLoaderLoadTypes(t *testing.T) { + ml := NewModLoader() + ml.SetEnabled(true) + + cases := []struct { + path string + typ string + }{ + {path: "mods/A.jar", typ: "java"}, + {path: "mods/b.js", typ: "js"}, + {path: "mods/c.go", typ: "go"}, + {path: "mods/d.node", typ: "node"}, + {path: "mods/E.NODE", typ: "node"}, + } + + for _, tc := range cases { + if err := ml.Load(tc.path); err != nil { + t.Fatalf("load %s failed: %v", tc.path, err) + } + mod, err := ml.GetMod(lastPathBase(tc.path)) + if err != nil { + t.Fatalf("get mod %s failed: %v", tc.path, err) + } + if mod.DataType != tc.typ { + t.Fatalf("expected type %s for %s, got %s", tc.typ, tc.path, mod.DataType) + } + } +} + +func TestModLoaderDisabledAndUnsupported(t *testing.T) { + ml := NewModLoader() + + if err := ml.Load("mods/x.js"); err == nil { + t.Fatalf("expected error when mods disabled") + } + + ml.SetEnabled(true) + if err := ml.Load("mods/x.txt"); err == nil { + t.Fatalf("expected unsupported type error") + } +} + +func lastPathBase(p string) string { + for i := len(p) - 1; i >= 0; i-- { + if p[i] == '/' || p[i] == '\\' { + return p[i+1:] + } + } + return p +} diff --git a/internal/mods/manager.go b/internal/mods/manager.go index c2d9a5b8..599ef233 100644 --- a/internal/mods/manager.go +++ b/internal/mods/manager.go @@ -5,14 +5,8 @@ import ( "fmt" "os" "path/filepath" - "sort" "strings" "sync" - - "mdt-server/internal/mods/go" - "mdt-server/internal/mods/java" - "mdt-server/internal/mods/js" - "mdt-server/internal/mods/node" ) // ==================== @@ -138,10 +132,12 @@ func (m *ModManager) LoadAll() error { // load each mod for _, path := range modFiles { if err := m.loader.Load(path); err != nil { - Log.Warn("failed to load mod: %s (%v)", path, err) + modLog.Warn("failed to load mod: %s (%v)", path, err) } } + m.mods = m.collectLoadedModsLocked() + return nil } @@ -154,9 +150,10 @@ func (m *ModManager) UnloadAll() error { for _, mod := range m.mods { if err := m.loader.Unload(mod.Name); err != nil { errors = append(errors, err) - Log.Warn("failed to unload mod: %s (%v)", mod.Name, err) + modLog.Warn("failed to unload mod: %s (%v)", mod.Name, err) } } + m.mods = m.mods[:0] if len(errors) > 0 { return fmt.Errorf("failed to unload some mods: %v", errors) @@ -179,13 +176,9 @@ func (m *ModManager) StartAll() error { defer m.mu.Unlock() var errors []error - for _, mod := range m.mods { - if !mod.Started { - if err := mod.Start(); err != nil { - errors = append(errors, err) - Log.Warn("failed to start mod: %s (%v)", mod.Name, err) - } - } + if err := m.loader.Start(); err != nil { + errors = append(errors, err) + modLog.Warn("failed to start mods: %v", err) } if len(errors) > 0 { @@ -201,13 +194,9 @@ func (m *ModManager) StopAll() error { defer m.mu.Unlock() var errors []error - for _, mod := range m.mods { - if mod.Started { - if err := mod.Stop(); err != nil { - errors = append(errors, err) - Log.Warn("failed to stop mod: %s (%v)", mod.Name, err) - } - } + if err := m.loader.Stop(); err != nil { + errors = append(errors, err) + modLog.Warn("failed to stop mods: %v", err) } if len(errors) > 0 { @@ -276,3 +265,15 @@ func (m *ModManager) GetProperty(key string) (interface{}, error) { func (m *ModManager) GetLoader() *ModLoader { return m.loader } + +func (m *ModManager) collectLoadedModsLocked() []*LoadedMod { + names := m.loader.ListMods() + mods := make([]*LoadedMod, 0, len(names)) + for _, name := range names { + mod, err := m.loader.GetMod(name) + if err == nil { + mods = append(mods, mod) + } + } + return mods +} diff --git a/internal/mods/types.go b/internal/mods/types.go index 833374fb..314da808 100644 --- a/internal/mods/types.go +++ b/internal/mods/types.go @@ -23,6 +23,8 @@ type Log interface { // simpleLogger simple logger type simpleLogger struct{} +var modLog Log = &simpleLogger{} + func (l *simpleLogger) Info(msg string, args ...interface{}) { fmt.Printf("[MOD] "+msg+"\n", args...) } diff --git a/internal/render/render.go b/internal/render/render.go index c0f1f998..e68ce888 100644 --- a/internal/render/render.go +++ b/internal/render/render.go @@ -264,9 +264,9 @@ func GetTeamColor(team world.TeamID) Color { {R: 0.95, G: 0.56, B: 0.12, A: 1.0}, // rime } // Ensure teamIdx is non-negative and within bounds - teamIdx := int(team) % int32(len(colors)) + teamIdx := int(team) % len(colors) if teamIdx < 0 { - teamIdx += int32(len(colors)) + teamIdx += len(colors) } return colors[teamIdx] }