feat: add configuration package with YAML loading and defaults
Add config package that loads game settings from YAML files with sensible defaults for server, game, combat, dungeon, and backup settings. Includes config.yaml with all defaults documented. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
75
config/config.go
Normal file
75
config/config.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig `yaml:"server"`
|
||||
Game GameConfig `yaml:"game"`
|
||||
Combat CombatConfig `yaml:"combat"`
|
||||
Dungeon DungeonConfig `yaml:"dungeon"`
|
||||
Backup BackupConfig `yaml:"backup"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
SSHPort int `yaml:"ssh_port"`
|
||||
HTTPPort int `yaml:"http_port"`
|
||||
}
|
||||
|
||||
type GameConfig struct {
|
||||
TurnTimeoutSec int `yaml:"turn_timeout_sec"`
|
||||
MaxPlayers int `yaml:"max_players"`
|
||||
MaxFloors int `yaml:"max_floors"`
|
||||
CoopBonus float64 `yaml:"coop_bonus"`
|
||||
InventoryLimit int `yaml:"inventory_limit"`
|
||||
SkillUses int `yaml:"skill_uses"`
|
||||
}
|
||||
|
||||
type CombatConfig struct {
|
||||
FleeChance float64 `yaml:"flee_chance"`
|
||||
MonsterScaling float64 `yaml:"monster_scaling"`
|
||||
SoloHPReduction float64 `yaml:"solo_hp_reduction"`
|
||||
}
|
||||
|
||||
type DungeonConfig struct {
|
||||
MapWidth int `yaml:"map_width"`
|
||||
MapHeight int `yaml:"map_height"`
|
||||
MinRooms int `yaml:"min_rooms"`
|
||||
MaxRooms int `yaml:"max_rooms"`
|
||||
}
|
||||
|
||||
type BackupConfig struct {
|
||||
IntervalMin int `yaml:"interval_min"`
|
||||
Dir string `yaml:"dir"`
|
||||
}
|
||||
|
||||
func defaults() Config {
|
||||
return Config{
|
||||
Server: ServerConfig{SSHPort: 2222, HTTPPort: 8080},
|
||||
Game: GameConfig{
|
||||
TurnTimeoutSec: 5, MaxPlayers: 4, MaxFloors: 20,
|
||||
CoopBonus: 0.10, InventoryLimit: 10, SkillUses: 3,
|
||||
},
|
||||
Combat: CombatConfig{FleeChance: 0.50, MonsterScaling: 1.15, SoloHPReduction: 0.50},
|
||||
Dungeon: DungeonConfig{MapWidth: 60, MapHeight: 20, MinRooms: 5, MaxRooms: 8},
|
||||
Backup: BackupConfig{IntervalMin: 60, Dir: "./data/backup"},
|
||||
}
|
||||
}
|
||||
|
||||
func Load(path string) (*Config, error) {
|
||||
cfg := defaults()
|
||||
if path == "" {
|
||||
return &cfg, nil
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
85
config/config_test.go
Normal file
85
config/config_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadDefaults(t *testing.T) {
|
||||
cfg, err := Load("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.Server.SSHPort != 2222 {
|
||||
t.Errorf("expected SSH port 2222, got %d", cfg.Server.SSHPort)
|
||||
}
|
||||
if cfg.Server.HTTPPort != 8080 {
|
||||
t.Errorf("expected HTTP port 8080, got %d", cfg.Server.HTTPPort)
|
||||
}
|
||||
if cfg.Game.TurnTimeoutSec != 5 {
|
||||
t.Errorf("expected turn timeout 5, got %d", cfg.Game.TurnTimeoutSec)
|
||||
}
|
||||
if cfg.Game.MaxPlayers != 4 {
|
||||
t.Errorf("expected max players 4, got %d", cfg.Game.MaxPlayers)
|
||||
}
|
||||
if cfg.Game.MaxFloors != 20 {
|
||||
t.Errorf("expected max floors 20, got %d", cfg.Game.MaxFloors)
|
||||
}
|
||||
if cfg.Game.CoopBonus != 0.10 {
|
||||
t.Errorf("expected coop bonus 0.10, got %f", cfg.Game.CoopBonus)
|
||||
}
|
||||
if cfg.Game.InventoryLimit != 10 {
|
||||
t.Errorf("expected inventory limit 10, got %d", cfg.Game.InventoryLimit)
|
||||
}
|
||||
if cfg.Combat.FleeChance != 0.50 {
|
||||
t.Errorf("expected flee chance 0.50, got %f", cfg.Combat.FleeChance)
|
||||
}
|
||||
if cfg.Combat.MonsterScaling != 1.15 {
|
||||
t.Errorf("expected monster scaling 1.15, got %f", cfg.Combat.MonsterScaling)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromFile(t *testing.T) {
|
||||
content := []byte(`
|
||||
server:
|
||||
ssh_port: 3333
|
||||
http_port: 9090
|
||||
game:
|
||||
turn_timeout_sec: 10
|
||||
max_players: 2
|
||||
`)
|
||||
f, err := os.CreateTemp("", "config-*.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
f.Write(content)
|
||||
f.Close()
|
||||
|
||||
cfg, err := Load(f.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.Server.SSHPort != 3333 {
|
||||
t.Errorf("expected SSH port 3333, got %d", cfg.Server.SSHPort)
|
||||
}
|
||||
if cfg.Server.HTTPPort != 9090 {
|
||||
t.Errorf("expected HTTP port 9090, got %d", cfg.Server.HTTPPort)
|
||||
}
|
||||
if cfg.Game.TurnTimeoutSec != 10 {
|
||||
t.Errorf("expected turn timeout 10, got %d", cfg.Game.TurnTimeoutSec)
|
||||
}
|
||||
if cfg.Game.MaxPlayers != 2 {
|
||||
t.Errorf("expected max players 2, got %d", cfg.Game.MaxPlayers)
|
||||
}
|
||||
// Unset fields should have defaults
|
||||
if cfg.Game.MaxFloors != 20 {
|
||||
t.Errorf("expected default max floors 20, got %d", cfg.Game.MaxFloors)
|
||||
}
|
||||
if cfg.Combat.FleeChance != 0.50 {
|
||||
t.Errorf("expected default flee chance 0.50, got %f", cfg.Combat.FleeChance)
|
||||
}
|
||||
if cfg.Dungeon.MapWidth != 60 {
|
||||
t.Errorf("expected default map width 60, got %d", cfg.Dungeon.MapWidth)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user