From c3e17f9a80769fc0368fd1c8621606bf5dc47f6e Mon Sep 17 00:00:00 2001 From: chris bell Date: Sat, 6 Jun 2026 18:09:07 -0500 Subject: [PATCH 1/2] Can now change all configuration options per instance --- README.md | 5 +++-- config.go | 4 ++-- daemon.go | 7 +++---- instance.go | 4 ++-- instance_config.go | 34 +++++++++++++--------------------- process.go | 2 +- 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index f9b2ed8..e763193 100644 --- a/README.md +++ b/README.md @@ -34,18 +34,19 @@ Alongside this project, I am developing a seperate web-based dashboard that can - [x] Daemon instance remembers config path (*so you don't have to pass `--config` everytime you run an ipc command*) - [ ] Daemon listen port (kind of implemented, could be cleaner) - [x] Declarative Server configuration (Create servers in config) - - [ ] More configuration options + - [x] All configuration options - [ ] Server management - [x] Create servers - [x] Automatic server binary downloading by version - [x] Delete servers - [x] Start/Stop servers - - [ ] Server configuration + - [x] Server configuration - [x] Download correct configuration template for version (see availible templates [here](https://git.bellsworne.tech/chrisbell/vssm_config_templates)) - [x] Version - [x] Server Name - [x] Port + - [x] Full declarative configuration support in main app config - [x] Multiple servers support - [x] List servers and their status - [x] Send commands to servers diff --git a/config.go b/config.go index 46af439..f1b2350 100644 --- a/config.go +++ b/config.go @@ -20,7 +20,7 @@ type AppConfig struct { ListenAddress string `json:"listen_address"` } `json:"daemon"` - Instances map[string]VsServerConfigOptions `json:"instances"` + Instances map[string]InstanceMetadata `json:"instances"` } func CreateConfigWithDefaults(configPath string) *AppConfig { @@ -36,7 +36,7 @@ func CreateConfigWithDefaults(configPath string) *AppConfig { cfg.Daemon.ListenAddress = "127.0.0.1:12345" - cfg.Instances = make(map[string]VsServerConfigOptions) + cfg.Instances = make(map[string]InstanceMetadata) return cfg } diff --git a/daemon.go b/daemon.go index 45c6da9..4e4f789 100644 --- a/daemon.go +++ b/daemon.go @@ -193,11 +193,10 @@ func (ds *DaemonServer) handleCreate(w http.ResponseWriter, r *http.Request) { } } - options := VsServerConfigOptions{ + metadata := InstanceMetadata{ Version: version, ServerName: name, Port: converted_port, - MaxClients: 10, } err = DownloadAndExtractServer(version, ds.cfg.Storage.InstallDir) @@ -206,13 +205,13 @@ func (ds *DaemonServer) handleCreate(w http.ResponseWriter, r *http.Request) { return } - err = CreateNewInstance(name, version, options, ds.cfg) + err = CreateNewInstance(name, version, metadata, ds.cfg) if err != nil { http.Error(w, fmt.Sprintf("Instance provisioning failed: %v", err), http.StatusInternalServerError) return } - ds.cfg.Instances[name] = options + ds.cfg.Instances[name] = metadata data, err := json.MarshalIndent(ds.cfg, "", " ") if err != nil { diff --git a/instance.go b/instance.go index 3d5c13f..5563e73 100644 --- a/instance.go +++ b/instance.go @@ -13,7 +13,7 @@ const ( StateRunning InstanceState = "RUNNING" ) -func CreateNewInstance(name string, version string, options VsServerConfigOptions, cfg *AppConfig) error { +func CreateNewInstance(name string, version string, meta InstanceMetadata, cfg *AppConfig) error { instanceDir := filepath.Join(cfg.Storage.InstancesDir, name) dirs := []string{ @@ -29,7 +29,7 @@ func CreateNewInstance(name string, version string, options VsServerConfigOption } instanceConfigPath := filepath.Join(instanceDir, "serverconfig.json") - if err := PrepareInstanceConfig(version, instanceConfigPath, options, cfg); err != nil { + if err := PrepareInstanceConfig(version, instanceConfigPath, meta, cfg); err != nil { return fmt.Errorf("Failed provisioning server baseline configuration: %w", err) } diff --git a/instance_config.go b/instance_config.go index a4d368d..126015b 100644 --- a/instance_config.go +++ b/instance_config.go @@ -10,23 +10,18 @@ import ( "strings" ) -type VsServerConfigOptions struct { - Version string `json:"Version"` - ServerName string `json:"ServerName"` - Port int `json:"Port"` - IpAddress string `json:"IpAddress"` - MaxClients int `json:"MaxClients"` - Password string `json:"Password"` - DefaultRole string `json:"DefaultRole"` - GuestRole string `json:"GuestRole"` - PreApprovedRole string `json:"PreApprovedRole"` +type InstanceMetadata struct { + Version string `json:"Version"` + ServerName string `json:"ServerName"` + Port int `json:"Port"` + Config map[string]interface{} `json:"config"` } -func PrepareInstanceConfig(templateVersion string, instanceConfigPath string, options VsServerConfigOptions, cfg *AppConfig) error { - return SyncInstanceConfig(templateVersion, instanceConfigPath, options, cfg) +func PrepareInstanceConfig(templateVersion string, instanceConfigPath string, meta InstanceMetadata, cfg *AppConfig) error { + return SyncInstanceConfig(templateVersion, instanceConfigPath, meta, cfg) } -func SyncInstanceConfig(templateVersion string, instanceConfigPath string, options VsServerConfigOptions, cfg *AppConfig) error { +func SyncInstanceConfig(templateVersion string, instanceConfigPath string, meta InstanceMetadata, cfg *AppConfig) error { templatePath := filepath.Join(cfg.Storage.ConfigTemplatesDir, templateVersion, "serverconfig.json") if err := ensureTemplateExists(templateVersion, templatePath, cfg); err != nil { @@ -66,16 +61,13 @@ func SyncInstanceConfig(templateVersion string, instanceConfigPath string, optio return fmt.Errorf("failed parsing configuration JSON payload: %w", err) } - rawConfig["ServerName"] = options.ServerName - rawConfig["Port"] = options.Port - rawConfig["MaxClients"] = options.MaxClients - - if options.Password != "" { - rawConfig["Password"] = options.Password - } else { - rawConfig["Password"] = nil + for key, value := range meta.Config { + rawConfig[key] = value } + rawConfig["ServerName"] = meta.ServerName + rawConfig["Port"] = meta.Port + instanceDir := filepath.Dir(instanceConfigPath) if worldConfig, ok := rawConfig["WorldConfig"].(map[string]interface{}); ok { worldConfig["SaveFileLocation"] = filepath.Join(instanceDir, "Saves", "default.vcdbs") diff --git a/process.go b/process.go index d455d21..46645aa 100644 --- a/process.go +++ b/process.go @@ -32,7 +32,7 @@ func NewProcessManager() *ProcessManager { } } -func (pm *ProcessManager) StartInstance(name string, version string, options VsServerConfigOptions, config *AppConfig) error { +func (pm *ProcessManager) StartInstance(name string, version string, meta InstanceMetadata, config *AppConfig) error { pm.Lock() defer pm.Unlock() From 988bdfcf6c9228d33ed02efe61f606db4d6513f8 Mon Sep 17 00:00:00 2001 From: chris bell Date: Sat, 6 Jun 2026 23:42:54 -0500 Subject: [PATCH 2/2] Fixed IPC calls in main to use new ip and port seperation in the config --- config.go | 9 ++++----- daemon.go | 6 ++++-- main.go | 10 ++++++++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/config.go b/config.go index f1b2350..6c28f85 100644 --- a/config.go +++ b/config.go @@ -18,23 +18,23 @@ type AppConfig struct { Daemon struct { ListenAddress string `json:"listen_address"` + Port int `json:"port"` } `json:"daemon"` Instances map[string]InstanceMetadata `json:"instances"` } func CreateConfigWithDefaults(configPath string) *AppConfig { - home, _ := os.UserHomeDir() - basePath := filepath.Join(home, ".local", "share", "vssm") + basePath := filepath.Join(filepath.Dir(configPath), "vssm_data") cfg := &AppConfig{} - cfg.Storage.AppDataDir = basePath cfg.Storage.InstallDir = filepath.Join(basePath, "installs") cfg.Storage.InstancesDir = filepath.Join(basePath, "instances") cfg.Storage.BackupDir = filepath.Join(basePath, "backups") cfg.Storage.ConfigTemplatesDir = filepath.Join(basePath, "config_templates") - cfg.Daemon.ListenAddress = "127.0.0.1:12345" + cfg.Daemon.ListenAddress = "127.0.0.1" + cfg.Daemon.Port = 65000 cfg.Instances = make(map[string]InstanceMetadata) @@ -70,7 +70,6 @@ func LoadOrCreateConfig(configPath string) (*AppConfig, error) { } dirs := []string{ - cfg.Storage.AppDataDir, cfg.Storage.InstallDir, cfg.Storage.InstancesDir, cfg.Storage.BackupDir, diff --git a/daemon.go b/daemon.go index 4e4f789..b8059a5 100644 --- a/daemon.go +++ b/daemon.go @@ -60,8 +60,10 @@ func StartDaemon(cfg *AppConfig, configPath string) error { mux.ServeHTTP(w, r) }) + listenAddress := cfg.Daemon.ListenAddress + ":" + strconv.Itoa(cfg.Daemon.Port) + server := &http.Server{ - Addr: cfg.Daemon.ListenAddress, + Addr: listenAddress, Handler: corsWrappedHandler, } @@ -69,7 +71,7 @@ func StartDaemon(cfg *AppConfig, configPath string) error { signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) go func() { - fmt.Printf("Engine daemon actively listening on http://%s\n", cfg.Daemon.ListenAddress) + fmt.Printf("Engine daemon actively listening on http://%s\n", listenAddress) if err := server.ListenAndServe(); err != http.ErrServerClosed { fmt.Printf("Daemon runtime failure: %v\n", err) } diff --git a/main.go b/main.go index 8b83490..a069b8e 100644 --- a/main.go +++ b/main.go @@ -63,6 +63,9 @@ func main() { log.Fatalf("Initialization failed: %v", err) } + listenAddress := cfg.Daemon.ListenAddress + ":" + strconv.Itoa(cfg.Daemon.Port) + fmt.Printf("Got listen address from config: %s\n", listenAddress) + subCommand := args[0] switch subCommand { @@ -132,7 +135,9 @@ func main() { } func sendIPCRequest(cfg *AppConfig, method string, path string, body io.Reader) { - targetUrl := fmt.Sprintf("http://%s%s", cfg.Daemon.ListenAddress, path) + listenAddress := cfg.Daemon.ListenAddress + ":" + strconv.Itoa(cfg.Daemon.Port) + targetUrl := fmt.Sprintf("http://%s%s", listenAddress, path) + fmt.Printf("Listen Addr: %s; Target URL: %s\n", listenAddress, targetUrl) req, err := http.NewRequest(method, targetUrl, body) if err != nil { log.Fatalf("Failed to construct IPC frame: %v", err) @@ -158,7 +163,8 @@ func sendIPCRequest(cfg *AppConfig, method string, path string, body io.Reader) } func fetchAndPrintStatus(cfg *AppConfig) { - targetUrl := fmt.Sprintf("http://%s/instances/list", cfg.Daemon.ListenAddress) + listenAddress := cfg.Daemon.ListenAddress + ":" + strconv.Itoa(cfg.Daemon.Port) + targetUrl := fmt.Sprintf("http://%s/instances/list", listenAddress) resp, err := http.Get(targetUrl) if err != nil { log.Fatalf("IPC connection failed. Is the vs-manager daemon running? Error: %v", err)