Use sockets for server creation progress
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -131,11 +132,13 @@ func (s *SteamService) InstallServer(ctx context.Context, installPath string, se
|
||||
}
|
||||
|
||||
if creds != nil && creds.Username != "" {
|
||||
logging.Info("Using Steam credentials for user: %s", creds.Username)
|
||||
steamCMDArgs = append(steamCMDArgs, creds.Username)
|
||||
if creds.Password != "" {
|
||||
steamCMDArgs = append(steamCMDArgs, creds.Password)
|
||||
}
|
||||
} else {
|
||||
logging.Info("Using anonymous Steam login")
|
||||
steamCMDArgs = append(steamCMDArgs, "anonymous")
|
||||
}
|
||||
|
||||
@@ -145,37 +148,196 @@ func (s *SteamService) InstallServer(ctx context.Context, installPath string, se
|
||||
"+quit",
|
||||
)
|
||||
|
||||
// Build PowerShell arguments to execute SteamCMD directly
|
||||
// This matches the format: powershell -nologo -noprofile c:\steamcmd\steamcmd.exe +args...
|
||||
args := []string{"-nologo", "-noprofile"}
|
||||
args = append(args, steamCMDPath)
|
||||
args = append(args, steamCMDArgs...)
|
||||
// Execute SteamCMD directly without PowerShell wrapper to get better output capture
|
||||
args := steamCMDArgs
|
||||
|
||||
// Use interactive executor to handle potential 2FA prompts with timeout
|
||||
logging.Info("Installing ACC server to %s...", absPath)
|
||||
|
||||
logging.Info("SteamCMD command: %s %s", steamCMDPath, strings.Join(args, " "))
|
||||
|
||||
// Create a context with timeout to prevent hanging indefinitely
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Minute)
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Minute) // Increased timeout
|
||||
defer cancel()
|
||||
|
||||
|
||||
// Update the executor to use SteamCMD directly
|
||||
originalExePath := s.interactiveExecutor.ExePath
|
||||
s.interactiveExecutor.ExePath = steamCMDPath
|
||||
defer func() {
|
||||
s.interactiveExecutor.ExePath = originalExePath
|
||||
}()
|
||||
|
||||
if err := s.interactiveExecutor.ExecuteInteractive(timeoutCtx, serverID, args...); err != nil {
|
||||
logging.Error("SteamCMD execution failed: %v", err)
|
||||
if timeoutCtx.Err() == context.DeadlineExceeded {
|
||||
return fmt.Errorf("SteamCMD operation timed out after 10 minutes")
|
||||
return fmt.Errorf("SteamCMD operation timed out after 15 minutes - this usually means Steam Guard confirmation is required")
|
||||
}
|
||||
return fmt.Errorf("failed to run SteamCMD: %v", err)
|
||||
}
|
||||
|
||||
logging.Info("SteamCMD execution completed successfully, proceeding with verification...")
|
||||
|
||||
// Add a delay to allow Steam to properly cleanup
|
||||
logging.Info("Waiting for Steam operations to complete...")
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Verify installation
|
||||
exePath := filepath.Join(absPath, "server", "accServer.exe")
|
||||
logging.Info("Checking for ACC server executable at: %s", exePath)
|
||||
|
||||
if _, err := os.Stat(exePath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("server installation failed: accServer.exe not found in %s", absPath)
|
||||
// Log directory contents to help debug
|
||||
logging.Info("accServer.exe not found, checking directory contents...")
|
||||
if entries, dirErr := os.ReadDir(absPath); dirErr == nil {
|
||||
logging.Info("Contents of %s:", absPath)
|
||||
for _, entry := range entries {
|
||||
logging.Info(" - %s (dir: %v)", entry.Name(), entry.IsDir())
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there's a server subdirectory
|
||||
serverDir := filepath.Join(absPath, "server")
|
||||
if entries, dirErr := os.ReadDir(serverDir); dirErr == nil {
|
||||
logging.Info("Contents of %s:", serverDir)
|
||||
for _, entry := range entries {
|
||||
logging.Info(" - %s (dir: %v)", entry.Name(), entry.IsDir())
|
||||
}
|
||||
} else {
|
||||
logging.Info("Server directory %s does not exist or cannot be read: %v", serverDir, dirErr)
|
||||
}
|
||||
|
||||
return fmt.Errorf("server installation failed: accServer.exe not found in %s", exePath)
|
||||
}
|
||||
|
||||
logging.Info("Server installation completed successfully")
|
||||
logging.Info("Server installation completed successfully - accServer.exe found at %s", exePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallServerWithWebSocket installs a server with WebSocket output streaming
|
||||
func (s *SteamService) InstallServerWithWebSocket(ctx context.Context, installPath string, serverID *uuid.UUID, wsService *WebSocketService) error {
|
||||
if err := s.ensureSteamCMD(ctx); err != nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Error ensuring SteamCMD: %v", err), true)
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate installation path for security
|
||||
if err := s.pathValidator.ValidateInstallPath(installPath); err != nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Invalid installation path: %v", err), true)
|
||||
return fmt.Errorf("invalid installation path: %v", err)
|
||||
}
|
||||
|
||||
// Convert to absolute path and ensure proper Windows path format
|
||||
absPath, err := filepath.Abs(installPath)
|
||||
if err != nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Failed to get absolute path: %v", err), true)
|
||||
return fmt.Errorf("failed to get absolute path: %v", err)
|
||||
}
|
||||
absPath = filepath.Clean(absPath)
|
||||
|
||||
// Ensure install path exists
|
||||
if err := os.MkdirAll(absPath, 0755); err != nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Failed to create install directory: %v", err), true)
|
||||
return fmt.Errorf("failed to create install directory: %v", err)
|
||||
}
|
||||
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Installation directory prepared: %s", absPath), false)
|
||||
|
||||
// Get Steam credentials
|
||||
creds, err := s.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Failed to get Steam credentials: %v", err), true)
|
||||
return fmt.Errorf("failed to get Steam credentials: %v", err)
|
||||
}
|
||||
|
||||
// Get SteamCMD path from environment variable
|
||||
steamCMDPath := env.GetSteamCMDPath()
|
||||
|
||||
// Build SteamCMD command arguments
|
||||
steamCMDArgs := []string{
|
||||
"+force_install_dir", absPath,
|
||||
"+login",
|
||||
}
|
||||
|
||||
if creds != nil && creds.Username != "" {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Using Steam credentials for user: %s", creds.Username), false)
|
||||
steamCMDArgs = append(steamCMDArgs, creds.Username)
|
||||
if creds.Password != "" {
|
||||
steamCMDArgs = append(steamCMDArgs, creds.Password)
|
||||
}
|
||||
} else {
|
||||
wsService.BroadcastSteamOutput(*serverID, "Using anonymous Steam login", false)
|
||||
steamCMDArgs = append(steamCMDArgs, "anonymous")
|
||||
}
|
||||
|
||||
steamCMDArgs = append(steamCMDArgs,
|
||||
"+app_update", ACCServerAppID,
|
||||
"validate",
|
||||
"+quit",
|
||||
)
|
||||
|
||||
// Execute SteamCMD with WebSocket output streaming
|
||||
args := steamCMDArgs
|
||||
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Starting SteamCMD: %s %s", steamCMDPath, strings.Join(args, " ")), false)
|
||||
|
||||
// Create a context with timeout to prevent hanging indefinitely
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Update the executor to use SteamCMD directly
|
||||
originalExePath := s.interactiveExecutor.ExePath
|
||||
s.interactiveExecutor.ExePath = steamCMDPath
|
||||
defer func() {
|
||||
s.interactiveExecutor.ExePath = originalExePath
|
||||
}()
|
||||
|
||||
// Create a modified interactive executor that streams output to WebSocket
|
||||
wsInteractiveExecutor := command.NewInteractiveCommandExecutorWithWebSocket(s.executor, s.tfaManager, wsService, *serverID)
|
||||
wsInteractiveExecutor.ExePath = steamCMDPath
|
||||
|
||||
if err := wsInteractiveExecutor.ExecuteInteractive(timeoutCtx, serverID, args...); err != nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("SteamCMD execution failed: %v", err), true)
|
||||
if timeoutCtx.Err() == context.DeadlineExceeded {
|
||||
return fmt.Errorf("SteamCMD operation timed out after 15 minutes - this usually means Steam Guard confirmation is required")
|
||||
}
|
||||
return fmt.Errorf("failed to run SteamCMD: %v", err)
|
||||
}
|
||||
|
||||
wsService.BroadcastSteamOutput(*serverID, "SteamCMD execution completed successfully, proceeding with verification...", false)
|
||||
|
||||
// Add a delay to allow Steam to properly cleanup
|
||||
wsService.BroadcastSteamOutput(*serverID, "Waiting for Steam operations to complete...", false)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Verify installation
|
||||
exePath := filepath.Join(absPath, "server", "accServer.exe")
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Checking for ACC server executable at: %s", exePath), false)
|
||||
|
||||
if _, err := os.Stat(exePath); os.IsNotExist(err) {
|
||||
wsService.BroadcastSteamOutput(*serverID, "accServer.exe not found, checking directory contents...", false)
|
||||
|
||||
if entries, dirErr := os.ReadDir(absPath); dirErr == nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Contents of %s:", absPath), false)
|
||||
for _, entry := range entries {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf(" - %s (dir: %v)", entry.Name(), entry.IsDir()), false)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there's a server subdirectory
|
||||
serverDir := filepath.Join(absPath, "server")
|
||||
if entries, dirErr := os.ReadDir(serverDir); dirErr == nil {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Contents of %s:", serverDir), false)
|
||||
for _, entry := range entries {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf(" - %s (dir: %v)", entry.Name(), entry.IsDir()), false)
|
||||
}
|
||||
} else {
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Server directory %s does not exist or cannot be read: %v", serverDir, dirErr), true)
|
||||
}
|
||||
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Server installation failed: accServer.exe not found in %s", exePath), true)
|
||||
return fmt.Errorf("server installation failed: accServer.exe not found in %s", exePath)
|
||||
}
|
||||
|
||||
wsService.BroadcastSteamOutput(*serverID, fmt.Sprintf("Server installation completed successfully - accServer.exe found at %s", exePath), false)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user