138 lines
3.8 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/signal"
"syscall"
)
const (
ConfigFile = "world_config.json"
Version = "0.1.0"
)
// printHeader displays the EQ2Emu banner and copyright info
func printHeader() {
fmt.Println("EQ2Emulator World Server")
fmt.Printf("Version: %s\n", Version)
fmt.Println()
fmt.Println("Copyright (C) 2007-2026 EQ2Emulator Development Team")
fmt.Println("https://www.eq2emu.com")
fmt.Println()
fmt.Println("EQ2Emulator is free software licensed under the GNU GPL v3")
fmt.Println("See LICENSE file for details")
fmt.Println()
}
// loadConfig loads configuration from JSON file with command line overrides
func loadConfig() (*WorldConfig, error) {
// Default configuration
config := &WorldConfig{
ListenAddr: "0.0.0.0",
ListenPort: 9000,
MaxClients: 1000,
BufferSize: 8192,
WebAddr: "0.0.0.0",
WebPort: 8080,
DatabasePath: "world.db",
XPRate: 1.0,
TSXPRate: 1.0,
VitalityRate: 1.0,
LogLevel: "info",
ThreadedLoad: true,
}
// Load from config file if it exists
if data, err := os.ReadFile(ConfigFile); err == nil {
if err := json.Unmarshal(data, config); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
log.Printf("Loaded configuration from %s", ConfigFile)
} else {
log.Printf("Config file %s not found, using defaults", ConfigFile)
}
// Command line overrides
flag.StringVar(&config.ListenAddr, "listen-addr", config.ListenAddr, "UDP listen address")
flag.IntVar(&config.ListenPort, "listen-port", config.ListenPort, "UDP listen port")
flag.IntVar(&config.MaxClients, "max-clients", config.MaxClients, "Maximum client connections")
flag.StringVar(&config.WebAddr, "web-addr", config.WebAddr, "Web server address")
flag.IntVar(&config.WebPort, "web-port", config.WebPort, "Web server port")
flag.StringVar(&config.DatabasePath, "db-path", config.DatabasePath, "Database file path")
flag.StringVar(&config.LogLevel, "log-level", config.LogLevel, "Log level (debug, info, warn, error)")
flag.BoolVar(&config.ThreadedLoad, "threaded-load", config.ThreadedLoad, "Use threaded loading")
flag.Parse()
return config, nil
}
// saveConfig saves the current configuration to file
func saveConfig(config *WorldConfig) error {
data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.WriteFile(ConfigFile, data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
return nil
}
// setupSignalHandlers sets up graceful shutdown on SIGINT/SIGTERM
func setupSignalHandlers(world *World) <-chan os.Signal {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigChan
log.Printf("Received signal %v, initiating graceful shutdown...", sig)
world.Shutdown()
}()
return sigChan
}
func main() {
printHeader()
// Load configuration
config, err := loadConfig()
if err != nil {
log.Fatalf("Configuration error: %v", err)
}
// Save config file with any command line overrides
if err := saveConfig(config); err != nil {
log.Printf("Warning: failed to save config: %v", err)
}
// Create world server instance
world, err := NewWorld(config)
if err != nil {
log.Fatalf("Failed to create world server: %v", err)
}
// Initialize all components
log.Println("Initializing EQ2Emulator World Server...")
if err := world.Initialize(); err != nil {
log.Fatalf("Failed to initialize world server: %v", err)
}
// Setup signal handlers for graceful shutdown
setupSignalHandlers(world)
// Run the server
log.Println("Starting World Server...")
if err := world.Run(); err != nil {
log.Fatalf("World server error: %v", err)
}
log.Println("World Server stopped gracefully")
}