Skip to content
Draft
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-rc17\n"
"RLBotServer v5.0.0-rc.18\n"
+ $"Bridge {BridgeVersion.Version}\n"
+ "@ https://www.rlbot.org & https://github.com/RLBot/core"
);
Expand Down
73 changes: 68 additions & 5 deletions RLBotCS/ManagerTools/ConfigParser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using RLBot.Flat;
using RLBotCS.Model;
Expand Down Expand Up @@ -77,6 +78,8 @@ public static class Fields
public const string AgentRunCommand = "run_command";
public const string AgentRunCommandLinux = "run_command_linux";
public const string AgentEnvironment = "environment";
public const string AgentEnvironmentWindows = "windows";
public const string AgentEnvironmentLinux = "linux";
public const string AgentHivemind = "hivemind";

public const string LoadoutBlueTable = "blue_loadout";
Expand Down Expand Up @@ -261,6 +264,26 @@ private string GetRunCommand(TomlTable runnableSettings)
#endif
}

private static readonly Regex UnixEnvironmentVariablePattern = new(
@"\$(?:\{(?<braced>[A-Za-z_][A-Za-z0-9_]*)\}|(?<bare>[A-Za-z_][A-Za-z0-9_]*))",

@VirxEC VirxEC Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: this regex is AI generated and I have not reviewed it yet outside of "it appears to work"

RegexOptions.Compiled
);

private static string ExpandEnvironmentValue(string value)
{
value = Environment.ExpandEnvironmentVariables(value);
return UnixEnvironmentVariablePattern.Replace(
value,
match =>
{
string name = match.Groups["braced"].Success
? match.Groups["braced"].Value
: match.Groups["bare"].Value;
return Environment.GetEnvironmentVariable(name) ?? match.Value;
}
);
}

private List<EnvironmentVariableT> GetEnvironment(TomlTable runnableSettings)
{
TomlTable environment = GetValue<TomlTable>(
Expand All @@ -269,23 +292,63 @@ private List<EnvironmentVariableT> GetEnvironment(TomlTable runnableSettings)
[]
);

List<EnvironmentVariableT> variables = [];
using (_context.Begin(Fields.AgentEnvironment))
Dictionary<string, string> values = new(StringComparer.Ordinal);

void ReadValues(TomlTable table, bool skipPlatformTables, bool expandValues)
{
foreach (var (key, rawValue) in environment)
foreach (var (key, rawValue) in table)
{
if (
skipPlatformTables
&& (
key == Fields.AgentEnvironmentWindows
|| key == Fields.AgentEnvironmentLinux
)
)
continue;

if (rawValue is not string value)
{
throw new InvalidCastException(
$"{_context.ToStringWithEnd(key)} has value {rawValue}, but a value of type String was expected."
);
}

variables.Add(new() { Name = key, Value = value });
values[key] = expandValues ? ExpandEnvironmentValue(value) : value;
}
}

using (_context.Begin(Fields.AgentEnvironment))
{
ReadValues(environment, true, false);

string? platform =
OperatingSystem.IsWindows() ? Fields.AgentEnvironmentWindows
: OperatingSystem.IsLinux() ? Fields.AgentEnvironmentLinux
: null;

if (
platform is not null
&& environment.TryGetValue(platform, out var rawPlatformEnvironment)
)
{
if (rawPlatformEnvironment is not TomlTable platformEnvironment)
{
throw new InvalidCastException(
$"{_context.ToStringWithEnd(platform)} has value {rawPlatformEnvironment}, but a table was expected."
);
}

using (_context.Begin(platform))
{
ReadValues(platformEnvironment, false, true);
}
}
}

return variables;
return values
.Select(pair => new EnvironmentVariableT { Name = pair.Key, Value = pair.Value })
.ToList();
}

private ScriptConfigurationT LoadScriptConfig(string scriptConfigPath)
Expand Down
70 changes: 70 additions & 0 deletions RLBotCSTests/ConfigParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,76 @@ public void Overrides()
);
}

[TestMethod]
public void PlatformEnvironmentOverrides()
{
ConfigParser parser = new();
MatchConfigurationT mc = parser.LoadMatchConfig("TestTomls/platform.toml");

string expectedShared =
OperatingSystem.IsWindows() ? "windows-bot-value"
: OperatingSystem.IsLinux() ? "linux-bot-value"
: "common-bot-value";
string expectedScriptShared =
OperatingSystem.IsWindows() ? "windows-script-value"
: OperatingSystem.IsLinux() ? "linux-script-value"
: "common-script-value";

CustomBotT bot = mc.PlayerConfigurations[0].Variety.AsCustomBot();
Assert.AreEqual(
"$HOME/common-value",
bot.Environment.Single(e => e.Name == "COMMON_ENV").Value
);
Assert.AreEqual(
expectedShared,
bot.Environment.Single(e => e.Name == "SHARED_ENV").Value
);

ScriptConfigurationT script = mc.ScriptConfigurations[0];
Assert.AreEqual(
"$HOME/common-value",
script.Environment.Single(e => e.Name == "COMMON_ENV").Value
);
Assert.AreEqual(
expectedScriptShared,
script.Environment.Single(e => e.Name == "SHARED_ENV").Value
);

if (OperatingSystem.IsWindows())
{
Assert.AreEqual(
Environment.ExpandEnvironmentVariables(
"%LOCALAPPDATA%\\RLBot5\\bots\\torch-archive"
),
bot.Environment.Single(e => e.Name == "PLATFORM_ENV").Value
);
Assert.AreEqual(
Environment.ExpandEnvironmentVariables(
"%LOCALAPPDATA%\\RLBot5\\bots\\torch-archive"
),
script.Environment.Single(e => e.Name == "PLATFORM_ENV").Value
);
}
else if (OperatingSystem.IsLinux())
{
string expectedPlatform =
$"{Environment.GetEnvironmentVariable("HOME")}/.local/share/RLBot5/bots/torch-archive";
Assert.AreEqual(
expectedPlatform,
bot.Environment.Single(e => e.Name == "PLATFORM_ENV").Value
);
Assert.AreEqual(
expectedPlatform,
script.Environment.Single(e => e.Name == "PLATFORM_ENV").Value
);
}
else
{
Assert.IsFalse(bot.Environment.Any(e => e.Name == "PLATFORM_ENV"));
Assert.IsFalse(script.Environment.Any(e => e.Name == "PLATFORM_ENV"));
}
}

[TestMethod]
public void ConfigNotFound()
{
Expand Down
19 changes: 19 additions & 0 deletions RLBotCSTests/TestTomls/platform.bot.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[settings]
agent_id = "test/platform-bot"
name = "Platform Test Bot"
run_command = "python bot.py"
run_command_linux = "python bot.py"

[settings.environment]
COMMON_ENV = "$HOME/common-value"
SHARED_ENV = "common-bot-value"

[settings.environment.windows]
SHARED_ENV = "windows-bot-value"
PLATFORM_ENV = "%LOCALAPPDATA%\\RLBot5\\bots\\torch-archive"

[settings.environment.linux]
SHARED_ENV = "linux-bot-value"
PLATFORM_ENV = "$HOME/.local/share/RLBot5/bots/torch-archive"

[details]
19 changes: 19 additions & 0 deletions RLBotCSTests/TestTomls/platform.script.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[settings]
agent_id = "test/platform-script"
name = "Platform Test Script"
run_command = "python script.py"
run_command_linux = "python script.py"

[settings.environment]
COMMON_ENV = "$HOME/common-value"
SHARED_ENV = "common-script-value"

[settings.environment.windows]
SHARED_ENV = "windows-script-value"
PLATFORM_ENV = "%LOCALAPPDATA%\\RLBot5\\bots\\torch-archive"

[settings.environment.linux]
SHARED_ENV = "linux-script-value"
PLATFORM_ENV = "$HOME/.local/share/RLBot5/bots/torch-archive"

[details]
7 changes: 7 additions & 0 deletions RLBotCSTests/TestTomls/platform.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[match]

[[cars]]
config_file = "platform.bot.toml"

[[scripts]]
config_file = "platform.script.toml"
Loading