mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
launcher: EAC-aware exe pick + optional TLS trust bypass
Launch RecRoom.exe directly on 2020.12 EAC builds (skip the Recroom_Release.exe EAC wrapper); fall back to whichever single binary exists. Add an opt-in "trust self-signed TLS certs" toggle that flows through ClientPatcher into the ClientMod config, off by default since Localtunnel/Tunnelto serve valid certs.
This commit is contained in:
@@ -79,6 +79,17 @@ public sealed class AppState
|
|||||||
[JsonPropertyName("launchInScreenMode")]
|
[JsonPropertyName("launchInScreenMode")]
|
||||||
public bool LaunchInScreenMode { get; set; }
|
public bool LaunchInScreenMode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>When true, the patched ClientMod skips BouncyCastle's
|
||||||
|
/// TLS chain validation — needed only for self-signed certs on a
|
||||||
|
/// LAN-only / dev tunnel. Localtunnel + Tunnelto both terminate
|
||||||
|
/// TLS with valid certs, so the default is off. Leaving it on
|
||||||
|
/// triggers a CLR detour-compile crash (0x80131506) on the
|
||||||
|
/// december build — see project_build_layout_per_branch +
|
||||||
|
/// MonoMod's CompileMethodHook comment in
|
||||||
|
/// DorkNet.ClientMod/Mod.cs.</summary>
|
||||||
|
[JsonPropertyName("enableTlsTrustBypass")]
|
||||||
|
public bool EnableTlsTrustBypass { get; set; }
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
{
|
{
|
||||||
WriteIndented = true,
|
WriteIndented = true,
|
||||||
|
|||||||
@@ -59,10 +59,11 @@ public sealed class ClientPatcher
|
|||||||
string photonRegion,
|
string photonRegion,
|
||||||
string apexHost,
|
string apexHost,
|
||||||
bool singleOrigin = false,
|
bool singleOrigin = false,
|
||||||
|
bool enableTlsTrustBypass = false,
|
||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
return Task.Run(() => ApplyCore(patcherDir, recRoomDataPath,
|
return Task.Run(() => ApplyCore(patcherDir, recRoomDataPath,
|
||||||
photonAppId, photonVoiceAppId, photonRegion, apexHost, singleOrigin), ct);
|
photonAppId, photonVoiceAppId, photonRegion, apexHost, singleOrigin, enableTlsTrustBypass), ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PatchResult ApplyCore(
|
private PatchResult ApplyCore(
|
||||||
@@ -72,7 +73,8 @@ public sealed class ClientPatcher
|
|||||||
string photonVoiceAppId,
|
string photonVoiceAppId,
|
||||||
string photonRegion,
|
string photonRegion,
|
||||||
string apexHost,
|
string apexHost,
|
||||||
bool singleOrigin)
|
bool singleOrigin,
|
||||||
|
bool enableTlsTrustBypass)
|
||||||
{
|
{
|
||||||
var log = new System.Text.StringBuilder();
|
var log = new System.Text.StringBuilder();
|
||||||
try
|
try
|
||||||
@@ -220,7 +222,8 @@ public sealed class ClientPatcher
|
|||||||
.Replace("{PHOTON_VOICE_APPID}",
|
.Replace("{PHOTON_VOICE_APPID}",
|
||||||
string.IsNullOrEmpty(photonVoiceAppId) ? photonAppId ?? "" : photonVoiceAppId)
|
string.IsNullOrEmpty(photonVoiceAppId) ? photonAppId ?? "" : photonVoiceAppId)
|
||||||
.Replace("{PHOTON_REGION}", string.IsNullOrEmpty(photonRegion) ? "us" : photonRegion)
|
.Replace("{PHOTON_REGION}", string.IsNullOrEmpty(photonRegion) ? "us" : photonRegion)
|
||||||
.Replace("{SINGLE_ORIGIN_BASE_URL}", singleOrigin ? $"https://{apexHost}" : "");
|
.Replace("{SINGLE_ORIGIN_BASE_URL}", singleOrigin ? $"https://{apexHost}" : "")
|
||||||
|
.Replace("{ENABLE_TLS_TRUST_BYPASS}", enableTlsTrustBypass ? "true" : "false");
|
||||||
File.WriteAllText(configDest, rendered);
|
File.WriteAllText(configDest, rendered);
|
||||||
log.AppendLine($"[patch] wrote config: {Path.GetFileName(configDest)}");
|
log.AppendLine($"[patch] wrote config: {Path.GetFileName(configDest)}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,32 @@ using System.IO;
|
|||||||
namespace DorkNet.Launcher.Backend;
|
namespace DorkNet.Launcher.Backend;
|
||||||
|
|
||||||
/// <summary>Launches the user's already-patched Rec Room install
|
/// <summary>Launches the user's already-patched Rec Room install
|
||||||
/// directly via <c>Recroom_Release.exe</c>. Never goes through Steam —
|
/// directly. Never goes through Steam — DorkNet users may not own
|
||||||
/// DorkNet users may not own the game on Steam.</summary>
|
/// the game on Steam.
|
||||||
|
///
|
||||||
|
/// <para><b>2020.03 vs 2020.12 build layout:</b></para>
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item>2020.03 (pre-EAC): single binary <c>Recroom_Release.exe</c>
|
||||||
|
/// alongside <c>Recroom_Release_Data/</c>. Run that directly.</item>
|
||||||
|
/// <item>2020.12+ (EAC): the real Unity binary is <c>RecRoom.exe</c>
|
||||||
|
/// with its data in <c>RecRoom_Data/</c>; <c>Recroom_Release.exe</c>
|
||||||
|
/// is the Easy Anti-Cheat launcher wrapper that Steam invokes. Run
|
||||||
|
/// <c>RecRoom.exe</c> directly to skip EAC entirely — EAC never
|
||||||
|
/// loads into the game process, no binary patching required (this is
|
||||||
|
/// the same approach <c>tools/remove-eac.ps1</c> documents on the
|
||||||
|
/// per-version branch).</item>
|
||||||
|
/// </list>
|
||||||
|
/// Detection: an <c>EasyAntiCheat/</c> folder next to the binaries
|
||||||
|
/// flags the EAC layout. Falls back to whichever exe exists if the
|
||||||
|
/// heuristic is ambiguous.</summary>
|
||||||
public static class RecRoomLauncher
|
public static class RecRoomLauncher
|
||||||
{
|
{
|
||||||
/// <summary>The picked install path is the <c>Recroom_Release_Data</c>
|
/// <summary>The picked install path is the <c>*_Data</c> folder;
|
||||||
/// folder; the executable lives at <c><parent>\Recroom_Release.exe</c>.
|
/// the executable lives at its parent. Returns true if
|
||||||
/// Returns true if Process.Start succeeded. <paramref name="screenMode"/>
|
/// <see cref="Process.Start(ProcessStartInfo)"/> succeeded.
|
||||||
/// passes Rec Room's <c>+forcemode:screen</c> arg so the game opens
|
/// <paramref name="screenMode"/> passes Rec Room's
|
||||||
/// in 2D desktop mode instead of trying to bind a headset.</summary>
|
/// <c>+forcemode:screen</c> arg so the game opens in 2D desktop
|
||||||
|
/// mode instead of trying to bind a headset.</summary>
|
||||||
public static bool TryLaunch(string recRoomDataPath, bool screenMode, out string error)
|
public static bool TryLaunch(string recRoomDataPath, bool screenMode, out string error)
|
||||||
{
|
{
|
||||||
error = "";
|
error = "";
|
||||||
@@ -27,13 +44,15 @@ public static class RecRoomLauncher
|
|||||||
error = $"Couldn't resolve the parent directory of '{recRoomDataPath}'.";
|
error = $"Couldn't resolve the parent directory of '{recRoomDataPath}'.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var exe = Path.Combine(parent, "Recroom_Release.exe");
|
|
||||||
if (!File.Exists(exe))
|
var exe = PickExecutable(parent);
|
||||||
|
if (exe is null)
|
||||||
{
|
{
|
||||||
error = $"Recroom_Release.exe not found at '{exe}'. " +
|
error = $"Neither RecRoom.exe nor Recroom_Release.exe found next to '{recRoomDataPath}'. " +
|
||||||
"Pick the *_Data folder that sits next to it.";
|
"Pick the *_Data folder that sits beside the game binary.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process.Start(new ProcessStartInfo
|
Process.Start(new ProcessStartInfo
|
||||||
@@ -51,4 +70,24 @@ public static class RecRoomLauncher
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Pick the right game exe for the install. Prefer
|
||||||
|
/// <c>RecRoom.exe</c> on EAC builds (so we skip the EAC launcher
|
||||||
|
/// wrapper). Fall back to whichever single binary exists when only
|
||||||
|
/// one is present.</summary>
|
||||||
|
private static string? PickExecutable(string parent)
|
||||||
|
{
|
||||||
|
var recRoomExe = Path.Combine(parent, "RecRoom.exe");
|
||||||
|
var legacyExe = Path.Combine(parent, "Recroom_Release.exe");
|
||||||
|
var hasEac = Directory.Exists(Path.Combine(parent, "EasyAntiCheat"));
|
||||||
|
|
||||||
|
// EAC build: RecRoom.exe is the actual game; Recroom_Release.exe
|
||||||
|
// is the EAC launcher wrapper. Use the former — it never loads
|
||||||
|
// EAC into the process.
|
||||||
|
if (hasEac && File.Exists(recRoomExe)) return recRoomExe;
|
||||||
|
// Only-one-present cases.
|
||||||
|
if (File.Exists(legacyExe)) return legacyExe;
|
||||||
|
if (File.Exists(recRoomExe)) return recRoomExe;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -967,6 +967,14 @@
|
|||||||
<ComboBoxItem Content="India" Tag="in" />
|
<ComboBoxItem Content="India" Tag="in" />
|
||||||
<ComboBoxItem Content="Australia" Tag="au" />
|
<ComboBoxItem Content="Australia" Tag="au" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
||||||
|
<CheckBox x:Name="TlsTrustBypassChk"
|
||||||
|
Content="Trust self-signed TLS certs (advanced)"
|
||||||
|
Margin="0,16,0,0"
|
||||||
|
ToolTip="Skip the watch's TLS chain validation on your server's HTTPS connections. Needed only for self-signed certs (LAN-only / dev tunnels). Off for Localtunnel / Tunnelto / any real CDN cert. WARNING: leaving this on with the December 2020.12.18 build triggers a MonoMod CLR crash (0x80131506) at launch."
|
||||||
|
Checked="OnTlsTrustBypassChanged"
|
||||||
|
Unchecked="OnTlsTrustBypassChanged"
|
||||||
|
Foreground="{StaticResource Ink}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ public partial class MainWindow : Window
|
|||||||
HostScreenModeChk.IsChecked = _state.LaunchInScreenMode;
|
HostScreenModeChk.IsChecked = _state.LaunchInScreenMode;
|
||||||
if (JoinScreenModeChk is not null)
|
if (JoinScreenModeChk is not null)
|
||||||
JoinScreenModeChk.IsChecked = _state.LaunchInScreenMode;
|
JoinScreenModeChk.IsChecked = _state.LaunchInScreenMode;
|
||||||
|
if (TlsTrustBypassChk is not null)
|
||||||
|
TlsTrustBypassChk.IsChecked = _state.EnableTlsTrustBypass;
|
||||||
|
|
||||||
// Hosting mode radios — reflect persisted choice on both the
|
// Hosting mode radios — reflect persisted choice on both the
|
||||||
// main HostView and the setup wizard's matching step.
|
// main HostView and the setup wizard's matching step.
|
||||||
@@ -721,7 +723,8 @@ public partial class MainWindow : Window
|
|||||||
var patch = await _patcher.ApplyAsync(
|
var patch = await _patcher.ApplyAsync(
|
||||||
patcherDir, _state.RecRoomPath!,
|
patcherDir, _state.RecRoomPath!,
|
||||||
_state.PhotonAppId, _state.PhotonVoiceAppId, _state.PhotonRegion, _hostApex,
|
_state.PhotonAppId, _state.PhotonVoiceAppId, _state.PhotonRegion, _hostApex,
|
||||||
singleOrigin: _state.HostingMode == HostingMode.SingleOriginTunnel);
|
singleOrigin: _state.HostingMode == HostingMode.SingleOriginTunnel,
|
||||||
|
enableTlsTrustBypass: _state.EnableTlsTrustBypass);
|
||||||
if (!patch.Ok)
|
if (!patch.Ok)
|
||||||
{
|
{
|
||||||
FailStepWithFriendly(activeStep, ErrorTranslator.TranslateMessage(patch.Log));
|
FailStepWithFriendly(activeStep, ErrorTranslator.TranslateMessage(patch.Log));
|
||||||
@@ -1013,7 +1016,8 @@ public partial class MainWindow : Window
|
|||||||
var patch = await _patcher.ApplyAsync(
|
var patch = await _patcher.ApplyAsync(
|
||||||
patcherDir, _state.RecRoomPath!,
|
patcherDir, _state.RecRoomPath!,
|
||||||
payload.PhotonAppId, payload.PhotonVoiceAppId, payload.PhotonRegion, payload.Host,
|
payload.PhotonAppId, payload.PhotonVoiceAppId, payload.PhotonRegion, payload.Host,
|
||||||
singleOrigin: payload.SingleOrigin);
|
singleOrigin: payload.SingleOrigin,
|
||||||
|
enableTlsTrustBypass: _state.EnableTlsTrustBypass);
|
||||||
if (!patch.Ok)
|
if (!patch.Ok)
|
||||||
{
|
{
|
||||||
FailStepWithFriendly(activeStep, ErrorTranslator.TranslateMessage(patch.Log));
|
FailStepWithFriendly(activeStep, ErrorTranslator.TranslateMessage(patch.Log));
|
||||||
@@ -1066,6 +1070,13 @@ public partial class MainWindow : Window
|
|||||||
_state.Save();
|
_state.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnTlsTrustBypassChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_state is null) return;
|
||||||
|
_state.EnableTlsTrustBypass = (sender as CheckBox)?.IsChecked == true;
|
||||||
|
_state.Save();
|
||||||
|
}
|
||||||
|
|
||||||
private void EnsureRemoteWildcardApex()
|
private void EnsureRemoteWildcardApex()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(_state.RemoteWildcardApex)) return;
|
if (!string.IsNullOrWhiteSpace(_state.RemoteWildcardApex)) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user