From 60440ba6d52caf04bdf2eaa55a59f92493ba711a Mon Sep 17 00:00:00 2001 From: VirxEC Date: Thu, 30 Apr 2026 13:58:57 -0400 Subject: [PATCH 1/6] Fix EGS launch on Windows --- RLBotCS/Main.cs | 2 +- RLBotCS/ManagerTools/LaunchManager.cs | 68 +++++++++++++++------------ RLBotCS/ManagerTools/WinReadLog.cs | 67 ++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 30 deletions(-) create mode 100644 RLBotCS/ManagerTools/WinReadLog.cs diff --git a/RLBotCS/Main.cs b/RLBotCS/Main.cs index 171d375..44cd327 100644 --- a/RLBotCS/Main.cs +++ b/RLBotCS/Main.cs @@ -10,7 +10,7 @@ if (args.Length > 0 && args[0] == "--version") { Console.WriteLine( - "RLBotServer v5.0.0-rc.4\n" + "RLBotServer v5.0.0-rc.5\n" + $"Bridge {BridgeVersion.Version}\n" + "@ https://www.rlbot.org & https://github.com/RLBot/core" ); diff --git a/RLBotCS/ManagerTools/LaunchManager.cs b/RLBotCS/ManagerTools/LaunchManager.cs index 991c5c2..40443dd 100644 --- a/RLBotCS/ManagerTools/LaunchManager.cs +++ b/RLBotCS/ManagerTools/LaunchManager.cs @@ -17,7 +17,7 @@ static class LaunchManager private static readonly ILogger Logger = Logging.GetLogger("LaunchManager"); - public static string? GetGameArgs(bool kill) + public static string? GetGameArgs() { Process[] candidates = Process.GetProcesses(); @@ -26,16 +26,25 @@ static class LaunchManager if (!candidate.ProcessName.Contains("RocketLeague")) continue; - string args = GetProcessArgs(candidate); - if (kill) - candidate.Kill(); - - return args; + return GetProcessArgs(candidate); } return null; } + public static void KillGame() + { + Process[] candidates = Process.GetProcesses(); + + foreach (var candidate in candidates) + { + if (!candidate.ProcessName.Contains("RocketLeague")) + continue; + + candidate.Kill(); + } + } + public static int FindUsableGamePort(int rlbotSocketsPort) { Process[] candidates = Process.GetProcessesByName("RocketLeague"); @@ -293,33 +302,30 @@ int gamePort } Logger.LogInformation("Finding Rocket League..."); - string? args = null; + (string, string)? pathAndAuth = null; - // get the game path & login args, the quickly kill the game - // todo: add max number of retries - while (args is null) + WinReadLog logReader = new(); + + // get the game path & login args + while (pathAndAuth is null) { - // don't kill the game if it was already running, and not for RLBot - args = GetGameArgs(!nonRLBotGameRunning); + pathAndAuth = logReader.GetGamePathAndAuth(); Thread.Sleep(1000); } - if (args is null) + // kill the game if it wasn't already running + if (!nonRLBotGameRunning) + KillGame(); + + if (pathAndAuth is null) throw new Exception("Failed to get Rocket League args"); - string directGamePath = ParseCommand(args)[0]; + string directGamePath = pathAndAuth.Value.Item1; Logger.LogInformation($"Found Rocket League at \"{directGamePath}\""); - // append RLBot args - args = args.Replace(directGamePath, ""); - args = args.Replace("\"\"", ""); + string auth = pathAndAuth.Value.Item2; string idealArgs = string.Join(" ", GetIdealArgs(gamePort)); - // rlbot args need to be first or the game might ignore them :( - string modifiedArgs = $"\"{directGamePath}\" {idealArgs} {args}"; - - // wait for the game to fully close - while (IsRocketLeagueRunning()) - Thread.Sleep(500); + string modifiedArgs = $"\"{directGamePath}\" {idealArgs} {auth}"; // relaunch the game with the new args Process epicRocketLeague = new(); @@ -330,9 +336,13 @@ int gamePort epicRocketLeague.StartInfo.UseShellExecute = false; epicRocketLeague.StartInfo.RedirectStandardOutput = true; epicRocketLeague.StartInfo.RedirectStandardError = true; - epicRocketLeague.Start(); + + // wait for the game to fully close before starting the game + while (IsRocketLeagueRunning()) + Thread.Sleep(500); Logger.LogInformation($"Starting RocketLeague.exe directly with {idealArgs}"); + epicRocketLeague.Start(); // if we don't read the output, the game will hang new Thread(() => @@ -342,12 +352,12 @@ int gamePort break; case RLBot.Flat.Launcher.Custom: - if (extraArg.ToLower() == "legendary") + if (extraArg.Equals("legendary", StringComparison.OrdinalIgnoreCase)) { LaunchGameViaLegendary(); return; } - else if (extraArg.ToLower() == "heroic") + else if (extraArg.Equals("heroic", StringComparison.OrdinalIgnoreCase)) { LaunchGameViaHeroic(); return; @@ -377,12 +387,12 @@ int gamePort "Epic Games Store is not directly supported on Linux." ); case RLBot.Flat.Launcher.Custom: - if (extraArg.ToLower() == "legendary") + if (extraArg.Equals("legendary", StringComparison.OrdinalIgnoreCase)) { LaunchGameViaLegendary(); return; } - else if (extraArg.ToLower() == "heroic") + else if (extraArg.Equals("heroic", StringComparison.OrdinalIgnoreCase)) { LaunchGameViaHeroic(); return; @@ -398,7 +408,7 @@ int gamePort public static string? GetRocketLeaguePath() { // Assumes the game has already been launched - string? args = GetGameArgs(false); + string? args = GetGameArgs(); if (args is null) return null; diff --git a/RLBotCS/ManagerTools/WinReadLog.cs b/RLBotCS/ManagerTools/WinReadLog.cs new file mode 100644 index 0000000..c307b32 --- /dev/null +++ b/RLBotCS/ManagerTools/WinReadLog.cs @@ -0,0 +1,67 @@ +#if WINDOWS +using System.Runtime.InteropServices; +using System.Text; + +public class WinReadLog +{ + private const int CSIDL_PERSONAL = 0x0005; + private const int SHGFP_TYPE_CURRENT = 0; + private const string AUTH_LINE_PREFIX = "Init: Command line: "; + private const string PATH_LINE_PREFIX = "Init: Base directory: "; + private const string BINARY_NAME = "RocketLeague.exe"; + + [DllImport("shell32.dll", CharSet = CharSet.Unicode)] + private static extern int SHGetFolderPathW( + IntPtr hwnd, + int csidl, + IntPtr hToken, + int dwFlags, + StringBuilder pszPath + ); + + static string GetMyDocumentsFolder() + { + var sb = new StringBuilder(260); + SHGetFolderPathW(IntPtr.Zero, CSIDL_PERSONAL, IntPtr.Zero, SHGFP_TYPE_CURRENT, sb); + return sb.ToString(); + } + + private string LogPath; + + public WinReadLog() + { + LogPath = Path.Combine( + GetMyDocumentsFolder(), + "My Games", + "Rocket League", + "TAGame", + "Logs", + "Launch.log" + ); + } + + public (string, string)? GetGamePathAndAuth() + { + string logContent = File.ReadAllText(LogPath); + + int authStart = + logContent.IndexOf(AUTH_LINE_PREFIX, StringComparison.Ordinal) + + AUTH_LINE_PREFIX.Length; + int pathStart = + logContent.IndexOf(PATH_LINE_PREFIX, StringComparison.Ordinal) + + PATH_LINE_PREFIX.Length; + if (authStart == -1 || pathStart == -1) + return null; + + int authEnd = logContent.IndexOf('\n', authStart); + int pathEnd = logContent.IndexOf('\n', pathStart); + if (authEnd == -1 || pathEnd == -1) + return null; + + string auth = logContent[authStart..authEnd].TrimEnd('\r', '\n'); + string path = logContent[pathStart..pathEnd].TrimEnd('\r', '\n'); + + return (Path.Combine(path, BINARY_NAME), auth); + } +} +#endif From 43f879572252f22a96e780e3069dbbd8f324c4f1 Mon Sep 17 00:00:00 2001 From: Virx Date: Thu, 30 Apr 2026 14:28:06 -0400 Subject: [PATCH 2/6] Wait for the game to launch --- RLBotCS/ManagerTools/LaunchManager.cs | 10 ++++++++-- RLBotCS/ManagerTools/WinReadLog.cs | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/RLBotCS/ManagerTools/LaunchManager.cs b/RLBotCS/ManagerTools/LaunchManager.cs index 40443dd..803a593 100644 --- a/RLBotCS/ManagerTools/LaunchManager.cs +++ b/RLBotCS/ManagerTools/LaunchManager.cs @@ -302,11 +302,17 @@ int gamePort } Logger.LogInformation("Finding Rocket League..."); - (string, string)? pathAndAuth = null; - WinReadLog logReader = new(); + // Wait until the game is running + while (!IsRocketLeagueRunning()) + { + Thread.Sleep(1000); + } // get the game path & login args + WinReadLog logReader = new(); + (string, string)? pathAndAuth = null; + while (pathAndAuth is null) { pathAndAuth = logReader.GetGamePathAndAuth(); diff --git a/RLBotCS/ManagerTools/WinReadLog.cs b/RLBotCS/ManagerTools/WinReadLog.cs index c307b32..539fd81 100644 --- a/RLBotCS/ManagerTools/WinReadLog.cs +++ b/RLBotCS/ManagerTools/WinReadLog.cs @@ -42,6 +42,9 @@ public WinReadLog() public (string, string)? GetGamePathAndAuth() { + if (!File.Exists(LogPath)) + return null; + string logContent = File.ReadAllText(LogPath); int authStart = From 19b585c35d6d01c288b526ed81c42078ab86eeaf Mon Sep 17 00:00:00 2001 From: NicEastvillage Date: Fri, 1 May 2026 09:10:59 +0200 Subject: [PATCH 3/6] Take auth args from process instead --- RLBotCS/ManagerTools/LaunchManager.cs | 90 ++++++++++++++------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/RLBotCS/ManagerTools/LaunchManager.cs b/RLBotCS/ManagerTools/LaunchManager.cs index 803a593..fadb5e2 100644 --- a/RLBotCS/ManagerTools/LaunchManager.cs +++ b/RLBotCS/ManagerTools/LaunchManager.cs @@ -103,7 +103,7 @@ private static string GetProcessArgs(Process process) #endif } - private static string[] GetIdealArgs(int gamePort) => + private static string[] GetRLBotArgs(int gamePort) => [ "-rlbot", $"RLBot_ControllerURL=127.0.0.1:{gamePort}", @@ -276,81 +276,87 @@ int gamePort Process steam = new(); steam.StartInfo.FileName = steamPath; steam.StartInfo.Arguments = - $"-applaunch {SteamGameId} " + string.Join(" ", GetIdealArgs(gamePort)); + $"-applaunch {SteamGameId} " + string.Join(" ", GetRLBotArgs(gamePort)); Logger.LogInformation( - $"Starting Rocket League with args {steamPath} {steam.StartInfo.Arguments}" + $"Starting Rocket League with steam: {steamPath} {steam.StartInfo.Arguments}" ); steam.Start(); break; case RLBot.Flat.Launcher.Epic: - bool nonRLBotGameRunning = IsRocketLeagueRunning(); - - // we don't need to start the game because there's another instance of non-rlbot rocket league open - if (!nonRLBotGameRunning) + if (IsRocketLeagueRunningWithArgs()) { - // we need a hack to launch the game properly - // start the game - Process launcher = new(); - launcher.StartInfo.FileName = "cmd.exe"; - launcher.StartInfo.Arguments = - "/c start \"\" \"com.epicgames.launcher://apps/9773aa1aa54f4f7b80e44bef04986cea%3A530145df28a24424923f5828cc9031a1%3ASugar?action=launch&silent=true\""; - launcher.Start(); - - // wait for it to start - Thread.Sleep(1000); + return; + } + + if (IsRocketLeagueRunning()) + { + Logger.LogError("Please close Rocket League so RLBot can start it in RLBot mode."); + return; } + // To launch RocketLeague for Epic we need some extra login parameters from Epic. + // We get these by launching the game normally, reading the args, and then closing it again. + + Process launcher = new(); + launcher.StartInfo.FileName = "cmd.exe"; + launcher.StartInfo.Arguments = + "/c start \"\" \"com.epicgames.launcher://apps/9773aa1aa54f4f7b80e44bef04986cea%3A530145df28a24424923f5828cc9031a1%3ASugar?action=launch&silent=true\""; + launcher.Start(); + Thread.Sleep(500); + + // Get login args Logger.LogInformation("Finding Rocket League..."); - - // Wait until the game is running - while (!IsRocketLeagueRunning()) + string? epicArgs = null; + int triesLeft = 40; + while (epicArgs is null && triesLeft-- > 0) { - Thread.Sleep(1000); + epicArgs = GetGameArgs(); + Thread.Sleep(500); } + KillGame(); + if (epicArgs is null) + throw new Exception("Failed to get Rocket League args"); + Logger.LogDebug("Epic RocketLeague args: {}", epicArgs); + epicArgs = epicArgs.Replace("\"RocketLeague_EAC.exe\"", "").Trim(); + - // get the game path & login args + // Get the game path from launch logs WinReadLog logReader = new(); (string, string)? pathAndAuth = null; - while (pathAndAuth is null) { pathAndAuth = logReader.GetGamePathAndAuth(); - Thread.Sleep(1000); + Thread.Sleep(500); } - - // kill the game if it wasn't already running - if (!nonRLBotGameRunning) - KillGame(); - if (pathAndAuth is null) - throw new Exception("Failed to get Rocket League args"); - + throw new Exception("Failed to get Rocket League exe path"); string directGamePath = pathAndAuth.Value.Item1; Logger.LogInformation($"Found Rocket League at \"{directGamePath}\""); + + // Wait for the game to fully close + Logger.LogDebug("Waiting for Rocket League to fully close..."); + while (IsRocketLeagueRunning()) + Thread.Sleep(500); - string auth = pathAndAuth.Value.Item2; - string idealArgs = string.Join(" ", GetIdealArgs(gamePort)); - string modifiedArgs = $"\"{directGamePath}\" {idealArgs} {auth}"; + string rlbotArgs = string.Join(" ", GetRLBotArgs(gamePort)); + string modifiedArgs = $"\"{directGamePath}\" {rlbotArgs} {epicArgs}"; - // relaunch the game with the new args + // Relaunch the game with the new args Process epicRocketLeague = new(); epicRocketLeague.StartInfo.FileName = "cmd.exe"; epicRocketLeague.StartInfo.Arguments = $"/c \"{modifiedArgs}\""; - // prevent the game from printing to the console + // Prevent the game from printing to the console epicRocketLeague.StartInfo.UseShellExecute = false; epicRocketLeague.StartInfo.RedirectStandardOutput = true; epicRocketLeague.StartInfo.RedirectStandardError = true; - // wait for the game to fully close before starting the game - while (IsRocketLeagueRunning()) - Thread.Sleep(500); - - Logger.LogInformation($"Starting RocketLeague.exe directly with {idealArgs}"); + Logger.LogInformation($"Starting Rocket League with Epic: {rlbotArgs}"); + Logger.LogDebug("Full command: {} {}", epicRocketLeague.StartInfo.FileName, epicRocketLeague.StartInfo.Arguments); epicRocketLeague.Start(); - // if we don't read the output, the game will hang + // If we don't read the output, the game will hang new Thread(() => { epicRocketLeague.StandardOutput.ReadToEnd(); From 73a4ed8641d361ea1747ecc0f6878e37162ca0fe Mon Sep 17 00:00:00 2001 From: NicEastvillage Date: Fri, 1 May 2026 09:53:04 +0200 Subject: [PATCH 4/6] Fix failed rename --- RLBotCS/ManagerTools/LaunchManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RLBotCS/ManagerTools/LaunchManager.cs b/RLBotCS/ManagerTools/LaunchManager.cs index fadb5e2..f566a8f 100644 --- a/RLBotCS/ManagerTools/LaunchManager.cs +++ b/RLBotCS/ManagerTools/LaunchManager.cs @@ -383,7 +383,7 @@ int gamePort switch (launcherPref) { case RLBot.Flat.Launcher.Steam: - string args = string.Join("%20", GetIdealArgs(gamePort)); + string args = string.Join("%20", GetRLBotArgs(gamePort)); Process rocketLeague = new(); rocketLeague.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; rocketLeague.StartInfo.FileName = "steam"; From 1902cddaf0533c6dd673dd9a2cbf7830fbf4eb05 Mon Sep 17 00:00:00 2001 From: NicEastvillage Date: Fri, 1 May 2026 16:52:51 +0200 Subject: [PATCH 5/6] Backwards compatibility with old RL was requested --- RLBotCS/ManagerTools/LaunchManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RLBotCS/ManagerTools/LaunchManager.cs b/RLBotCS/ManagerTools/LaunchManager.cs index f566a8f..239aef3 100644 --- a/RLBotCS/ManagerTools/LaunchManager.cs +++ b/RLBotCS/ManagerTools/LaunchManager.cs @@ -318,8 +318,7 @@ int gamePort if (epicArgs is null) throw new Exception("Failed to get Rocket League args"); Logger.LogDebug("Epic RocketLeague args: {}", epicArgs); - epicArgs = epicArgs.Replace("\"RocketLeague_EAC.exe\"", "").Trim(); - + epicArgs = Regex.Replace(epicArgs, "\".*\"", "").Replace("\"\"", "").Trim(); // Get the game path from launch logs WinReadLog logReader = new(); From e31aca96296784a6bbd6de27dad195b82991f405 Mon Sep 17 00:00:00 2001 From: NicEastvillage Date: Fri, 1 May 2026 16:55:55 +0200 Subject: [PATCH 6/6] Format --- RLBotCS/ManagerTools/LaunchManager.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/RLBotCS/ManagerTools/LaunchManager.cs b/RLBotCS/ManagerTools/LaunchManager.cs index 239aef3..6291410 100644 --- a/RLBotCS/ManagerTools/LaunchManager.cs +++ b/RLBotCS/ManagerTools/LaunchManager.cs @@ -288,16 +288,18 @@ int gamePort { return; } - + if (IsRocketLeagueRunning()) { - Logger.LogError("Please close Rocket League so RLBot can start it in RLBot mode."); + Logger.LogError( + "Please close Rocket League so RLBot can start it in RLBot mode." + ); return; } // To launch RocketLeague for Epic we need some extra login parameters from Epic. // We get these by launching the game normally, reading the args, and then closing it again. - + Process launcher = new(); launcher.StartInfo.FileName = "cmd.exe"; launcher.StartInfo.Arguments = @@ -332,7 +334,7 @@ int gamePort throw new Exception("Failed to get Rocket League exe path"); string directGamePath = pathAndAuth.Value.Item1; Logger.LogInformation($"Found Rocket League at \"{directGamePath}\""); - + // Wait for the game to fully close Logger.LogDebug("Waiting for Rocket League to fully close..."); while (IsRocketLeagueRunning()) @@ -352,7 +354,11 @@ int gamePort epicRocketLeague.StartInfo.RedirectStandardError = true; Logger.LogInformation($"Starting Rocket League with Epic: {rlbotArgs}"); - Logger.LogDebug("Full command: {} {}", epicRocketLeague.StartInfo.FileName, epicRocketLeague.StartInfo.Arguments); + Logger.LogDebug( + "Full command: {} {}", + epicRocketLeague.StartInfo.FileName, + epicRocketLeague.StartInfo.Arguments + ); epicRocketLeague.Start(); // If we don't read the output, the game will hang