diff --git a/Moonshark.go b/Moonshark.go index 2bea636..e6905aa 100644 --- a/Moonshark.go +++ b/Moonshark.go @@ -4,10 +4,6 @@ import ( "context" "errors" "fmt" - "os" - "os/signal" - "runtime" - "syscall" "time" "git.sharkk.net/Sky/Moonshark/core/config" @@ -19,115 +15,79 @@ import ( "git.sharkk.net/Sky/Moonshark/core/watchers" ) -// WatcherConfig holds the configuration for which watchers to enable -type WatcherConfig struct { - Routes bool - Static bool - Modules bool +// Moonshark represents the server and all its dependencies +type Moonshark struct { + Config *config.Config + LuaRouter *routers.LuaRouter + StaticRouter *routers.StaticRouter + LuaRunner *runner.LuaRunner + HTTPServer *http.Server + + // Clean-up functions for watchers + cleanupFuncs []func() error } -// initRouters sets up the Lua and static routers -func initRouters(routesDir, staticDir string) (*routers.LuaRouter, *routers.StaticRouter, error) { - // Ensure directories exist - if err := utils.EnsureDir(routesDir); err != nil { - return nil, nil, fmt.Errorf("routes directory doesn't exist, and could not create it: %v", err) - } - if err := utils.EnsureDir(staticDir); err != nil { - return nil, nil, fmt.Errorf("static directory doesn't exist, and could not create it: %v", err) +// NewMoonshark creates a new Moonshark server instance +func NewMoonshark(configPath string) (*Moonshark, error) { + // Initialize server struct + server := &Moonshark{} + + // Load configuration + if err := server.loadConfig(configPath); err != nil { + return nil, err } - // Initialize Lua router for dynamic routes - luaRouter, err := routers.NewLuaRouter(routesDir) - if err != nil { - // Check if this is a compilation warning or a more serious error - if errors.Is(err, routers.ErrRoutesCompilationErrors) { - // Some routes failed to compile, but router is still usable - logger.Warning("Some Lua routes failed to compile. Check logs for details.") + // Configure logging + server.setupLogging() - // Log details about each failed route - if failedRoutes := luaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 { - for _, re := range failedRoutes { - logger.Error("Route %s %s failed to compile: %v", re.Method, re.Path, re.Err) - } - } - } else { - // More serious error that prevents router initialization - return nil, nil, fmt.Errorf("failed to initialize Lua router: %v", err) - } + // Initialize routers + if err := server.initRouters(); err != nil { + return nil, err } - logger.Info("Lua router initialized with routes from %s", routesDir) - // Initialize static file router - staticRouter, err := routers.NewStaticRouter(staticDir) - if err != nil { - return nil, nil, fmt.Errorf("failed to initialize static router: %v", err) + // Initialize Lua runner + if err := server.initRunner(); err != nil { + return nil, err } - logger.Info("Static router initialized with files from %s", staticDir) - staticRouter.EnableDebugLog() - return luaRouter, staticRouter, nil + // Set up file watchers + if err := server.setupWatchers(); err != nil { + logger.Warning("Error setting up watchers: %v", err) + } + + // Create HTTP server + server.HTTPServer = http.New( + server.LuaRouter, + server.StaticRouter, + server.LuaRunner, + server.Config.HTTPLoggingEnabled, + server.Config.Debug, + server.Config.OverrideDir, + server.Config, + ) + + return server, nil } -// setupWatchers initializes and starts all file watchers -func setupWatchers(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter, - luaRunner *runner.LuaRunner, routesDir string, staticDir string, - libDirs []string, config WatcherConfig) ([]func() error, error) { +// loadConfig loads the configuration file +func (s *Moonshark) loadConfig(configPath string) error { + var err error - var cleanupFuncs []func() error - - // Set up watcher for Lua routes - if config.Routes { - luaRouterWatcher, err := watchers.WatchLuaRouter(luaRouter, luaRunner, routesDir) - if err != nil { - logger.Warning("Failed to watch routes directory: %v", err) - } else { - cleanupFuncs = append(cleanupFuncs, luaRouterWatcher.Close) - } - } - - // Set up watcher for static files - if config.Static { - staticWatcher, err := watchers.WatchStaticRouter(staticRouter, staticDir) - if err != nil { - logger.Warning("Failed to watch static directory: %v", err) - } else { - cleanupFuncs = append(cleanupFuncs, staticWatcher.Close) - } - } - - // Set up watchers for Lua modules libraries - if config.Modules && len(libDirs) > 0 { - moduleWatchers, err := watchers.WatchLuaModules(luaRunner, libDirs) - if err != nil { - logger.Warning("Failed to watch Lua module directories: %v", err) - } else { - for _, watcher := range moduleWatchers { - w := watcher // Capture variable for closure - cleanupFuncs = append(cleanupFuncs, w.Close) - } - logger.Info("File watchers active for %d Lua module directories", len(moduleWatchers)) - } - } - - return cleanupFuncs, nil -} - -func main() { - // Initialize global logger with debug level - logger.InitGlobalLogger(logger.LevelDebug, true) - - logger.Server("Starting Moonshark server") - - // Load configuration from config.lua - cfg, err := config.Load("config.lua") + // Load configuration from file + s.Config, err = config.Load(configPath) if err != nil { - logger.Warning("Failed to load config.lua: %v", err) + logger.Warning("Failed to load config file: %v", err) logger.Info("Using default configuration") - cfg = config.New() + s.Config = config.New() } + return nil +} + +// setupLogging configures the logger based on config settings +func (s *Moonshark) setupLogging() { // Set log level from config - switch cfg.GetString("log_level", "info") { + switch s.Config.LogLevel { case "warn": logger.SetLevel(logger.LevelWarning) case "error": @@ -140,143 +100,180 @@ func main() { logger.SetLevel(logger.LevelInfo) } - // Get debug mode setting - debugMode := cfg.GetBool("debug", false) - if debugMode { + // Set debug mode if configured + if s.Config.Debug { logger.EnableDebug() // Force debug logs regardless of level logger.Debug("Debug mode enabled") } +} - // Get configuration values - port := cfg.GetInt("port", 3117) - routesDir := cfg.GetString("routes_dir", "./routes") - staticDir := cfg.GetString("static_dir", "./static") - overrideDir := cfg.GetString("override_dir", "./override") - poolSize := cfg.GetInt("pool_size", runtime.NumCPU()) - - if err := utils.EnsureDir(overrideDir); err != nil { - logger.Warning("Override directory doesn't exist, and could not create it: %v", err) - overrideDir = "" // Disable overrides if directory can't be created +// initRouters initializes the Lua and static routers +func (s *Moonshark) initRouters() error { + // Ensure directories exist + if err := utils.EnsureDir(s.Config.RoutesDir); err != nil { + return fmt.Errorf("routes directory doesn't exist and could not be created: %v", err) } - // Get library directories - var libDirs []string - libDirsConfig := cfg.GetStringArray("lib_dirs") - if len(libDirsConfig) > 0 { - libDirs = libDirsConfig - } else { - // Default lib directory - libDirs = []string{"./libs"} + if err := utils.EnsureDir(s.Config.StaticDir); err != nil { + return fmt.Errorf("static directory doesn't exist and could not be created: %v", err) + } + + // Initialize Lua router for dynamic routes + var err error + s.LuaRouter, err = routers.NewLuaRouter(s.Config.RoutesDir) + if err != nil { + // Check if this is a compilation warning or a more serious error + if errors.Is(err, routers.ErrRoutesCompilationErrors) { + // Some routes failed to compile, but router is still usable + logger.Warning("Some Lua routes failed to compile. Check logs for details.") + + // Log details about each failed route + if failedRoutes := s.LuaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 { + for _, re := range failedRoutes { + logger.Error("Route %s %s failed to compile: %v", re.Method, re.Path, re.Err) + } + } + } else { + // More serious error that prevents router initialization + return fmt.Errorf("failed to initialize Lua router: %v", err) + } + } + logger.Info("Lua router initialized with routes from %s", s.Config.RoutesDir) + + // Initialize static file router + s.StaticRouter, err = routers.NewStaticRouter(s.Config.StaticDir) + if err != nil { + return fmt.Errorf("failed to initialize static router: %v", err) + } + logger.Info("Static router initialized with files from %s", s.Config.StaticDir) + s.StaticRouter.EnableDebugLog() + + return nil +} + +// initRunner initializes the Lua runner +func (s *Moonshark) initRunner() error { + // Ensure override directory exists + if err := utils.EnsureDir(s.Config.OverrideDir); err != nil { + logger.Warning("Override directory doesn't exist and could not be created: %v", err) + s.Config.OverrideDir = "" // Disable overrides if directory can't be created } // Ensure lib directories exist - for _, dir := range libDirs { + for _, dir := range s.Config.LibDirs { if err := utils.EnsureDir(dir); err != nil { - logger.Warning("Lib directory doesn't exist, and could not create it: %v", err) + logger.Warning("Lib directory doesn't exist and could not be created: %v", err) } } - // Initialize routers - luaRouter, staticRouter, err := initRouters(routesDir, staticDir) - if err != nil { - logger.Fatal("Router initialization failed: %v", err) - } - - // Initialize Lua runner with options + // Set up runner options runnerOpts := []runner.RunnerOption{ - runner.WithPoolSize(poolSize), - runner.WithLibDirs(libDirs...), + runner.WithPoolSize(s.Config.PoolSize), + runner.WithLibDirs(s.Config.LibDirs...), } // Add debug option conditionally - if debugMode { + if s.Config.Debug { runnerOpts = append(runnerOpts, runner.WithDebugEnabled()) logger.Debug("Debug logging enabled for Lua runner") } - luaRunner, err := runner.NewRunner(runnerOpts...) + // Initialize the runner + var err error + s.LuaRunner, err = runner.NewRunner(runnerOpts...) if err != nil { - logger.Fatal("Failed to initialize Lua runner: %v", err) - } - logger.Server("Lua runner initialized with pool size %d", poolSize) - defer luaRunner.Close() - - // Set up file watchers if enabled - var cleanupFuncs []func() error - - // Get watcher configuration (default all disabled) - watcherConfig := WatcherConfig{ - Routes: false, - Static: false, - Modules: false, + return fmt.Errorf("failed to initialize Lua runner: %v", err) } - // Enable watchers based on map configuration - watchersMap := cfg.GetMap("watchers") - if watchersMap != nil { - if routes, ok := watchersMap["routes"].(bool); ok && routes { - watcherConfig.Routes = true - } - if static, ok := watchersMap["static"].(bool); ok && static { - watcherConfig.Static = true - } - if modules, ok := watchersMap["modules"].(bool); ok && modules { - watcherConfig.Modules = true + logger.Server("Lua runner initialized with pool size %d", s.Config.PoolSize) + return nil +} + +// setupWatchers initializes and starts all configured file watchers +func (s *Moonshark) setupWatchers() error { + // Set up watcher for Lua routes + if s.Config.Watchers.Routes { + luaRouterWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.RoutesDir) + if err != nil { + logger.Warning("Failed to watch routes directory: %v", err) + } else { + s.cleanupFuncs = append(s.cleanupFuncs, luaRouterWatcher.Close) } } - // Setup enabled watchers - cleanupFuncs, err = setupWatchers(luaRouter, staticRouter, luaRunner, - routesDir, staticDir, libDirs, watcherConfig) - if err != nil { - logger.Warning("Error setting up watchers: %v", err) + // Set up watcher for static files + if s.Config.Watchers.Static { + staticWatcher, err := watchers.WatchStaticRouter(s.StaticRouter, s.Config.StaticDir) + if err != nil { + logger.Warning("Failed to watch static directory: %v", err) + } else { + s.cleanupFuncs = append(s.cleanupFuncs, staticWatcher.Close) + } } - // Register cleanup functions - defer func() { - for _, cleanup := range cleanupFuncs { - if err := cleanup(); err != nil { - logger.Warning("Cleanup error: %v", err) + // Set up watchers for Lua modules libraries + if s.Config.Watchers.Modules && len(s.Config.LibDirs) > 0 { + moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.LibDirs) + if err != nil { + logger.Warning("Failed to watch Lua module directories: %v", err) + } else { + for _, watcher := range moduleWatchers { + w := watcher // Capture variable for closure + s.cleanupFuncs = append(s.cleanupFuncs, w.Close) } + logger.Info("File watchers active for %d Lua module directories", len(moduleWatchers)) } - }() + } - httpLoggingEnabled := cfg.GetBool("http_logging_enabled", true) - if httpLoggingEnabled { + return nil +} + +// Start starts the HTTP server +func (s *Moonshark) Start() error { + logger.Server("Starting server on port %d", s.Config.Port) + + // Log HTTP logging status + if s.Config.HTTPLoggingEnabled { logger.Info("HTTP logging is enabled") } else { logger.Info("HTTP logging is disabled") } - // Create HTTP server - server := http.New(luaRouter, staticRouter, luaRunner, httpLoggingEnabled, debugMode, overrideDir, cfg) - - // Handle graceful shutdown - stop := make(chan os.Signal, 1) - signal.Notify(stop, os.Interrupt, syscall.SIGTERM) - - // Start server in a goroutine + // Start the server in a non-blocking way go func() { - if err := server.ListenAndServe(fmt.Sprintf(":%d", port)); err != nil { + if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Port)); err != nil { if err.Error() != "http: Server closed" { logger.Error("Server error: %v", err) } } }() - logger.Server("Server started on port %d", port) + return nil +} - // Wait for interrupt signal - <-stop - logger.Server("Shutdown signal received") +// Shutdown gracefully shuts down the server +func (s *Moonshark) Shutdown() error { + logger.Server("Shutting down server...") - // Gracefully shut down the server + // Shutdown HTTP server with timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - if err := server.Shutdown(ctx); err != nil { + if err := s.HTTPServer.Shutdown(ctx); err != nil { logger.Error("Server shutdown error: %v", err) + return err } + // Run cleanup functions + for _, cleanup := range s.cleanupFuncs { + if err := cleanup(); err != nil { + logger.Warning("Cleanup error: %v", err) + } + } + + // Close Lua runner + s.LuaRunner.Close() + logger.Server("Server stopped") + return nil } diff --git a/core/config/Config.go b/core/config/Config.go index 8901b84..bd45d14 100644 --- a/core/config/Config.go +++ b/core/config/Config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "runtime" + "strconv" luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) @@ -22,13 +23,17 @@ type Config struct { LibDirs []string // Performance settings - PoolSize int // Number of Lua states in the pool + PoolSize int // Feature flags HTTPLoggingEnabled bool - Watchers map[string]bool + Watchers struct { + Routes bool + Static bool + Modules bool + } - // Raw values map for all values including custom ones + // Raw values map for custom values values map[string]any } @@ -46,16 +51,11 @@ func New() *Config { OverrideDir: "./override", LibDirs: []string{"./libs"}, - // Performance defaults - PoolSize: runtime.NumCPU(), + // Performance defaults - respect GOMAXPROCS + PoolSize: runtime.GOMAXPROCS(0), // Feature flag defaults HTTPLoggingEnabled: true, - Watchers: map[string]bool{ - "routes": false, - "static": false, - "modules": false, - }, // Initialize values map values: make(map[string]any), @@ -64,8 +64,8 @@ func New() *Config { // Load loads configuration from a Lua file func Load(filePath string) (*Config, error) { - // Create a new Lua state without standard libraries - state := luajit.New(false) + // For simple configs, use standard libraries to ensure proper loading + state := luajit.New(true) if state == nil { return nil, errors.New("failed to create Lua state") } @@ -74,122 +74,106 @@ func Load(filePath string) (*Config, error) { // Create config with default values config := New() - // Execute the Lua file + // Execute the config file if err := state.DoFile(filePath); err != nil { return nil, fmt.Errorf("failed to load config file: %w", err) } - // Extract all globals from the Lua state - if err := extractGlobals(state, config); err != nil { - return nil, err - } + // Extract configuration values + extractConfig(state, config) return config, nil } -// extractGlobals extracts global variables from the Lua state -func extractGlobals(state *luajit.State, config *Config) error { - // Get the globals table - state.GetGlobal("_G") - if !state.IsTable(-1) { - state.Pop(1) - return errors.New("failed to get globals table") - } - - // Iterate through the globals table - state.PushNil() // Start iteration - for state.Next(-2) { - // Stack now has key at -2 and value at -1 - - // Skip non-string keys - if !state.IsString(-2) { - state.Pop(1) // Pop value, leave key for next iteration - continue - } - - // Get key and value type - key := state.ToString(-2) - valueType := state.GetType(-1) - - // Skip functions, userdata, and threads - we don't need these - if valueType == luajit.TypeFunction || - valueType == luajit.TypeUserData || - valueType == luajit.TypeThread { - state.Pop(1) - continue - } - - // Process the value - processConfigValue(state, config, key) - } - - // Pop the globals table - state.Pop(1) - - return nil -} - -// processConfigValue processes a specific config value from Lua -func processConfigValue(state *luajit.State, config *Config, key string) { - // Get the value as its natural type - value, err := state.ToValue(-1) - if err != nil { - state.Pop(1) - return - } - - // Store in the values map - config.values[key] = value - - // Process special cases and config fields - switch key { - case "log_level": - if strVal, ok := luajit.ConvertValue[string](value); ok { - config.LogLevel = strVal - } - case "port": - if intVal, ok := luajit.ConvertValue[int](value); ok { - config.Port = intVal - } - case "debug": - if boolVal, ok := luajit.ConvertValue[bool](value); ok { +// extractConfig extracts configuration values directly by name +func extractConfig(state *luajit.State, config *Config) { + // Process known config values directly by key + if value := extractValue(state, "debug"); value != nil { + if boolVal, ok := value.(bool); ok { config.Debug = boolVal } - case "routes_dir": - if strVal, ok := luajit.ConvertValue[string](value); ok { + } + + if value := extractValue(state, "log_level"); value != nil { + if strVal, ok := value.(string); ok { + config.LogLevel = strVal + } + } + + if value := extractValue(state, "port"); value != nil { + if numVal, ok := value.(float64); ok { + config.Port = int(numVal) + } + } + + if value := extractValue(state, "routes_dir"); value != nil { + if strVal, ok := value.(string); ok { config.RoutesDir = strVal } - case "static_dir": - if strVal, ok := luajit.ConvertValue[string](value); ok { + } + + if value := extractValue(state, "static_dir"); value != nil { + if strVal, ok := value.(string); ok { config.StaticDir = strVal } - case "override_dir": - if strVal, ok := luajit.ConvertValue[string](value); ok { + } + + if value := extractValue(state, "override_dir"); value != nil { + if strVal, ok := value.(string); ok { config.OverrideDir = strVal } - case "pool_size": - if intVal, ok := luajit.ConvertValue[int](value); ok { - config.PoolSize = intVal + } + + if value := extractValue(state, "pool_size"); value != nil { + if numVal, ok := value.(float64); ok { + config.PoolSize = int(numVal) } - case "http_logging_enabled": - if boolVal, ok := luajit.ConvertValue[bool](value); ok { + } + + if value := extractValue(state, "http_logging_enabled"); value != nil { + if boolVal, ok := value.(bool); ok { config.HTTPLoggingEnabled = boolVal } - case "watchers": + } + + // Handle watchers table + if value := extractValue(state, "watchers"); value != nil { if table, ok := value.(map[string]any); ok { - for k, v := range table { - if boolVal, ok := luajit.ConvertValue[bool](v); ok { - config.Watchers[k] = boolVal - } + if routes, ok := table["routes"].(bool); ok { + config.Watchers.Routes = routes + } + if static, ok := table["static"].(bool); ok { + config.Watchers.Static = static + } + if modules, ok := table["modules"].(bool); ok { + config.Watchers.Modules = modules } } - case "lib_dirs": + } + + // Handle lib_dirs array + if value := extractValue(state, "lib_dirs"); value != nil { if dirs := extractStringArray(value); len(dirs) > 0 { config.LibDirs = dirs } } +} - state.Pop(1) // Pop value, leave key for next iteration +// extractValue extracts a specific global value from the Lua state +func extractValue(state *luajit.State, key string) any { + state.GetGlobal(key) + defer state.Pop(1) + + if state.IsNil(-1) { + return nil + } + + value, err := state.ToValue(-1) + if err != nil { + return nil + } + + return value } // extractStringArray extracts a string array from various possible formats @@ -198,7 +182,7 @@ func extractStringArray(value any) []string { if arr, ok := value.([]any); ok { result := make([]string, 0, len(arr)) for _, v := range arr { - if str, ok := luajit.ConvertValue[string](v); ok { + if str, ok := v.(string); ok { result = append(result, str) } } @@ -223,7 +207,7 @@ func extractStringArray(value any) []string { case []any: result := make([]string, 0, len(arr)) for _, v := range arr { - if str, ok := luajit.ConvertValue[string](v); ok { + if str, ok := v.(string); ok { result = append(result, str) } } @@ -239,211 +223,90 @@ func extractStringArray(value any) []string { return nil } -// Get returns a configuration value by key -func (c *Config) Get(key string) any { +// GetCustomValue returns any custom configuration value by key +func (c *Config) GetCustomValue(key string) any { return c.values[key] } -// GetString returns a string configuration value -func (c *Config) GetString(key string, defaultValue string) string { - // Check for specific struct fields first for better performance - switch key { - case "log_level": - return c.LogLevel - case "routes_dir": - return c.RoutesDir - case "static_dir": - return c.StaticDir - case "override_dir": - return c.OverrideDir - } - - // Fall back to values map for other keys - value, ok := c.values[key] - if !ok { - return defaultValue - } - - result, ok := luajit.ConvertValue[string](value) - if !ok { - return defaultValue - } - - return result -} - -// GetInt returns an integer configuration value -func (c *Config) GetInt(key string, defaultValue int) int { - // Check for specific struct fields first for better performance - switch key { - case "port": - return c.Port - case "pool_size": - return c.PoolSize - } - - // Fall back to values map for other keys - value, ok := c.values[key] - if !ok { - return defaultValue - } - - result, ok := luajit.ConvertValue[int](value) - if !ok { - return defaultValue - } - - return result -} - -// GetFloat returns a float configuration value -func (c *Config) GetFloat(key string, defaultValue float64) float64 { - value, ok := c.values[key] - if !ok { - return defaultValue - } - - result, ok := luajit.ConvertValue[float64](value) - if !ok { - return defaultValue - } - - return result -} - -// GetBool returns a boolean configuration value -func (c *Config) GetBool(key string, defaultValue bool) bool { - // Check for specific struct fields first for better performance - switch key { - case "debug": - return c.Debug - case "http_logging_enabled": - return c.HTTPLoggingEnabled - } - - // Special case for watcher settings - if len(key) > 9 && key[:9] == "watchers." { - watcherKey := key[9:] - val, ok := c.Watchers[watcherKey] - if ok { - return val - } - return defaultValue - } - - // Fall back to values map for other keys - value, ok := c.values[key] - if !ok { - return defaultValue - } - - result, ok := luajit.ConvertValue[bool](value) - if !ok { - return defaultValue - } - - return result -} - -// GetMap returns a map configuration value -func (c *Config) GetMap(key string) map[string]any { - // Special case for watchers - if key == "watchers" { - result := make(map[string]any, len(c.Watchers)) - for k, v := range c.Watchers { - result[k] = v - } - return result - } - - value, ok := c.values[key] - if !ok { - return nil - } - - table, ok := value.(map[string]any) - if !ok { - return nil - } - - return table -} - -// GetArray returns an array of values from a Lua array -func (c *Config) GetArray(key string) []any { - // Special case for lib_dirs - if key == "lib_dirs" { - result := make([]any, len(c.LibDirs)) - for i, v := range c.LibDirs { - result[i] = v - } - return result - } - - value := c.Get(key) +// GetCustomString returns a custom string configuration value +func (c *Config) GetCustomString(key string, defaultValue string) string { + value := c.values[key] if value == nil { - return nil + return defaultValue } - // Direct array - if arr, ok := value.([]any); ok { - return arr - } - - // Arrays in Lua might be represented as maps with an empty string key - valueMap, ok := value.(map[string]any) - if !ok { - return nil - } - - // The array data is stored with an empty key - arr, ok := valueMap[""] - if !ok { - return nil - } - - // Check if it's a float64 array (common for Lua numeric arrays) - if floatArr, ok := arr.([]float64); ok { - // Convert to []any - result := make([]any, len(floatArr)) - for i, v := range floatArr { - result[i] = v + // Convert to string + switch v := value.(type) { + case string: + return v + case float64: + return fmt.Sprintf("%g", v) + case int: + return strconv.Itoa(v) + case bool: + if v { + return "true" } - return result + return "false" + default: + return defaultValue } - - // Otherwise, try to return as is - anyArr, ok := arr.([]any) - if !ok { - return nil - } - - return anyArr } -// GetStringArray returns an array of strings from a Lua array -func (c *Config) GetStringArray(key string) []string { - // Special case for lib_dirs - if key == "lib_dirs" { - return c.LibDirs +// GetCustomInt returns a custom integer configuration value +func (c *Config) GetCustomInt(key string, defaultValue int) int { + value := c.values[key] + if value == nil { + return defaultValue } - arr := c.GetArray(key) - if arr == nil { - return nil - } - - result := make([]string, 0, len(arr)) - for _, v := range arr { - if str, ok := luajit.ConvertValue[string](v); ok { - result = append(result, str) + // Convert to int + switch v := value.(type) { + case int: + return v + case float64: + return int(v) + case string: + if i, err := strconv.Atoi(v); err == nil { + return i } + case bool: + if v { + return 1 + } + return 0 + default: + return defaultValue } - return result + return defaultValue } -// Values returns all configuration values -func (c *Config) Values() map[string]any { - return c.values +// GetCustomBool returns a custom boolean configuration value +func (c *Config) GetCustomBool(key string, defaultValue bool) bool { + value := c.values[key] + if value == nil { + return defaultValue + } + + // Convert to bool + switch v := value.(type) { + case bool: + return v + case string: + switch v { + case "true", "yes", "1": + return true + case "false", "no", "0", "": + return false + } + case int: + return v != 0 + case float64: + return v != 0 + default: + return defaultValue + } + + return defaultValue } diff --git a/core/runner/LuaRunner.go b/core/runner/LuaRunner.go index f478d46..c39f6e0 100644 --- a/core/runner/LuaRunner.go +++ b/core/runner/LuaRunner.go @@ -89,7 +89,7 @@ func WithDebugEnabled() RunnerOption { func NewRunner(options ...RunnerOption) (*LuaRunner, error) { // Default configuration runner := &LuaRunner{ - poolSize: runtime.GOMAXPROCS(0), // Default pool size to number of CPUs + poolSize: runtime.GOMAXPROCS(0), debug: false, } diff --git a/main.go b/main.go new file mode 100644 index 0000000..632e3e2 --- /dev/null +++ b/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "os" + "os/signal" + "syscall" + + "git.sharkk.net/Sky/Moonshark/core/logger" +) + +func main() { + // Initialize global logger + logger.InitGlobalLogger(logger.LevelDebug, true) + logger.Server("Starting Moonshark server") + + // Create and initialize server + server, err := NewMoonshark("config.lua") + if err != nil { + logger.Fatal("Failed to initialize server: %v", err) + } + + // Start server + if err := server.Start(); err != nil { + logger.Fatal("Failed to start server: %v", err) + } + + // Wait for interrupt signal + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGTERM) + <-stop + + logger.Server("Shutdown signal received") + + // Shutdown server + if err := server.Shutdown(); err != nil { + logger.Error("Error during shutdown: %v", err) + os.Exit(1) + } +}