optimize config, fix poolsize, structurize moonshark

This commit is contained in:
Sky Johnson 2025-03-29 11:12:48 -05:00
parent 8a3515ed06
commit 2a2ffc9cc5
4 changed files with 375 additions and 476 deletions

View File

@ -4,10 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"os/signal"
"runtime"
"syscall"
"time" "time"
"git.sharkk.net/Sky/Moonshark/core/config" "git.sharkk.net/Sky/Moonshark/core/config"
@ -19,115 +15,79 @@ import (
"git.sharkk.net/Sky/Moonshark/core/watchers" "git.sharkk.net/Sky/Moonshark/core/watchers"
) )
// WatcherConfig holds the configuration for which watchers to enable // Moonshark represents the server and all its dependencies
type WatcherConfig struct { type Moonshark struct {
Routes bool Config *config.Config
Static bool LuaRouter *routers.LuaRouter
Modules bool 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 // NewMoonshark creates a new Moonshark server instance
func initRouters(routesDir, staticDir string) (*routers.LuaRouter, *routers.StaticRouter, error) { func NewMoonshark(configPath string) (*Moonshark, error) {
// Ensure directories exist // Initialize server struct
if err := utils.EnsureDir(routesDir); err != nil { server := &Moonshark{}
return nil, nil, fmt.Errorf("routes directory doesn't exist, and could not create it: %v", err)
} // Load configuration
if err := utils.EnsureDir(staticDir); err != nil { if err := server.loadConfig(configPath); err != nil {
return nil, nil, fmt.Errorf("static directory doesn't exist, and could not create it: %v", err) return nil, err
} }
// Initialize Lua router for dynamic routes // Configure logging
luaRouter, err := routers.NewLuaRouter(routesDir) server.setupLogging()
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 // Initialize routers
if failedRoutes := luaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 { if err := server.initRouters(); err != nil {
for _, re := range failedRoutes { return nil, err
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)
}
} }
logger.Info("Lua router initialized with routes from %s", routesDir)
// Initialize static file router // Initialize Lua runner
staticRouter, err := routers.NewStaticRouter(staticDir) if err := server.initRunner(); err != nil {
if err != nil { return nil, err
return nil, nil, fmt.Errorf("failed to initialize static router: %v", 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 // loadConfig loads the configuration file
func setupWatchers(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter, func (s *Moonshark) loadConfig(configPath string) error {
luaRunner *runner.LuaRunner, routesDir string, staticDir string, var err error
libDirs []string, config WatcherConfig) ([]func() error, error) {
var cleanupFuncs []func() error // Load configuration from file
s.Config, err = config.Load(configPath)
// 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")
if err != nil { 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") 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 // Set log level from config
switch cfg.GetString("log_level", "info") { switch s.Config.LogLevel {
case "warn": case "warn":
logger.SetLevel(logger.LevelWarning) logger.SetLevel(logger.LevelWarning)
case "error": case "error":
@ -140,143 +100,180 @@ func main() {
logger.SetLevel(logger.LevelInfo) logger.SetLevel(logger.LevelInfo)
} }
// Get debug mode setting // Set debug mode if configured
debugMode := cfg.GetBool("debug", false) if s.Config.Debug {
if debugMode {
logger.EnableDebug() // Force debug logs regardless of level logger.EnableDebug() // Force debug logs regardless of level
logger.Debug("Debug mode enabled") logger.Debug("Debug mode enabled")
} }
}
// Get configuration values // initRouters initializes the Lua and static routers
port := cfg.GetInt("port", 3117) func (s *Moonshark) initRouters() error {
routesDir := cfg.GetString("routes_dir", "./routes") // Ensure directories exist
staticDir := cfg.GetString("static_dir", "./static") if err := utils.EnsureDir(s.Config.RoutesDir); err != nil {
overrideDir := cfg.GetString("override_dir", "./override") return fmt.Errorf("routes directory doesn't exist and could not be created: %v", err)
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
} }
// Get library directories if err := utils.EnsureDir(s.Config.StaticDir); err != nil {
var libDirs []string return fmt.Errorf("static directory doesn't exist and could not be created: %v", err)
libDirsConfig := cfg.GetStringArray("lib_dirs") }
if len(libDirsConfig) > 0 {
libDirs = libDirsConfig // Initialize Lua router for dynamic routes
} else { var err error
// Default lib directory s.LuaRouter, err = routers.NewLuaRouter(s.Config.RoutesDir)
libDirs = []string{"./libs"} 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 // Ensure lib directories exist
for _, dir := range libDirs { for _, dir := range s.Config.LibDirs {
if err := utils.EnsureDir(dir); err != nil { 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 // Set up runner options
luaRouter, staticRouter, err := initRouters(routesDir, staticDir)
if err != nil {
logger.Fatal("Router initialization failed: %v", err)
}
// Initialize Lua runner with options
runnerOpts := []runner.RunnerOption{ runnerOpts := []runner.RunnerOption{
runner.WithPoolSize(poolSize), runner.WithPoolSize(s.Config.PoolSize),
runner.WithLibDirs(libDirs...), runner.WithLibDirs(s.Config.LibDirs...),
} }
// Add debug option conditionally // Add debug option conditionally
if debugMode { if s.Config.Debug {
runnerOpts = append(runnerOpts, runner.WithDebugEnabled()) runnerOpts = append(runnerOpts, runner.WithDebugEnabled())
logger.Debug("Debug logging enabled for Lua runner") 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 { if err != nil {
logger.Fatal("Failed to initialize Lua runner: %v", err) return fmt.Errorf("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,
} }
// Enable watchers based on map configuration logger.Server("Lua runner initialized with pool size %d", s.Config.PoolSize)
watchersMap := cfg.GetMap("watchers") return nil
if watchersMap != nil { }
if routes, ok := watchersMap["routes"].(bool); ok && routes {
watcherConfig.Routes = true // setupWatchers initializes and starts all configured file watchers
} func (s *Moonshark) setupWatchers() error {
if static, ok := watchersMap["static"].(bool); ok && static { // Set up watcher for Lua routes
watcherConfig.Static = true if s.Config.Watchers.Routes {
} luaRouterWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.RoutesDir)
if modules, ok := watchersMap["modules"].(bool); ok && modules { if err != nil {
watcherConfig.Modules = true logger.Warning("Failed to watch routes directory: %v", err)
} else {
s.cleanupFuncs = append(s.cleanupFuncs, luaRouterWatcher.Close)
} }
} }
// Setup enabled watchers // Set up watcher for static files
cleanupFuncs, err = setupWatchers(luaRouter, staticRouter, luaRunner, if s.Config.Watchers.Static {
routesDir, staticDir, libDirs, watcherConfig) staticWatcher, err := watchers.WatchStaticRouter(s.StaticRouter, s.Config.StaticDir)
if err != nil { if err != nil {
logger.Warning("Error setting up watchers: %v", err) logger.Warning("Failed to watch static directory: %v", err)
} else {
s.cleanupFuncs = append(s.cleanupFuncs, staticWatcher.Close)
}
} }
// Register cleanup functions // Set up watchers for Lua modules libraries
defer func() { if s.Config.Watchers.Modules && len(s.Config.LibDirs) > 0 {
for _, cleanup := range cleanupFuncs { moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.LibDirs)
if err := cleanup(); err != nil { if err != nil {
logger.Warning("Cleanup error: %v", err) 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) return nil
if httpLoggingEnabled { }
// 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") logger.Info("HTTP logging is enabled")
} else { } else {
logger.Info("HTTP logging is disabled") logger.Info("HTTP logging is disabled")
} }
// Create HTTP server // Start the server in a non-blocking way
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
go func() { 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" { if err.Error() != "http: Server closed" {
logger.Error("Server error: %v", err) logger.Error("Server error: %v", err)
} }
} }
}() }()
logger.Server("Server started on port %d", port) return nil
}
// Wait for interrupt signal // Shutdown gracefully shuts down the server
<-stop func (s *Moonshark) Shutdown() error {
logger.Server("Shutdown signal received") logger.Server("Shutting down server...")
// Gracefully shut down the server // Shutdown HTTP server with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
if err := server.Shutdown(ctx); err != nil { if err := s.HTTPServer.Shutdown(ctx); err != nil {
logger.Error("Server shutdown error: %v", err) 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") logger.Server("Server stopped")
return nil
} }

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"runtime" "runtime"
"strconv"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go" luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
) )
@ -22,13 +23,17 @@ type Config struct {
LibDirs []string LibDirs []string
// Performance settings // Performance settings
PoolSize int // Number of Lua states in the pool PoolSize int
// Feature flags // Feature flags
HTTPLoggingEnabled bool 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 values map[string]any
} }
@ -46,16 +51,11 @@ func New() *Config {
OverrideDir: "./override", OverrideDir: "./override",
LibDirs: []string{"./libs"}, LibDirs: []string{"./libs"},
// Performance defaults // Performance defaults - respect GOMAXPROCS
PoolSize: runtime.NumCPU(), PoolSize: runtime.GOMAXPROCS(0),
// Feature flag defaults // Feature flag defaults
HTTPLoggingEnabled: true, HTTPLoggingEnabled: true,
Watchers: map[string]bool{
"routes": false,
"static": false,
"modules": false,
},
// Initialize values map // Initialize values map
values: make(map[string]any), values: make(map[string]any),
@ -64,8 +64,8 @@ func New() *Config {
// Load loads configuration from a Lua file // Load loads configuration from a Lua file
func Load(filePath string) (*Config, error) { func Load(filePath string) (*Config, error) {
// Create a new Lua state without standard libraries // For simple configs, use standard libraries to ensure proper loading
state := luajit.New(false) state := luajit.New(true)
if state == nil { if state == nil {
return nil, errors.New("failed to create Lua state") return nil, errors.New("failed to create Lua state")
} }
@ -74,122 +74,106 @@ func Load(filePath string) (*Config, error) {
// Create config with default values // Create config with default values
config := New() config := New()
// Execute the Lua file // Execute the config file
if err := state.DoFile(filePath); err != nil { if err := state.DoFile(filePath); err != nil {
return nil, fmt.Errorf("failed to load config file: %w", err) return nil, fmt.Errorf("failed to load config file: %w", err)
} }
// Extract all globals from the Lua state // Extract configuration values
if err := extractGlobals(state, config); err != nil { extractConfig(state, config)
return nil, err
}
return config, nil return config, nil
} }
// extractGlobals extracts global variables from the Lua state // extractConfig extracts configuration values directly by name
func extractGlobals(state *luajit.State, config *Config) error { func extractConfig(state *luajit.State, config *Config) {
// Get the globals table // Process known config values directly by key
state.GetGlobal("_G") if value := extractValue(state, "debug"); value != nil {
if !state.IsTable(-1) { if boolVal, ok := value.(bool); ok {
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 {
config.Debug = boolVal 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 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 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 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 config.HTTPLoggingEnabled = boolVal
} }
case "watchers": }
// Handle watchers table
if value := extractValue(state, "watchers"); value != nil {
if table, ok := value.(map[string]any); ok { if table, ok := value.(map[string]any); ok {
for k, v := range table { if routes, ok := table["routes"].(bool); ok {
if boolVal, ok := luajit.ConvertValue[bool](v); ok { config.Watchers.Routes = routes
config.Watchers[k] = boolVal }
} 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 { if dirs := extractStringArray(value); len(dirs) > 0 {
config.LibDirs = dirs 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 // extractStringArray extracts a string array from various possible formats
@ -198,7 +182,7 @@ func extractStringArray(value any) []string {
if arr, ok := value.([]any); ok { if arr, ok := value.([]any); ok {
result := make([]string, 0, len(arr)) result := make([]string, 0, len(arr))
for _, v := range arr { for _, v := range arr {
if str, ok := luajit.ConvertValue[string](v); ok { if str, ok := v.(string); ok {
result = append(result, str) result = append(result, str)
} }
} }
@ -223,7 +207,7 @@ func extractStringArray(value any) []string {
case []any: case []any:
result := make([]string, 0, len(arr)) result := make([]string, 0, len(arr))
for _, v := range arr { for _, v := range arr {
if str, ok := luajit.ConvertValue[string](v); ok { if str, ok := v.(string); ok {
result = append(result, str) result = append(result, str)
} }
} }
@ -239,211 +223,90 @@ func extractStringArray(value any) []string {
return nil return nil
} }
// Get returns a configuration value by key // GetCustomValue returns any custom configuration value by key
func (c *Config) Get(key string) any { func (c *Config) GetCustomValue(key string) any {
return c.values[key] return c.values[key]
} }
// GetString returns a string configuration value // GetCustomString returns a custom string configuration value
func (c *Config) GetString(key string, defaultValue string) string { func (c *Config) GetCustomString(key string, defaultValue string) string {
// Check for specific struct fields first for better performance value := c.values[key]
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)
if value == nil { if value == nil {
return nil return defaultValue
} }
// Direct array // Convert to string
if arr, ok := value.([]any); ok { switch v := value.(type) {
return arr case string:
} return v
case float64:
// Arrays in Lua might be represented as maps with an empty string key return fmt.Sprintf("%g", v)
valueMap, ok := value.(map[string]any) case int:
if !ok { return strconv.Itoa(v)
return nil case bool:
} if v {
return "true"
// 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
} }
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 // GetCustomInt returns a custom integer configuration value
func (c *Config) GetStringArray(key string) []string { func (c *Config) GetCustomInt(key string, defaultValue int) int {
// Special case for lib_dirs value := c.values[key]
if key == "lib_dirs" { if value == nil {
return c.LibDirs return defaultValue
} }
arr := c.GetArray(key) // Convert to int
if arr == nil { switch v := value.(type) {
return nil case int:
} return v
case float64:
result := make([]string, 0, len(arr)) return int(v)
for _, v := range arr { case string:
if str, ok := luajit.ConvertValue[string](v); ok { if i, err := strconv.Atoi(v); err == nil {
result = append(result, str) return i
} }
case bool:
if v {
return 1
}
return 0
default:
return defaultValue
} }
return result return defaultValue
} }
// Values returns all configuration values // GetCustomBool returns a custom boolean configuration value
func (c *Config) Values() map[string]any { func (c *Config) GetCustomBool(key string, defaultValue bool) bool {
return c.values 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
} }

View File

@ -89,7 +89,7 @@ func WithDebugEnabled() RunnerOption {
func NewRunner(options ...RunnerOption) (*LuaRunner, error) { func NewRunner(options ...RunnerOption) (*LuaRunner, error) {
// Default configuration // Default configuration
runner := &LuaRunner{ runner := &LuaRunner{
poolSize: runtime.GOMAXPROCS(0), // Default pool size to number of CPUs poolSize: runtime.GOMAXPROCS(0),
debug: false, debug: false,
} }

39
main.go Normal file
View File

@ -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)
}
}