package config import ( "encoding/json" "fmt" "os" "path/filepath" ) type Config struct { Client ClientConfig `json:"client"` Network NetworkConfig `json:"network"` Admin AdminConfig `json:"admin"` Player PlayerConfig `json:"player"` World WorldConfig `json:"world"` } type ClientConfig struct { Version int `json:"version"` ArtifactVersion string `json:"artifactVersion"` ServerID int `json:"serverId"` Aid int `json:"aid"` RpcMd5 string `json:"rpcMd5"` NoMoreHotfixPatchError uint32 `json:"noMoreHotfixPatchError"` } type NetworkConfig struct { BindHost string `json:"bindHost"` AdvertisedHost string `json:"advertisedHost"` LoginPorts []int `json:"loginPorts"` GamePort int `json:"gamePort"` SocketHeartbeatMs int `json:"socketHeartbeatMs"` WriteTimeoutMs int `json:"writeTimeoutMs"` Proxy ProxyConfig `json:"proxy"` } type ProxyConfig struct { HttpsPort int `json:"httpsPort"` HttpPort int `json:"httpPort"` LoginListPort int `json:"loginListPort"` LoginTcpPort int `json:"loginTcpPort"` GameTcpPort int `json:"gameTcpPort"` SceneSubPort int `json:"sceneSubPort"` FastPatchPort int `json:"fastPatchPort"` UpdateHost string `json:"updateHost"` CertificatePassphrase string `json:"certificatePassphrase"` } type AdminConfig struct { Enabled bool `json:"enabled"` Host string `json:"host"` Port int `json:"port"` } type PlayerConfig struct { Pid uint64 `json:"pid"` InitialUnitId uint64 `json:"initialUnitId"` InitialSpiritTemplateId uint32 `json:"initialSpiritTemplateId"` AccountId string `json:"accountId"` UserName string `json:"userName"` DisplayName string `json:"displayName"` LoginToken string `json:"loginToken"` GameToken string `json:"gameToken"` LoginUniverseId uint32 `json:"loginUniverseId"` ShareToken string `json:"shareToken"` FpPassToken string `json:"fpPassToken"` Skey string `json:"skey"` } type Vec3 struct { X float64 `json:"x"` Y float64 `json:"y"` Z float64 `json:"z"` } type WorldConfig struct { RaidId uint32 `json:"raidId"` SceneInstanceId uint64 `json:"sceneInstanceId"` UniverseId uint32 `json:"universeId"` Spawn Vec3 `json:"spawn"` Facing float32 `json:"facing"` EntryOpeningEnabled bool `json:"entryOpeningEnabled"` } // LoadConfig reads and parses private-server.json from the specified path or standard locations. func LoadConfig(path string) (*Config, error) { if path == "" { candidates := []string{ "config/private-server.json", "../config/private-server.json", "../../config/private-server.json", } for _, c := range candidates { if _, err := os.Stat(c); err == nil { path = c break } } } if path == "" { return nil, fmt.Errorf("config/private-server.json not found") } absPath, err := filepath.Abs(path) if err != nil { absPath = path } data, err := os.ReadFile(absPath) if err != nil { return nil, fmt.Errorf("failed to read config from %s: %w", absPath, err) } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("failed to parse json config from %s: %w", absPath, err) } // Apply default values if omitted if cfg.Network.BindHost == "" { cfg.Network.BindHost = "0.0.0.0" } if cfg.Network.AdvertisedHost == "" { cfg.Network.AdvertisedHost = "127.0.0.1" } if len(cfg.Network.LoginPorts) == 0 { cfg.Network.LoginPorts = []int{5200, 5201} } if cfg.Network.GamePort == 0 { cfg.Network.GamePort = 5202 } if cfg.Network.Proxy.HttpsPort == 0 { cfg.Network.Proxy.HttpsPort = 443 } if cfg.Network.Proxy.HttpPort == 0 { cfg.Network.Proxy.HttpPort = 80 } if cfg.Network.Proxy.LoginListPort == 0 { cfg.Network.Proxy.LoginListPort = 5801 } if cfg.Admin.Port == 0 { cfg.Admin.Port = 17888 } if cfg.Player.Pid == 0 { cfg.Player.Pid = 100 } if cfg.Player.InitialUnitId == 0 { cfg.Player.InitialUnitId = 100000000000 } if cfg.Player.InitialSpiritTemplateId == 0 { cfg.Player.InitialSpiritTemplateId = 15020967 // Default character } return &cfg, nil }