Files
Nitrox/NitroxModel/Helper/NitroxUser.cs
2025-07-06 00:23:46 +02:00

263 lines
8.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using NitroxModel.Discovery;
using NitroxModel.Discovery.InstallationFinders.Core;
using NitroxModel.Platforms.OS.Shared;
using NitroxModel.Platforms.Store;
using NitroxModel.Platforms.Store.Interfaces;
namespace NitroxModel.Helper;
public static class NitroxUser
{
public const string LAUNCHER_PATH_ENV_KEY = "NITROX_LAUNCHER_PATH";
private const string PREFERRED_GAMEPATH_KEY = "PreferredGamePath";
private static string appDataPath;
private static string launcherPath;
private static string gamePath;
private static string executableRootPath;
private static string executablePath;
private static string assetsPath;
private static readonly IEnumerable<Func<string>> launcherPathDataSources = new List<Func<string>>
{
() => Environment.GetEnvironmentVariable(LAUNCHER_PATH_ENV_KEY),
() =>
{
Assembly currentAsm = Assembly.GetEntryAssembly();
if (currentAsm?.GetName().Name.Equals("Nitrox.Launcher") ?? false)
{
return Path.GetDirectoryName(currentAsm.Location);
}
Assembly execAsm = Assembly.GetExecutingAssembly();
string execDir = string.IsNullOrEmpty(execAsm.Location) ? Directory.GetCurrentDirectory() : execAsm.Location;
DirectoryInfo execParentDir = Directory.GetParent(execDir);
// When running tests LanguageFiles is in same directory
if (execParentDir != null && Directory.Exists(Path.Combine(execParentDir.FullName, "LanguageFiles")))
{
return execParentDir.FullName;
}
// NitroxModel, NitroxServer and other assemblies are stored in Nitrox.Launcher/lib
if (execParentDir?.Parent != null && Directory.Exists(Path.Combine(execParentDir.Parent.FullName, "Resources", "LanguageFiles")))
{
return execParentDir.Parent.FullName;
}
return null;
},
() =>
{
using ProcessEx proc = ProcessEx.GetFirstProcess("Nitrox.Launcher");
string executable = proc?.MainModule?.FileName;
return !string.IsNullOrWhiteSpace(executable) ? Path.GetDirectoryName(executable) : null;
}
};
public static string AppDataPath
{
get
{
if (appDataPath != null)
{
return appDataPath;
}
string applicationData = null;
// On linux Environment.SpecialFolder.ApplicationData returns the Windows version inside wine, this bypasses that behaviour
string homeInWineEnv = Environment.GetEnvironmentVariable("WINEHOMEDIR");
if (homeInWineEnv is { Length: > 4 })
{
string homeInWine = homeInWineEnv[4..]; // WINEHOMEDIR is prefixed with \??\
if (Directory.Exists(homeInWine))
{
applicationData = Path.Combine(homeInWine, ".config");
Directory.CreateDirectory(applicationData); // Create it if it's not there (which should not happen in normal setups)
}
}
if (!Directory.Exists(applicationData))
{
applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
}
return appDataPath = Path.Combine(applicationData, "Nitrox");
}
}
/// <summary>
/// Tries to get the launcher path that was previously saved by other Nitrox code.
/// </summary>
public static string LauncherPath
{
get
{
if (launcherPath != null)
{
return launcherPath;
}
foreach (Func<string> retriever in launcherPathDataSources)
{
string path = retriever();
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path))
{
return launcherPath = path;
}
}
return null;
}
}
public static string AssetBundlePath => Path.Combine(LauncherPath, "Resources", "AssetBundles");
public static string LanguageFilesPath => Path.Combine(LauncherPath, "Resources", "LanguageFiles");
public static string PreferredGamePath
{
get => KeyValueStore.Instance.GetValue<string>(PREFERRED_GAMEPATH_KEY);
set => KeyValueStore.Instance.SetValue(PREFERRED_GAMEPATH_KEY, value);
}
private static IGamePlatform gamePlatform;
public static event Action GamePlatformChanged;
public static IGamePlatform GamePlatform
{
get
{
if (gamePlatform == null)
{
_ = GamePath; // Ensure gamePath is set
}
return gamePlatform;
}
set
{
if (gamePlatform != value)
{
gamePlatform = value;
GamePlatformChanged?.Invoke();
}
}
}
public static string GamePath
{
get
{
if (!string.IsNullOrEmpty(gamePath))
{
return gamePath;
}
List<GameFinderResult> finderResults = GameInstallationFinder.Instance.FindGame(GameInfo.Subnautica).TakeUntilInclusive(r => r is { IsOk: false }).ToList();
GameFinderResult potentiallyValidResult = finderResults.LastOrDefault();
if (potentiallyValidResult?.IsOk == true)
{
Log.Debug($"Game installation was found by {potentiallyValidResult.FinderName} at '{potentiallyValidResult.Path}'");
gamePath = potentiallyValidResult.Path;
GamePlatform = GamePlatforms.GetPlatformByGameDir(gamePath);
return gamePath;
}
Log.Error($"Could not locate Subnautica installation directory: {Environment.NewLine}{string.Join(Environment.NewLine, finderResults.Select(i => $"{i.FinderName}: {i.ErrorMessage}"))}");
return string.Empty;
}
set
{
if (string.IsNullOrWhiteSpace(value))
{
return;
}
if (!Directory.Exists(value))
{
throw new ArgumentException("Given path is an invalid directory");
}
// Ensures the path looks alright (no mixed / and \ path separators)
gamePath = Path.GetFullPath(value);
GamePlatform = GamePlatforms.GetPlatformByGameDir(gamePath);
}
}
public static string ExecutableRootPath
{
get
{
if (!string.IsNullOrWhiteSpace(executableRootPath))
{
return executableRootPath;
}
string exePath = ExecutableFilePath;
if (exePath == null)
{
return null;
}
return executableRootPath = Path.GetDirectoryName(exePath);
}
}
public static string ExecutableFilePath
{
get
{
if (!string.IsNullOrWhiteSpace(executablePath))
{
return executablePath;
}
Assembly entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly == null)
{
return null;
}
string path = entryAssembly.Location;
// File URI works different on Linux/OSX, so only do uri parsing on Windows.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
path = new Uri(path).LocalPath;
}
return executablePath = path;
}
}
public static string AssetsPath
{
get
{
if (!string.IsNullOrWhiteSpace(assetsPath))
{
return assetsPath;
}
string nitroxAssets;
if (NitroxEnvironment.IsTesting)
{
nitroxAssets = Directory.GetCurrentDirectory();
while (nitroxAssets != null && Path.GetFileName(nitroxAssets) != "Nitrox.Test")
{
nitroxAssets = Directory.GetParent(nitroxAssets)?.FullName;
}
if (nitroxAssets != null)
{
nitroxAssets = Path.Combine(Directory.GetParent(nitroxAssets)?.FullName ?? throw new Exception("Failed to get Nitrox assets during tests"), "Nitrox.Assets.Subnautica");
}
}
else
{
nitroxAssets = LauncherPath ?? ExecutableRootPath;
}
return assetsPath = nitroxAssets;
}
}
}