Skip to content

Commit

Permalink
Get logs from servers (local and remote), and linux installs
Browse files Browse the repository at this point in the history
  • Loading branch information
mircearoata committed May 15, 2024
1 parent 86f4e9b commit f1ad846
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 54 deletions.
65 changes: 54 additions & 11 deletions backend/app/debug_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package app

import (
"archive/zip"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"
"strings"
"time"

Expand All @@ -25,6 +27,7 @@ type MetadataInstallation struct {
LaunchPath string `json:"launchPath"`
Name string `json:"name"`
Profile string `json:"profile"`
Log string `json:"log"`
}

type Metadata struct {
Expand All @@ -38,22 +41,60 @@ type Metadata struct {
ModsEnabled bool `json:"modsEnabled"`
}

func addFactoryGameLog(writer *zip.Writer) error {
if runtime.GOOS == "windows" {
cacheDir, err := os.UserCacheDir()
func addFactoryGameLogs(writer *zip.Writer) error {
cacheDir, err := os.UserCacheDir()
if err != nil {
return fmt.Errorf("failed to get user cache dir: %w", err)
}
err = utils.AddFileToZip(writer, filepath.Join(cacheDir, "FactoryGame", "Saved", "Logs", "FactoryGame.log"), "FactoryGame.log")
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to add file to zip: %w", err)
}
}
for _, meta := range ficsitcli.FicsitCLI.GetInstallationsMetadata() {
if meta.Info == nil {
continue
}

logPath := filepath.Join(meta.Info.SavedPath, "Logs", "FactoryGame.log")
d, err := ficsitcli.FicsitCLI.GetInstallation(meta.Info.Path).GetDisk()
if err != nil {
return fmt.Errorf("failed to get user cache dir: %w", err)
slog.Warn("failed to get disk for installation", slog.String("path", meta.Info.Path), slog.Any("error", err))
continue
}
err = utils.AddFileToZip(writer, filepath.Join(cacheDir, "FactoryGame", "Saved", "Logs", "FactoryGame.log"), "FactoryGame.log")
logExists, err := d.Exists(logPath)
if err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("failed to add file to zip: %w", err)
}
slog.Warn("failed to check if log exists", slog.String("path", logPath), slog.Any("error", err))
continue
}
if !logExists {
continue
}
bytes, err := d.Read(logPath)
if err != nil {
slog.Warn("failed to read log file", slog.String("path", logPath), slog.Any("error", err))
continue
}
logFile, err := writer.Create(getLogNameForInstall(meta.Info))
if err != nil {
slog.Warn("failed to create log file in zip", slog.Any("error", err))
continue
}
_, err = logFile.Write(bytes)
if err != nil {
slog.Warn("failed to write log file to zip", slog.Any("error", err))
continue
}
}
return nil
}

func getLogNameForInstall(install *common.Installation) string {
hash := sha256.Sum256([]byte(install.Path))
return fmt.Sprintf("FactoryGame_%s.log", hex.EncodeToString(hash[:])[:8])
}

func addMetadata(writer *zip.Writer) error {
installs := ficsitcli.FicsitCLI.GetInstallations()
selectedInstallInstance := ficsitcli.FicsitCLI.GetSelectedInstall()
Expand All @@ -67,8 +108,9 @@ func addMetadata(writer *zip.Writer) error {
}
i := &MetadataInstallation{
Installation: metadata.Info,
Name: fmt.Sprintf("Satisfactory %s (%s)", metadata.Info.Branch, metadata.Info.Branch),
Name: fmt.Sprintf("Satisfactory %s (%s)", metadata.Info.Branch, metadata.Info.Launcher),
Profile: ficsitcli.FicsitCLI.GetInstallation(install).Profile,
Log: getLogNameForInstall(metadata.Info),
}
i.Path = utils.RedactPath(i.Path)
i.LaunchPath = strings.Join(i.Installation.LaunchPath, " ")
Expand Down Expand Up @@ -143,7 +185,7 @@ func (a *app) generateAndSaveDebugInfo(filename string) error {
writer := zip.NewWriter(file)
defer writer.Close()

err = addFactoryGameLog(writer)
err = addFactoryGameLogs(writer)
if err != nil {
return fmt.Errorf("failed to add FactoryGame.log to debuginfo zip: %w", err)
}
Expand All @@ -153,6 +195,7 @@ func (a *app) generateAndSaveDebugInfo(filename string) error {
return fmt.Errorf("failed to add metadata to debuginfo zip: %w", err)
}

// Add SMM log last, as it may list errors from previous steps
err = utils.AddFileToZip(writer, viper.GetString("log-file"), "SatisfactoryModManager.log")
if err != nil {
return fmt.Errorf("failed to add SatisfactoryModManager.log to debuginfo zip: %w", err)
Expand Down
14 changes: 8 additions & 6 deletions backend/ficsitcli/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"log/slog"
"path"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -161,12 +162,13 @@ func (f *ficsitCLI) getRemoteServerMetadata(installation *cli.Installation) (*co
branch := common.BranchEarlyAccess // TODO: Do we have a way to detect this for remote installs?

return &common.Installation{
Path: installation.Path,
Type: installType,
Location: common.LocationTypeRemote,
Branch: branch,
Version: gameVersion,
Launcher: f.getNextRemoteLauncherName(),
Path: installation.Path,
Type: installType,
Location: common.LocationTypeRemote,
Branch: branch,
Version: gameVersion,
Launcher: f.getNextRemoteLauncherName(),
SavedPath: path.Join(installation.BasePath(), "FactoryGame", "Saved"),
}, nil
}

Expand Down
25 changes: 19 additions & 6 deletions backend/installfinders/common/gameinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type GameVersionFile struct {
BuildID string `json:"BuildId"`
}

func GetGameInfo(path string) (InstallType, int, error) {
func GetGameInfo(path string, platform Platform) (InstallType, int, string, error) {
for _, info := range gameInfo {
executablePath := filepath.Join(path, info.executable)
if _, err := os.Stat(executablePath); os.IsNotExist(err) {
Expand All @@ -65,20 +65,33 @@ func GetGameInfo(path string) (InstallType, int, error) {

versionFilePath := filepath.Join(path, info.versionPath)
if _, err := os.Stat(versionFilePath); os.IsNotExist(err) {
return InstallTypeWindowsClient, 0, fmt.Errorf("failed to get game info")
return InstallTypeWindowsClient, 0, "", fmt.Errorf("failed to get game info")
}

versionFile, err := os.ReadFile(versionFilePath)
if err != nil {
return InstallTypeWindowsClient, 0, fmt.Errorf("failed to read version file %s: %w", versionFilePath, err)
return InstallTypeWindowsClient, 0, "", fmt.Errorf("failed to read version file %s: %w", versionFilePath, err)
}

var versionData GameVersionFile
if err := json.Unmarshal(versionFile, &versionData); err != nil {
return InstallTypeWindowsClient, 0, fmt.Errorf("failed to parse version file %s: %w", versionFilePath, err)
return InstallTypeWindowsClient, 0, "", fmt.Errorf("failed to parse version file %s: %w", versionFilePath, err)
}

return info.installType, versionData.Changelist, nil
return info.installType, versionData.Changelist, getGameSavedDir(path, info.installType, platform), nil
}
return InstallTypeWindowsClient, 0, fmt.Errorf("failed to get game info")
return InstallTypeWindowsClient, 0, "", fmt.Errorf("failed to get game info")
}

func getGameSavedDir(gamePath string, install InstallType, platform Platform) string {
if install == InstallTypeWindowsClient {
cacheDir, err := platform.CacheDir()
if err != nil {
slog.Error("failed to get cache dir", slog.Any("error", err))
return ""
}

return filepath.Join(cacheDir, "FactoryGame", "Saved")
}
return filepath.Join(gamePath, "FactoryGame", "Saved")
}
1 change: 1 addition & 0 deletions backend/installfinders/common/launcherplatform.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package common

type Platform interface {
ProcessPath(path string) string
CacheDir() (string, error)
Os() string
}

Expand Down
5 changes: 5 additions & 0 deletions backend/installfinders/common/launcherplatform_native.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"os"
"runtime"
)

Expand All @@ -14,6 +15,10 @@ func (p nativePlatform) ProcessPath(path string) string {
return path
}

func (p nativePlatform) CacheDir() (string, error) {
return os.UserCacheDir() //nolint:wrapcheck
}

func (p nativePlatform) Os() string {
return runtime.GOOS
}
24 changes: 0 additions & 24 deletions backend/installfinders/common/launcherplatform_unix.go

This file was deleted.

75 changes: 75 additions & 0 deletions backend/installfinders/common/launcherplatform_wine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package common

import (
"errors"
"fmt"
"os"
"os/user"
"path/filepath"
"strings"

"gopkg.in/ini.v1"
)

type winePlatform struct {
winePrefix string
}

func WineLauncherPlatform(winePrefix string) Platform {
return winePlatform{winePrefix: winePrefix}
}

func (p winePlatform) ProcessPath(path string) string {
return filepath.Join(p.winePrefix, "dosdevices", strings.ToLower(path[0:1])+strings.ReplaceAll(path[1:], "\\", "/"))
}

func (p winePlatform) CacheDir() (string, error) {
regCacheDir, err := p.getRegCacheDir()
if err == nil {
return regCacheDir, nil
}
if errors.Is(err, os.ErrNotExist) {
return p.getDefaultCacheDir()
}
return "", err
}

func (p winePlatform) Os() string {
return "windows"
}

func (p winePlatform) getRegCacheDir() (string, error) {
userRegPath := filepath.Join(p.winePrefix, "user.reg")
userRegBytes, err := os.ReadFile(userRegPath)
if err != nil {
return "", fmt.Errorf("failed to read user.reg: %w", err)
}
userRegText := string(userRegBytes)
if strings.HasPrefix(userRegText, "WINE REGISTRY") {
newLineIndex := strings.Index(userRegText, "\n")
userRegText = userRegText[newLineIndex+1:]
}
userReg, err := ini.Load(strings.NewReader(userRegText))
if err != nil {
return "", fmt.Errorf("failed to load user.reg: %w", err)
}
return p.ProcessPath(userReg.Section(`Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders`).Key("Local AppData").String()), nil
}

func (p winePlatform) getDefaultCacheDir() (string, error) {
// Default can be either
// modern: C:\Users\<linux username>\AppData\Local
// legacy: C:\Users\<linux username>\Local Settings\Application Data
currentUser, err := user.Current()
if err != nil {
return "", fmt.Errorf("failed to get current user: %w", err)
}
modernPath := p.ProcessPath(fmt.Sprintf("C:\\Users\\%s\\AppData\\Local", currentUser.Name))
legacyPath := p.ProcessPath(fmt.Sprintf("C:\\Users\\%s\\Local Settings\\Application Data", currentUser.Name))

if _, err := os.Stat(modernPath); err == nil {
return modernPath, nil
}

return legacyPath, nil
}
1 change: 1 addition & 0 deletions backend/installfinders/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Installation struct {
Branch GameBranch `json:"branch"`
Launcher string `json:"launcher"`
LaunchPath []string `json:"launchPath"`
SavedPath string `json:"-"`
}

type InstallFindError struct {
Expand Down
6 changes: 5 additions & 1 deletion backend/installfinders/launchers/epic/epic.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ func FindInstallationsEpic(epicManifestsPath string, launcher string, platform c
continue
}

installType, version, err := common.GetGameInfo(installLocation)
// Epic can only launch games of the same platform
gamePlatform := platform.Platform

installType, version, savedPath, err := common.GetGameInfo(installLocation, gamePlatform)
if err != nil {
findErrors = append(findErrors, common.InstallFindError{
Path: installLocation,
Expand All @@ -148,6 +151,7 @@ func FindInstallationsEpic(epicManifestsPath string, launcher string, platform c
Branch: branch,
Launcher: launcher,
LaunchPath: platform.LauncherCommand(epicManifest.MainGameAppName),
SavedPath: savedPath,
})
}

Expand Down
Loading

0 comments on commit f1ad846

Please sign in to comment.