Files
vssm/downloader.go

126 lines
3.5 KiB
Go

package main
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
)
func DownloadAndExtractServer(version string, installBaseDir string) error {
targetDir := filepath.Join(installBaseDir, version)
if _, err := os.Stat(filepath.Join(targetDir, "VintagestoryServer")); err == nil {
fmt.Printf("[Downloader] Server version %s is already installed\n", version)
return nil
}
url := fmt.Sprintf("https://cdn.vintagestory.at/gamefiles/stable/vs_server_linux-x64_%s.tar.gz", version)
fmt.Printf("Establishing connection to '%s'\n", url)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("Error connecting to '%s', %w", url, err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return fmt.Errorf("Version %s not found on the stable CDN branch (404)", version)
} else if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Unexpected CDN response status: %s", resp.Status)
}
fmt.Printf("Downloading and extracting archive to '%s'\n", targetDir)
if err := os.MkdirAll(targetDir, 0755); err != nil {
return fmt.Errorf("Failed to create version installation directory: %w", err)
}
gzipReader, err := gzip.NewReader(resp.Body)
if err != nil {
return fmt.Errorf("Failed to initialize gzip decompressor: %w", err)
}
defer gzipReader.Close()
tarReader := tar.NewReader(gzipReader)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return fmt.Errorf("Error reading TAR stream: %w", err)
}
targetFilePath := filepath.Join(targetDir, header.Name)
switch header.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(targetFilePath, 0755); err != nil {
return fmt.Errorf("failed creating archive directory entry: %w", err)
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(targetFilePath), 0755); err != nil {
return fmt.Errorf("failed creating parent path for file: %w", err)
}
outFile, err := os.OpenFile(targetFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, header.FileInfo().Mode())
if err != nil {
return fmt.Errorf("failed creating target system file: %w", err)
}
if _, err := io.Copy(outFile, tarReader); err != nil {
outFile.Close()
return fmt.Errorf("failed streaming file payload extraction: %w", err)
}
outFile.Close()
}
}
binaryExecutor := filepath.Join(targetDir, "VintagestoryServer")
if err := patchBinaryForNixos(binaryExecutor); err != nil {
return fmt.Errorf("failed fixing platform compatibility gates: %w", err)
}
fmt.Printf("Server version %s successfully installed\n", version)
return nil
}
func patchBinaryForNixos(binaryPath string) error {
if _, err := os.Stat("/etc/NIXOS"); os.IsNotExist(err) {
return nil
}
fmt.Println("[NixOS Detected] Patching server interpreter pathway...")
dotnetRoot := os.Getenv("DOTNET_ROOT")
var interpreter string
if dotnetRoot != "" {
// Use the glibc version that matches the active .NET runtime exactly
// Finds the underlying ld-linux-x86-64.so.2 link within the .NET store closure
out, err := exec.Command("patchelf", "--print-interpreter", filepath.Join(dotnetRoot, "dotnet")).Output()
if err == nil && len(out) > 0 {
interpreter = strings.TrimSpace(string(out))
}
}
if interpreter == "" {
interpreter = "/lib/ld-linux-x86-64.so.2"
}
cmd := exec.Command("patchelf", "--set-interpreter", interpreter, binaryPath)
if err := cmd.Run(); err != nil {
return fmt.Errorf("patchelf failed to link binary: %w", err)
}
return nil
}