Adding current project state

This commit is contained in:
2026-06-05 16:44:41 -05:00
commit 073db62fb1
10 changed files with 663 additions and 0 deletions

116
process.go Normal file
View File

@@ -0,0 +1,116 @@
package main
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"sync"
)
type ProcessManager struct {
sync.RWMutex
ActiveInstances map[string]*exec.Cmd
StdinPipes map[string]io.WriteCloser
}
func NewProcessManager() *ProcessManager {
return &ProcessManager{
ActiveInstances: make(map[string]*exec.Cmd),
StdinPipes: make(map[string]io.WriteCloser),
}
}
func (pm *ProcessManager) StartInstance(name string, version string, options VsServerConfigOptions, config *AppConfig) error {
pm.Lock()
defer pm.Unlock()
if _, running := pm.ActiveInstances[name]; running {
return fmt.Errorf("Instance '%s' is already running", name)
}
binaryPath := filepath.Join(config.Storage.InstallDir, version, "VintagestoryServer")
instanceDataPath := filepath.Join(config.Storage.InstancesDir, name)
if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
fmt.Printf("Server version '%s' not installed, attempting to install\n", version)
install_err := DownloadAndExtractServer(version, config.Storage.InstallDir)
if install_err != nil {
return fmt.Errorf("Could not locate or download a server binary for version '%s'", version)
}
}
cmd := exec.Command(binaryPath, "--dataPath", instanceDataPath)
cmd.Env = os.Environ()
cmd.Stderr = os.Stderr
stdinPipe, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("Failed to open stdin pipe: %w", err)
}
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
stdinPipe.Close()
return fmt.Errorf("failed to open stdout conduit: %w", err)
}
if err := cmd.Start(); err != nil {
stdinPipe.Close()
stdoutPipe.Close()
return fmt.Errorf("Failed to start process thread: %w", err)
}
pm.ActiveInstances[name] = cmd
pm.StdinPipes[name] = stdinPipe
go pm.streamLogs(name, stdoutPipe)
go pm.watchProcessExit(name, cmd)
fmt.Printf("Server instance '%s' successfully spawned under PID %d\n", name, cmd.Process.Pid)
return nil
}
func (pm *ProcessManager) streamLogs(name string, stdout io.ReadCloser) {
defer stdout.Close()
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
//stream straight to the manager console terminal,
// but later dispatch payloads to our web UI or a log file
fmt.Printf("[%s]: %s\n", name, scanner.Text())
}
}
func (pm *ProcessManager) watchProcessExit(name string, cmd *exec.Cmd) {
_ = cmd.Wait()
pm.Lock()
defer pm.Unlock()
delete(pm.ActiveInstances, name)
if pipe, exists := pm.StdinPipes[name]; exists {
pipe.Close()
delete(pm.StdinPipes, name)
}
fmt.Printf("Server instance '%s' has shut down or terminated.\n", name)
}
func (pm *ProcessManager) SendCommand(name string, command string) error {
pm.RLock()
pipe, exists := pm.StdinPipes[name]
pm.RUnlock()
if !exists {
return fmt.Errorf("Cannot send command, instance with name '%s' is not running", name)
}
_, err := io.WriteString(pipe, command+"\n")
if err != nil {
return fmt.Errorf("failed writing to server process stdin conduit: %w", err)
}
return nil
}