Skip to content

Commit 55afb6d

Browse files
authored
Added SteamOS support for starting servers (SubnauticaNitrox#2721)
1 parent 97f9974 commit 55afb6d

6 files changed

Lines changed: 94 additions & 18 deletions

File tree

Nitrox.Launcher/GlobalStatic.cs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
using System;
22
using System.IO;
3+
using System.Threading;
34
using Avalonia.Controls;
45
using Nitrox.Model.Platforms.OS.Shared;
56

67
namespace Nitrox.Launcher;
78

89
internal static class GlobalStatic
910
{
11+
private const string SERVER_FILE_NAME_NO_EXT = "Nitrox.Server.Subnautica";
12+
private static long isSteamOs = -1;
13+
1014
public static bool IsDesignMode => Design.IsDesignMode;
1115

1216
/// <inheritdoc cref="ProcessEx.OpenUri" />
@@ -44,13 +48,51 @@ public static bool TryDeleteDirectory(string directory, bool recursive = false)
4448
return false;
4549
}
4650

47-
public static string GetServerExeName()
51+
public static string GetServerFileName()
4852
{
49-
string serverExeName = "Nitrox.Server.Subnautica.exe";
50-
if (!OperatingSystem.IsWindows())
53+
if (OperatingSystem.IsWindows())
54+
{
55+
return $"{SERVER_FILE_NAME_NO_EXT}.exe";
56+
}
57+
58+
return SERVER_FILE_NAME_NO_EXT;
59+
}
60+
61+
public static string GetServerProcessName()
62+
{
63+
// On Steam Deck, server process name is "dotnet" as it's started through command line.
64+
if (IsSteamOs())
65+
{
66+
return "dotnet";
67+
}
68+
69+
return GetServerFileName();
70+
}
71+
72+
public static bool IsSteamOs()
73+
{
74+
if (!OperatingSystem.IsLinux())
75+
{
76+
return false;
77+
}
78+
switch (Interlocked.Read(ref isSteamOs))
79+
{
80+
case 1:
81+
return true;
82+
case 0:
83+
return false;
84+
}
85+
86+
try
87+
{
88+
string osReleaseInfo = File.ReadAllText("/etc/os-release");
89+
bool result = osReleaseInfo.Contains(@"NAME=""SteamOS""", StringComparison.Ordinal);
90+
Interlocked.Exchange(ref isSteamOs, result ? 1 : 0);
91+
return result;
92+
}
93+
catch (IOException)
5194
{
52-
serverExeName = "Nitrox.Server.Subnautica";
95+
return false;
5396
}
54-
return serverExeName;
5597
}
5698
}

Nitrox.Launcher/Models/Design/ServerEntry.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ public async Task StopAsync()
320320
using CancellationTokenSource waitProcessExitCts = new(TimeSpan.FromSeconds(20));
321321
try
322322
{
323-
while (ProcessEx.ProcessExists(GetServerExeName(), ex => ex.Id == LastProcessId))
323+
while (ProcessEx.ProcessExists(GetServerProcessName(), ex => ex.Id == LastProcessId))
324324
{
325325
await Task.Delay(200, waitProcessExitCts.Token);
326326
}
@@ -364,7 +364,7 @@ internal async Task ResetCtsAsync(bool cancelPreexisting = false)
364364
await Dispatcher.UIThread.InvokeAsync(() => IsServerClosing = true);
365365
await Dispatcher.UIThread.InvokeAsync(async () =>
366366
{
367-
while (ProcessEx.ProcessExists(GetServerExeName(), ex => ex.Id == LastProcessId))
367+
while (ProcessEx.ProcessExists(GetServerProcessName(), ex => ex.Id == LastProcessId))
368368
{
369369
try
370370
{
@@ -411,7 +411,7 @@ private ServerProcess(string saveDir, CancellationTokenSource cts, bool isEmbedd
411411
throw new Exception($"{nameof(launcherPath)} must be set");
412412
}
413413

414-
string serverFile = Path.Combine(launcherPath, GetServerExeName());
414+
string serverFile = Path.Combine(launcherPath, GetServerFileName());
415415
ProcessStartInfo startInfo = new(serverFile)
416416
{
417417
WorkingDirectory = launcherPath,
@@ -425,6 +425,17 @@ private ServerProcess(string saveDir, CancellationTokenSource cts, bool isEmbedd
425425
WindowStyle = isEmbeddedMode ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal,
426426
CreateNoWindow = isEmbeddedMode
427427
};
428+
// On Steam Deck, start through user-wide .dotnet. The default is system-wide which might not work.
429+
if (IsSteamOs())
430+
{
431+
string dotnetExecutable = Path.Combine(NitroxUser.HomePath, ".dotnet", "dotnet");
432+
if (!File.Exists(dotnetExecutable))
433+
{
434+
throw new FileNotFoundException("A compatible .NET version must be installed by the user to run the server on SteamOS. Please install dotnet to your user home: ~/.dotnet/");
435+
}
436+
startInfo.FileName = dotnetExecutable;
437+
startInfo.ArgumentList.Insert(0, $"{serverFile}.dll");
438+
}
428439
// Assist server with finding launcher location.
429440
if (Directory.Exists(launcherPath))
430441
{

Nitrox.Launcher/Models/Services/DialogService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public Task ShowErrorAsync(Exception exception, string? title = null, string? de
4949
ShowAsync<DialogBoxViewModel>(model =>
5050
{
5151
model.Title = title ?? "Error";
52-
model.Description = string.IsNullOrWhiteSpace(description) ? exception.ToString() : $"{description}{Environment.NewLine}{exception}";
52+
model.Description = string.IsNullOrWhiteSpace(description) ? exception.ToString() : $"[b][i]{description}[/b][/i]{Environment.NewLine}{Environment.NewLine}[#f94239]{exception}";
5353
model.ButtonOptions = ButtonOptions.OkClipboard;
5454
});
5555

Nitrox.Launcher/Models/Services/ServerService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public async Task<bool> StartServerAsync(ServerEntry server)
116116
catch (Exception ex)
117117
{
118118
Log.Error(ex, $"Error while starting server \"{server.Name}\"");
119-
await Dispatcher.UIThread.InvokeAsync(async () => await dialogService.ShowErrorAsync(ex, $"Error while starting server \"{server.Name}\""));
119+
await Dispatcher.UIThread.InvokeAsync(async () => await dialogService.ShowErrorAsync(ex, $"Error while starting server \"{server.Name}\"", ex.GetFirstNonAggregateMessage()));
120120
return false;
121121
}
122122
}

Nitrox.Launcher/Views/DialogBoxModal.axaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
Grid.Row="1"
3939
HorizontalScrollBarVisibility="Disabled"
4040
Margin="24,20,10,20">
41-
<SelectableTextBlock
41+
<controls:SelectableRichTextBlock
4242
FontSize="{Binding DescriptionFontSize}"
4343
FontWeight="{Binding DescriptionFontWeight}"
4444
IsVisible="{Binding Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"

Nitrox.Model/Helper/NitroxUser.cs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ public static class NitroxUser
5656
}
5757
};
5858

59-
[field: MaybeNull, AllowNull]
6059
public static string AppDataPath
6160
{
6261
get
@@ -140,12 +139,6 @@ public static string PreferredGamePath
140139

141140
public static string GamePath => string.IsNullOrEmpty(gamePath) ? string.Empty : gamePath;
142141

143-
public static void SetGamePathAndPlatform(string path, IGamePlatform? platform)
144-
{
145-
gamePath = Path.GetFullPath(path);
146-
GamePlatform = platform ?? GamePlatforms.GetPlatformByGameDir(path);
147-
}
148-
149142
public static string ExecutableRootPath
150143
{
151144
get
@@ -221,4 +214,34 @@ public static string? AssetsPath
221214
return field = nitroxAssets;
222215
}
223216
}
217+
218+
/// <summary>
219+
/// Gets the user home directory of the current operating system user.
220+
/// </summary>
221+
public static string HomePath
222+
{
223+
get
224+
{
225+
string homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
226+
if (string.IsNullOrWhiteSpace(homePath))
227+
{
228+
homePath = Environment.GetEnvironmentVariable("HOME");
229+
}
230+
if (!Directory.Exists(homePath))
231+
{
232+
throw new DirectoryNotFoundException("User home directory does not exist or is inaccessible");
233+
}
234+
if (string.IsNullOrWhiteSpace(homePath))
235+
{
236+
throw new InvalidOperationException("User home directory is not given by the operating system");
237+
}
238+
return homePath;
239+
}
240+
}
241+
242+
public static void SetGamePathAndPlatform(string path, IGamePlatform? platform)
243+
{
244+
gamePath = Path.GetFullPath(path);
245+
GamePlatform = platform ?? GamePlatforms.GetPlatformByGameDir(path);
246+
}
224247
}

0 commit comments

Comments
 (0)