Make Singleplayer work on Windows via a completely different method

This commit is contained in:
dginovker 2023-11-05 12:55:33 -05:00
parent 3795e951f1
commit 61053cc1f1
7 changed files with 104 additions and 40 deletions

View file

@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Saradomin/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Singleplayer/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -2,7 +2,7 @@
<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;
&lt;Project Location="/home/xacket/Projects/Saradomin-Launcher/UnitTests" Presentation="&amp;lt;UnitTests&amp;gt;" /&gt;
<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

@ -1,3 +1,4 @@
using System;
using System.IO;
using System.Text.Json;
using Saradomin.Model.Settings.Client;

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
namespace Saradomin.Utilities;
@ -22,15 +23,11 @@ public static class SingleplayerManagement
public static void MakeBackup(Action<string> log)
{
if (!Directory.Exists(CrossPlatform.GetSingleplayerBackupsHome()))
Directory.CreateDirectory(CrossPlatform.GetSingleplayerBackupsHome());
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string newBackupDir = Path.Combine(
CrossPlatform.GetSingleplayerBackupsHome(),
timestamp
);
Directory.CreateDirectory(newBackupDir);
foreach (string dir in DirsToBackup)
{

View file

@ -0,0 +1,77 @@
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

@ -1,8 +1,13 @@
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
@ -73,7 +78,7 @@ public class SingleplayerViewModel : ViewModelBase
public void DownloadSingleplayer()
{
if (!CanLaunch) return; // While the button could be disabled, this looks nicer visually (since we update the download progress in the button text)
if (File.Exists(CrossPlatform.LocateSingleplayerExecutable()) && !CanLaunch) return; // While the button could be disabled, this looks nicer visually (since we update the download progress in the button text)
CanLaunch = false;
MakeBackup();
PrintLog($"Starting singleplayer download...");
@ -103,6 +108,8 @@ public class SingleplayerViewModel : ViewModelBase
private void PrintLog(string message)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) message = Regex.Replace(message, @"\e\[[0-9;]*m", string.Empty);
ShowLogPanel = true;
Dispatcher.UIThread.Post(() => { SingleplayerLogsTextBox.Text += message + Environment.NewLine; },
DispatcherPriority.Background);
@ -113,12 +120,19 @@ public class SingleplayerViewModel : ViewModelBase
public void LaunchSingleplayer()
{
CanLaunch = false;
new Task(() =>
CrossPlatform.RunCommandAndGetOutput(
$"{CrossPlatform.LocateSingleplayerExecutable()} {Launcher.JavaExecutableLocation}",
PrintLog,
PrintLog)
).Start();
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();
}
}
private void LaunchFaq()

View file

@ -25,9 +25,6 @@ namespace Saradomin.ViewModel.Windows
private readonly IRemoteConfigService _remoteConfigService;
private readonly ISettingsService _settingsService;
private bool JavaExecutableValid
=> CrossPlatform.IsJavaExecutableValid(_settingsService.Launcher.JavaExecutableLocation);
private LauncherSettings Launcher { get; }
public string Title { get; set; } = "2009scape launcher";
@ -50,9 +47,7 @@ namespace Saradomin.ViewModel.Windows
_remoteConfigService = remoteConfigService;
_javaUpdateService = javaUpdateService;
_javaUpdateService.JavaDownloadProgressChanged += OnJavaDownloadProgressUpdated;
_settingsService = settingsService;
Launcher = _settingsService.Launcher;
@ -62,13 +57,9 @@ namespace Saradomin.ViewModel.Windows
};
Message.Subscribe<MainViewLoadedMessage>(this, MainViewLoaded);
Message.Subscribe<SettingsModifiedMessage>(this, SettingsModified);
Message.Subscribe<NotificationBoxStateChangedMessage>(this, NotificatationBoxStateChanged);
if (!JavaExecutableValid)
{
_settingsService.Launcher.JavaExecutableLocation = CrossPlatform.LocateJavaExecutable();
}
_settingsService.Launcher.JavaExecutableLocation ??= CrossPlatform.LocateJavaExecutable();
}
public void ExitApplication()
@ -102,23 +93,6 @@ namespace Saradomin.ViewModel.Windows
}
}
public void SettingsModified(SettingsModifiedMessage msg)
{
if (msg.SettingName == nameof(LauncherSettings.JavaExecutableLocation))
{
if (!JavaExecutableValid)
{
CanLaunch = false;
LaunchText = "Unable to locate Java. Find Java executable using Settings page.";
}
else
{
CanLaunch = true;
LaunchText = "Play!";
}
}
}
public void NotificatationBoxStateChanged(NotificationBoxStateChangedMessage msg)
{
DimContent = msg.WasOpened;