Make singleplayer actually use the launcher's client and configuration.

This commit is contained in:
ceikry 2024-03-27 23:36:09 -05:00
parent 3d51cbf87e
commit 90b8027be1
7 changed files with 112 additions and 94 deletions

View file

@ -2,7 +2,4 @@
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">/usr/share/dotnet/sdk/7.0.100/MSBuild.dll</s:String>
<s:String x:Key="/Default/Environment/Hierarchy/Build/SolBuilderDuo/UseMsbuildSolutionBuilder/@EntryValue">No</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=7a951a1e_002Dc4b2_002D4516_002Db0cc_002D72fd4df57b19/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from FindMostRecentBackupDirectory.cs" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;Project Location="\home\xacket\Projects\Saradomin-Launcher\UnitTests" Presentation="&amp;lt;UnitTests&amp;gt;" /&gt;&#xD;
&lt;/SessionState&gt;</s:String>
</wpf:ResourceDictionary>

View file

@ -0,0 +1,8 @@
using Glitonea.Mvvm.Messaging;
namespace Saradomin.Infrastructure.Messaging;
public class ClientClosedMessage : Message
{
}

View file

@ -0,0 +1,8 @@
using Glitonea.Mvvm.Messaging;
namespace Saradomin.Infrastructure.Messaging;
public class ClientLaunchRequestedMessage : Message
{
}

View file

@ -355,5 +355,41 @@ namespace Saradomin.Utilities
return false;
}
}
public static Process StartJavaProcess(string javaExecutable, string jarPath, string memoryAllocation, Action<string> outputHandler, Action onExit)
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = javaExecutable,
Arguments = $"-Xmx{memoryAllocation} -Xms{memoryAllocation} -jar \"{jarPath}\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = Path.Combine(CrossPlatform.GetSingleplayerHome(), "game")
};
process.OutputDataReceived += (_, args) =>
{
if (string.IsNullOrEmpty(args.Data)) return;
outputHandler?.Invoke(args.Data);
};
process.ErrorDataReceived += (_, args) =>
{
if (string.IsNullOrEmpty(args.Data)) return;
outputHandler?.Invoke(args.Data);
};
process.EnableRaisingEvents = true;
process.Exited += (_, _) => onExit?.Invoke();
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return process;
}
}
}

View file

@ -1,77 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
namespace Saradomin.Utilities.Singleplayer;
public static class Windows
{
private static bool IsPortInUse(int port)
{
var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
var tcpConnInfoArray = ipGlobalProperties.GetActiveTcpListeners();
return tcpConnInfoArray.Any(endpoint => endpoint.Port == port);
}
private static Process StartJavaProcess(string javaExecutable, string jarPath, string memoryAllocation, Action<string> outputHandler, Action onExit)
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = javaExecutable,
Arguments = $"-Xmx{memoryAllocation} -Xms{memoryAllocation} -jar \"{jarPath}\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = Path.Combine(CrossPlatform.GetSingleplayerHome(), "game")
};
process.OutputDataReceived += (_, args) =>
{
if (string.IsNullOrEmpty(args.Data)) return;
outputHandler?.Invoke(args.Data);
};
process.ErrorDataReceived += (_, args) =>
{
if (string.IsNullOrEmpty(args.Data)) return;
outputHandler?.Invoke(args.Data);
};
process.EnableRaisingEvents = true;
process.Exited += (_, _) => onExit?.Invoke();
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return process;
}
public static void WindowsLaunchServerAndClient(string javaExecutableLocation, Action<string> log)
{
string serverJar = CrossPlatform.GetSingleplayerHome() + @"\game\server.jar";
string clientJar = CrossPlatform.GetSingleplayerHome() + @"\game\client.jar";
if (IsPortInUse(43595))
{
log("Port 43595 is in use. Cannot start the server again.");
return;
}
Process serverProcess = StartJavaProcess(javaExecutableLocation, serverJar, "2G", log, null);
while (!IsPortInUse(43595)) Thread.Sleep(1000);
StartJavaProcess(javaExecutableLocation, clientJar, "1G", null, () =>
{
serverProcess.Kill();
});
}
}

View file

@ -14,6 +14,8 @@ using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using Glitonea.Mvvm;
using Glitonea.Mvvm.Messaging;
using Saradomin.Infrastructure.Messaging;
using Saradomin.Infrastructure.Services;
using Saradomin.Model.Settings.Launcher;
using Saradomin.Utilities;
@ -35,6 +37,9 @@ public class SingleplayerViewModel : ViewModelBase
public bool ShowLogPanel { get; private set; }
public LauncherSettings Launcher => _settingsService.Launcher;
private string _oldServerAddress, _oldManagementAddress;
private Process _serverProcess;
public SingleplayerViewModel(ISettingsService settingsService,
IJavaUpdateService javaUpdateService,
ISingleplayerUpdateService iSingleplayerUpdateService)
@ -55,6 +60,18 @@ public class SingleplayerViewModel : ViewModelBase
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
};
Message.Subscribe<ClientClosedMessage>(this, OnClientClosed);
}
private void OnClientClosed(ClientClosedMessage _)
{
if (_serverProcess == null) return;
_serverProcess.Kill();
_serverProcess = null;
CanLaunch = true;
_settingsService.Client.GameServerAddress = _oldServerAddress;
_settingsService.Client.ManagementServerAddress = _oldManagementAddress;
}
private void OnSingleplayerDownloadProgressChanged(object sender, Tuple<float, bool> e)
@ -133,21 +150,36 @@ public class SingleplayerViewModel : ViewModelBase
}
CanLaunch = false;
PrintLog("Starting Singleplayer.. The Singleplayer client will launch when 2009scape is ready. Sit tight!");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
new Task(() => Utilities.Singleplayer.Windows.WindowsLaunchServerAndClient(Launcher.JavaExecutableLocation, PrintLog)).Start();
}
else
{
new Task(() =>
CrossPlatform.RunCommandAndGetOutput(
$"{CrossPlatform.LocateSingleplayerExecutable()} \"{Launcher.JavaExecutableLocation}\"",
PrintLog,
PrintLog)
).Start();
}
_oldServerAddress = _settingsService.Client.GameServerAddress;
_oldManagementAddress = _settingsService.Client.ManagementServerAddress;
_settingsService.Client.GameServerAddress = _settingsService.Client.ManagementServerAddress = "localhost";
new Task(() => LaunchServerAndClient(Launcher.JavaExecutableLocation, PrintLog)).Start();
}
private void LaunchServerAndClient (string javaExecutableLocation, Action<string> log)
{
var serverJar = CrossPlatform.GetSingleplayerHome() + @"/game/server.jar";
if (IsPortInUse(43595))
{
log("Port 43595 is in use. Cannot start the server again.");
return;
}
_serverProcess = CrossPlatform.StartJavaProcess(javaExecutableLocation, serverJar, "2G", log, null);
while (!IsPortInUse(43595)) Thread.Sleep(1000);
Message.Broadcast<ClientLaunchRequestedMessage>();
}
private static bool IsPortInUse(int port)
{
var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
var tcpConnInfoArray = ipGlobalProperties.GetActiveTcpListeners();
return tcpConnInfoArray.Any(endpoint => endpoint.Port == port);
}
private void LaunchFaq()
{
CrossPlatform.LaunchURL("https://2009scape.org/site/game_guide/singleplayer.html");

View file

@ -58,6 +58,7 @@ namespace Saradomin.ViewModel.Windows
Message.Subscribe<MainViewLoadedMessage>(this, MainViewLoaded);
Message.Subscribe<NotificationBoxStateChangedMessage>(this, NotificatationBoxStateChanged);
Message.Subscribe<ClientLaunchRequestedMessage>(this, ClientLaunchRequested);
_settingsService.Launcher.JavaExecutableLocation ??= CrossPlatform.LocateJavaExecutable();
}
@ -67,6 +68,12 @@ namespace Saradomin.ViewModel.Windows
Environment.Exit(0);
}
public async void ClientLaunchRequested(ClientLaunchRequestedMessage _)
{
if (CanLaunch)
await ExecuteLaunchSequence();
}
public async void MainViewLoaded(MainViewLoadedMessage _)
{
using (var httpClient = new HttpClient())
@ -117,7 +124,13 @@ namespace Saradomin.ViewModel.Windows
public bool CanExecuteLaunchSequence(object param)
=> CanLaunch;
//Stub to maintain compatibility with AXAML
public async Task ExecuteLaunchSequence()
{
await ExecuteLaunchSequence(false);
}
private async Task ExecuteLaunchSequence(bool forceWait)
{
CanLaunch = false;
@ -159,7 +172,7 @@ namespace Saradomin.ViewModel.Windows
// Will block this task until client process exits.
var t = _launchService.LaunchClient();
if (!_settingsService.Launcher.AllowMultiboxing)
if (!_settingsService.Launcher.AllowMultiboxing || forceWait)
await t;
}
}
@ -174,6 +187,7 @@ namespace Saradomin.ViewModel.Windows
{
CanLaunch = true;
LaunchText = "Play!";
Message.Broadcast<ClientClosedMessage>();
}
}