package main import ( "flag" "fmt" "log" "os" "os/signal" "syscall" "thehouseoficarus/internal/admin" "thehouseoficarus/internal/behavior" "thehouseoficarus/internal/config" "thehouseoficarus/internal/game" "thehouseoficarus/internal/net" ) func main() { configPath := flag.String("config", "config.yaml", "path to config file") flag.Parse() dataDir := os.Getenv("DATA_DIR") if dataDir == "" { dataDir = "data" } cfg, err := config.Load(*configPath) if err != nil { if os.IsNotExist(err) { log.Printf("config file %s not found, creating with defaults", *configPath) if err := config.SaveDefault(*configPath); err != nil { log.Fatalf("failed to create config: %v", err) } cfg = config.Default() } else { log.Fatalf("failed to load config: %v", err) } } if cfg.GameConstants.ShopDefaultRestock > 0 { behavior.ShopDefaultRestock = cfg.GameConstants.ShopDefaultRestock } g := game.New(dataDir, &cfg.DefaultColors, cfg.StartupValidation, cfg.StartingRoom) g.Ticks.Start(cfg.TickLength) defer g.Ticks.Stop() g.Ticks.Subscribe(1, func() bool { g.MoveTick() g.ProcessQueuedCommands() g.EnterSeqTick() g.TriggerSeqTick() g.TransientMobTick() g.World.Tick() g.MobStore.Tick() g.RegenTick() g.DisconnectTick() g.WanderTick() g.SharedDepletionTick() g.FireTick() g.AdvanceActions() g.TechTick() g.ConsumeTick() g.BroadcastRespawns() g.VisualTick() g.SneakTick() g.FarmTick() g.BuffTick() g.SafespotTick() g.HazardTick() return true }) server, err := net.NewServer(cfg) if err != nil { log.Fatalf("failed to start server: %v", err) } g.SetHub(server.Hub()) junkItems := []string{"solarjunk", "ecojunk", "hydrojunk", "biojunk", "chaosjunk", "cosmicjunk", "naturejunk", "bloodjunk", "deathjunk", "lawjunk", "scrap"} for _, j := range junkItems { g.World.AddGroundItem(1, j, 500) } if cfg.AdminHTTP.Enabled { adminSrv, err := admin.NewServer(cfg, false, g.AccountStore, g.World, g.ItemStore, g.ObjectStore, g.MobStore, dataDir, g.CourseStore.ReloadAll) if err != nil { log.Fatalf("failed to start admin HTTP server: %v", err) } go func() { if err := adminSrv.ListenAndServe(); err != nil { log.Printf("admin HTTP server error: %v", err) } }() log.Printf("Admin HTTP listening on :%d", cfg.AdminHTTP.Port) } if cfg.AdminHTTPS.Enabled { adminSrv, err := admin.NewServer(cfg, true, g.AccountStore, g.World, g.ItemStore, g.ObjectStore, g.MobStore, dataDir, g.CourseStore.ReloadAll) if err != nil { log.Fatalf("failed to start admin HTTPS server: %v", err) } go func() { if err := adminSrv.ListenAndServe(); err != nil { log.Printf("admin HTTPS server error: %v", err) } }() log.Printf("Admin HTTPS listening on :%d", cfg.AdminHTTPS.Port) } if cfg.Telnet.Enabled { log.Printf("Telnet listening on :%d", cfg.Telnet.Port) } if cfg.TelnetTLS.Enabled { log.Printf("Telnet+TLS listening on :%d", cfg.TelnetTLS.Port) } if cfg.HTTP.Enabled { log.Printf("HTTP listening on :%d", cfg.HTTP.Port) } if cfg.HTTPS.Enabled { log.Printf("HTTPS listening on :%d", cfg.HTTPS.Port) } fmt.Println() // blank line after log prefixes sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigCh log.Println("Shutting down...") g.Ticks.Stop() os.Exit(0) }() if err := server.ListenAndServe(g.HandleSession); err != nil { log.Fatalf("server error: %v", err) } }