diff --git a/RLBotCS/ManagerTools/LaunchManager.cs b/RLBotCS/ManagerTools/LaunchManager.cs index ce11da6..4f89201 100644 --- a/RLBotCS/ManagerTools/LaunchManager.cs +++ b/RLBotCS/ManagerTools/LaunchManager.cs @@ -180,7 +180,7 @@ int rlbotSocketsPort if (script.Location != "") scriptProcess.StartInfo.WorkingDirectory = script.Location; - scriptProcess.StartInfo.EnvironmentVariables["RLBOT_GROUP_ID"] = script.AgentId; + scriptProcess.StartInfo.EnvironmentVariables["RLBOT_AGENT_ID"] = script.AgentId; scriptProcess.StartInfo.EnvironmentVariables["RLBOT_SERVER_PORT"] = rlbotSocketsPort.ToString(); diff --git a/RLBotCS/ManagerTools/PerfMonitor.cs b/RLBotCS/ManagerTools/PerfMonitor.cs index 84c41b1..67800a9 100644 --- a/RLBotCS/ManagerTools/PerfMonitor.cs +++ b/RLBotCS/ManagerTools/PerfMonitor.cs @@ -75,8 +75,8 @@ public void RenderSummary(Rendering rendering, GameState gameState, float deltaT var renderText = new String2DT() { - Y = 10f / 1920f, - X = 200f / 1080f, + X = 10f / Rendering.ResolutionWidthPixels, + Y = 200f / Rendering.ResolutionHeightPixels, Text = message, Foreground = TextColor, Background = BackColor, diff --git a/RLBotCS/ManagerTools/QuickChat.cs b/RLBotCS/ManagerTools/QuickChat.cs index f5d97a6..73711f9 100644 --- a/RLBotCS/ManagerTools/QuickChat.cs +++ b/RLBotCS/ManagerTools/QuickChat.cs @@ -71,7 +71,7 @@ public void RenderChats(Rendering rendering, GameState gameState) return; _hasUpdate = false; - int xVal = 10; + float yVal = 10f; List renderMessages = new(); foreach (var chat in _chats) @@ -82,8 +82,8 @@ public void RenderChats(Rendering rendering, GameState gameState) new() { Text = chat.Item2.Display, - Y = 10f / 1920f, - X = xVal / 1080f, + X = 10f / Rendering.ResolutionWidthPixels, + Y = yVal / Rendering.ResolutionHeightPixels, Scale = 1, Foreground = textColor, Background = BackgroundColor, @@ -95,7 +95,7 @@ public void RenderChats(Rendering rendering, GameState gameState) new RenderMessageT() { Variety = RenderTypeUnion.FromString2D(message), } ); - xVal += 20; + yVal += Rendering.FontHeightPixels; } if (renderMessages.Count > 0) diff --git a/RLBotCS/ManagerTools/Rendering.cs b/RLBotCS/ManagerTools/Rendering.cs index 8c01749..4fb9e1f 100644 --- a/RLBotCS/ManagerTools/Rendering.cs +++ b/RLBotCS/ManagerTools/Rendering.cs @@ -1,21 +1,28 @@ +using System.Text; using Bridge.Controller; using Bridge.State; using Bridge.TCP; using rlbot.flat; using RLBotCS.Conversion; +using Color = System.Drawing.Color; namespace RLBotCS.ManagerTools; public class Rendering(TcpMessenger tcpMessenger) { - private static int MaxClearsPerTick = 1024; + private const int MaxClearsPerTick = 1024; + + public const int ResolutionWidthPixels = 1920; + public const int ResolutionHeightPixels = 1080; + public const int FontWidthPixels = 10; + public const int FontHeightPixels = 20; private readonly RenderingSender _renderingSender = new(tcpMessenger); private readonly Dictionary>> _clientRenderTracker = []; private readonly Queue _RenderClearQueue = new(); - private ushort? RenderItem(RenderTypeUnion renderItem, GameState gameState) => + private ushort RenderItem(RenderTypeUnion renderItem, GameState gameState) => renderItem.Value switch { Line3DT { Start: var start, End: var end, Color: var color } @@ -69,9 +76,104 @@ public class Rendering(TcpMessenger tcpMessenger) (byte)vAlign, scale ), - _ => null + Rect2DT rect2Dt => SendRect2D(rect2Dt), + Rect3DT rect3Dt => SendRect3D(rect3Dt, gameState), + _ => throw new NotImplementedException("Unknown RenderMessage"), }; + private ushort SendRect2D(Rect2DT rect2Dt) + { + // Move rect left/up when width/height is negative + var adjustedX = + !rect2Dt.Centered && rect2Dt.Width < 0 ? rect2Dt.X - rect2Dt.Width : rect2Dt.X; + var adjustedY = + !rect2Dt.Centered && rect2Dt.Height < 0 ? rect2Dt.Y - rect2Dt.Height : rect2Dt.X; + + // Fake a filled rectangle using a string with colored background + var (text, scale) = MakeFakeRectangleString( + (int)Math.Abs(rect2Dt.Width * ResolutionWidthPixels), + (int)Math.Abs(rect2Dt.Height * ResolutionHeightPixels) + ); + + var hAlign = rect2Dt.Centered ? TextHAlign.Center : TextHAlign.Left; + var vAlign = rect2Dt.Centered ? TextHAlign.Center : TextHAlign.Left; + + return _renderingSender.AddText2D( + text, + adjustedX, + adjustedY, + Color.Transparent, // Foreground + FlatToModel.ToColor(rect2Dt.Color), // Background + (byte)hAlign, + (byte)vAlign, + scale + ); + } + + private ushort SendRect3D(Rect3DT rect3Dt, GameState gameState) + { + // Fake a filled rectangle using a string with colored background + var (text, scale) = MakeFakeRectangleString( + (int)Math.Abs(rect3Dt.Width * ResolutionWidthPixels), + (int)Math.Abs(rect3Dt.Height * ResolutionHeightPixels) + ); + + return _renderingSender.AddText3D( + text, + FlatToModel.ToRenderAnchor(rect3Dt.Anchor, gameState), + Color.Transparent, + FlatToModel.ToColor(rect3Dt.Color), + (byte)TextHAlign.Center, + (byte)TextVAlign.Center, + scale + ); + } + + /// + /// Computes a string in the shape of a rectangle. The rectangle has the given width and height in pixels when + /// scaled the string is scaled with returned scaling factor. We use this as a hack to created filled rectangles + /// for rectangle rendering. + /// + /// + private (string, float) MakeFakeRectangleString(int width, int height) + { + int Gcd(int a, int b) + { + // Greatest common divisor by Euclidean algorithm https://stackoverflow.com/a/41766138 + while (a != 0 && b != 0) + { + if (a > b) + a %= b; + else + b %= a; + } + + return a | b; + } + + // We use the greatest common divisor to simplify the fraction (width/height) + // minimizing the characters needed for the rectangle. + int gcd = Gcd(width, height); + int cols = (width / gcd) * (FontHeightPixels / FontWidthPixels); + int rows = height / gcd; + + StringBuilder str = new StringBuilder(cols * rows + rows); + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + str.Append(' '); + } + + if (r + 1 < rows) + { + str.Append('\n'); + } + } + + return (str.ToString(), gcd / (float)FontHeightPixels); + } + public void AddRenderGroup( int clientId, int renderId, @@ -86,8 +188,9 @@ GameState gameState List renderGroup = []; foreach (RenderMessageT renderItem in renderItems) - if (RenderItem(renderItem.Variety, gameState) is { } renderItemId) - renderGroup.Add(renderItemId); + { + renderGroup.Add(RenderItem(renderItem.Variety, gameState)); + } _renderingSender.Send(); diff --git a/RLBotCS/lib/Bridge.dll b/RLBotCS/lib/Bridge.dll index 98c0067..f1a9d42 100644 Binary files a/RLBotCS/lib/Bridge.dll and b/RLBotCS/lib/Bridge.dll differ diff --git a/flatbuffers-schema b/flatbuffers-schema index 15d09b4..a01a99d 160000 --- a/flatbuffers-schema +++ b/flatbuffers-schema @@ -1 +1 @@ -Subproject commit 15d09b465b0620ec3eac114d9996a673b478d258 +Subproject commit a01a99dedbe9f7b58e3f91191dbd20e90c476d84