Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion RLBotCS/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
Expand Down
132 changes: 77 additions & 55 deletions RLBotCS/ManagerTools/LaunchManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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");
Expand Down Expand Up @@ -94,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}",
Expand Down Expand Up @@ -267,87 +276,100 @@ 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...");
string? args = null;

// get the game path & login args, the quickly kill the game
// todo: add max number of retries
while (args is null)
string? epicArgs = null;
int triesLeft = 40;
while (epicArgs is null && triesLeft-- > 0)
{
// don't kill the game if it was already running, and not for RLBot
args = GetGameArgs(!nonRLBotGameRunning);
Thread.Sleep(1000);
epicArgs = GetGameArgs();
Thread.Sleep(500);
}

if (args is null)
KillGame();
if (epicArgs is null)
throw new Exception("Failed to get Rocket League args");

string directGamePath = ParseCommand(args)[0];
Logger.LogDebug("Epic RocketLeague args: {}", epicArgs);
epicArgs = epicArgs.Replace("\"RocketLeague_EAC.exe\"", "").Trim();


// Get the game path from launch logs
WinReadLog logReader = new();
(string, string)? pathAndAuth = null;
while (pathAndAuth is null)
{
pathAndAuth = logReader.GetGamePathAndAuth();
Thread.Sleep(500);
}
if (pathAndAuth is null)
throw new Exception("Failed to get Rocket League exe path");
string directGamePath = pathAndAuth.Value.Item1;
Logger.LogInformation($"Found Rocket League at \"{directGamePath}\"");

// append RLBot args
args = args.Replace(directGamePath, "");
args = args.Replace("\"\"", "");
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

// Wait for the game to fully close
Logger.LogDebug("Waiting for Rocket League to fully close...");
while (IsRocketLeagueRunning())
Thread.Sleep(500);

// relaunch the game with the new args
string rlbotArgs = string.Join(" ", GetRLBotArgs(gamePort));
string modifiedArgs = $"\"{directGamePath}\" {rlbotArgs} {epicArgs}";

// 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;
epicRocketLeague.Start();

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();
}).Start();

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;
Expand All @@ -361,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";
Expand All @@ -377,12 +399,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;
Expand All @@ -398,7 +420,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;

Expand Down
70 changes: 70 additions & 0 deletions RLBotCS/ManagerTools/WinReadLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#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()
{
if (!File.Exists(LogPath))
return null;

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
Loading