From 5b921d2af63169c5617921ad1625c7b228654654 Mon Sep 17 00:00:00 2001 From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Fri, 5 Jun 2026 22:04:22 +0100 Subject: [PATCH 1/2] OpenDream side --- OpenDreamClient/Interface/BrowsePopup.cs | 25 ++-- .../Interface/Controls/ControlWindow.cs | 24 ++-- .../Interface/Controls/InterfaceControl.cs | 8 +- .../Interface/DreamInterfaceManager.cs | 124 ++++++++++++------ OpenDreamRuntime/DreamConnection.cs | 29 ++-- OpenDreamShared/Network/Messages/MsgBrowse.cs | 14 +- 6 files changed, 134 insertions(+), 90 deletions(-) diff --git a/OpenDreamClient/Interface/BrowsePopup.cs b/OpenDreamClient/Interface/BrowsePopup.cs index 4102bb17ff..e4c6b00204 100644 --- a/OpenDreamClient/Interface/BrowsePopup.cs +++ b/OpenDreamClient/Interface/BrowsePopup.cs @@ -17,18 +17,19 @@ internal sealed class BrowsePopup { public BrowsePopup( string name, Vector2i size, - IClydeWindow ownerWindow) { - WindowDescriptor popupWindowDescriptor = new WindowDescriptor(name, - new() { - new ControlDescriptorBrowser { - Id = new DMFPropertyString("browser"), - Size = new DMFPropertySize(size), - Anchor1 = new DMFPropertyPos(0, 0), - Anchor2 = new DMFPropertyPos(100, 100) - } - }) { - Size = new DMFPropertySize(size) - }; + IClydeWindow ownerWindow, + WindowDescriptor? descriptor = null) { + + WindowDescriptor popupWindowDescriptor = descriptor ?? new WindowDescriptor(name) { + Size = new DMFPropertySize(size) + }; + + popupWindowDescriptor.ControlDescriptors.Add(new ControlDescriptorBrowser { + Id = new DMFPropertyString("browser"), + Size = new DMFPropertySize(size), + Anchor1 = new DMFPropertyPos(0, 0), + Anchor2 = new DMFPropertyPos(100, 100), + }); WindowElement = new ControlWindow(popupWindowDescriptor); WindowElement.CreateChildControls(); diff --git a/OpenDreamClient/Interface/Controls/ControlWindow.cs b/OpenDreamClient/Interface/Controls/ControlWindow.cs index 17ba69b83c..c26eadf63c 100644 --- a/OpenDreamClient/Interface/Controls/ControlWindow.cs +++ b/OpenDreamClient/Interface/Controls/ControlWindow.cs @@ -1,9 +1,9 @@ -using System.Diagnostics.CodeAnalysis; -using OpenDreamShared.Interface.Descriptors; +using OpenDreamShared.Interface.Descriptors; using OpenDreamShared.Interface.DMF; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; +using System.Diagnostics.CodeAnalysis; namespace OpenDreamClient.Interface.Controls; @@ -50,7 +50,7 @@ protected override void UpdateElementDescriptor() { _menuContainer.Visible = false; } - if(!WindowDescriptor.IsPane.Value) + if (!WindowDescriptor.IsPane.Value) UpdateWindowAttributes(_myWindow); if (WindowDescriptor.IsDefault.Value) { @@ -62,16 +62,16 @@ protected override void UpdateElementDescriptor() { /// Closes the window if it is a child window. No effect if it is either a default window or a pane /// public void CloseChildWindow() { - if(_myWindow.osWindow is not null) + if (_myWindow.osWindow is not null) _myWindow.osWindow.Close(); } public OSWindow CreateWindow() { - if(_myWindow.osWindow is not null) + if (_myWindow.osWindow is not null) return _myWindow.osWindow; OSWindow window = new(); - if(UIElement.Parent is not null) + if (UIElement.Parent is not null) UIElement.Orphan(); window.Children.Add(UIElement); @@ -103,7 +103,7 @@ public OSWindow CreateWindow() { public void RegisterOnClydeWindow(IClydeWindow window) { // todo: listen for closed. - if(_myWindow.osWindow is not null){ + if (_myWindow.osWindow is not null) { _myWindow.osWindow.Close(); UIElement.Orphan(); } @@ -171,9 +171,9 @@ public void UpdateAnchorPosition(InterfaceControl control) { // Also update the anchor position for anything with a size of 0 foreach (var child in ChildControls) { if (child.UIElement.SetWidth == 0) - child.AnchorPosition = child.AnchorPosition with {X = _canvas.PixelWidth + child.Size.X}; + child.AnchorPosition = child.AnchorPosition with { X = _canvas.PixelWidth + child.Size.X }; if (child.UIElement.SetHeight == 0) - child.AnchorPosition = child.AnchorPosition with {Y = _canvas.PixelHeight + child.Size.Y}; + child.AnchorPosition = child.AnchorPosition with { Y = _canvas.PixelHeight + child.Size.Y }; } UpdateAnchors(); @@ -184,7 +184,7 @@ private void UpdateWindowAttributes((OSWindow? osWindow, IClydeWindow? clydeWind var (osWindow, clydeWindow) = windowRoot; //if our window is null or closed, we need to create a new one. Otherwise we need to update the existing one. - if(osWindow == null && clydeWindow == null) { + if (osWindow == null && clydeWindow == null) { CreateWindow(); return; //we return because CreateWindow() calls UpdateWindowAttributes() again. } @@ -206,8 +206,10 @@ private void UpdateWindowAttributes((OSWindow? osWindow, IClydeWindow? clydeWind if (osWindow != null && osWindow.ClydeWindow != null) { osWindow.ClydeWindow.IsVisible = WindowDescriptor.IsVisible.Value; + osWindow.ClydeWindow.IsTitleBarVisible = WindowDescriptor.TitleBar.Value; } else if (clydeWindow != null) { clydeWindow.IsVisible = WindowDescriptor.IsVisible.Value; + clydeWindow.IsTitleBarVisible = WindowDescriptor.TitleBar.Value; } } @@ -341,7 +343,7 @@ public override bool TryGetProperty(string property, [NotNullWhen(true)] out IDM public override void SetProperty(string property, string value, bool manualWinset = false) { switch (property) { case "size": - if (_myWindow.osWindow is {ClydeWindow: not null}) { + if (_myWindow.osWindow is { ClydeWindow: not null }) { var size = new DMFPropertySize(value); var uiScale = _myWindow.osWindow.UIScale; size.X = (int)(size.X * uiScale); // TODO: RT should probably do this itself diff --git a/OpenDreamClient/Interface/Controls/InterfaceControl.cs b/OpenDreamClient/Interface/Controls/InterfaceControl.cs index 8338aca88c..5c83867f3c 100644 --- a/OpenDreamClient/Interface/Controls/InterfaceControl.cs +++ b/OpenDreamClient/Interface/Controls/InterfaceControl.cs @@ -1,9 +1,9 @@ -using System.Diagnostics.CodeAnalysis; -using OpenDreamShared.Interface.Descriptors; +using OpenDreamShared.Interface.Descriptors; using OpenDreamShared.Interface.DMF; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; +using System.Diagnostics.CodeAnalysis; namespace OpenDreamClient.Interface.Controls; @@ -22,7 +22,7 @@ public abstract class InterfaceControl : InterfaceElement { /// public Vector2i AnchorPosition = Vector2i.Zero; - protected ControlDescriptor ControlDescriptor => (ControlDescriptor) ElementDescriptor; + protected ControlDescriptor ControlDescriptor => (ControlDescriptor)ElementDescriptor; protected readonly ControlWindow? Window; [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] @@ -45,7 +45,7 @@ protected override void UpdateElementDescriptor() { //transparent is default because it's white with 0 alpha, and DMF color can't have none-255 alpha StyleBox? styleBox = (ControlDescriptor.BackgroundColor.Value != Color.Transparent) - ? new StyleBoxFlat {BackgroundColor = ControlDescriptor.BackgroundColor.Value} + ? new StyleBoxFlat { BackgroundColor = ControlDescriptor.BackgroundColor.Value } : null; switch (UIElement) { diff --git a/OpenDreamClient/Interface/DreamInterfaceManager.cs b/OpenDreamClient/Interface/DreamInterfaceManager.cs index f1b0ab7b90..67df704641 100644 --- a/OpenDreamClient/Interface/DreamInterfaceManager.cs +++ b/OpenDreamClient/Interface/DreamInterfaceManager.cs @@ -1,28 +1,29 @@ -using System.IO; -using System.Text; -using System.Globalization; -using OpenDreamShared.Network.Messages; -using OpenDreamClient.Interface.Controls; -using OpenDreamShared.Interface.Descriptors; -using OpenDreamShared.Interface.DMF; +using OpenDreamClient.Interface.Controls; using OpenDreamClient.Interface.Prompts; using OpenDreamClient.Resources; using OpenDreamClient.Resources.ResourceTypes; using OpenDreamShared.Dream; +using OpenDreamShared.Interface.Descriptors; +using OpenDreamShared.Interface.DMF; +using OpenDreamShared.Network.Messages; using Robust.Client; using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.ContentPack; +using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown.Mapping; using Robust.Shared.Timing; using Robust.Shared.Utility; using SixLabors.ImageSharp; +using System.Globalization; +using System.IO; using System.Linq; -using Robust.Shared.Map; +using System.Text; namespace OpenDreamClient.Interface; @@ -203,7 +204,10 @@ private void RxBrowse(MsgBrowse pBrowse) { browser.SetFileSource(null); } } else if (pBrowse.HtmlSource != null) { - var htmlFileName = $"browse_{pBrowse.Window}_{_random.Next()}"; // TODO: Possible collisions and explicit file names + // TODO: Possible collisions and explicit file names + // pBrowse contains options, which may contain the 'file' property. When this is explicitly declared, instead of + // using a random htmlFileName, we should instead use that file instead. + var htmlFileName = $"browse_{pBrowse.Window}_{_random.Next()}"; ControlBrowser? outputBrowser = referencedElement as ControlBrowser; if (outputBrowser == null) { @@ -219,8 +223,32 @@ private void RxBrowse(MsgBrowse pBrowse) { break; } } else if (pBrowse.Window != null) { + + DMFParser? parser; + WindowDescriptor? descriptor = null; + + // Handle using options to set the initial properties of the window + if (pBrowse.Options != null + && (parser = ParseDmfParams(pBrowse.Options, out var CheckParserErrors)) != null + && parser.AttributesValues() is Dictionary attributes + && !CheckParserErrors()) { + + var mappingElement = new MappingDataNode(); + + mappingElement.Add("id", pBrowse.Window); + foreach (var attribute in attributes) { + mappingElement.Add(attribute.Key, attribute.Value); + } + descriptor = (WindowDescriptor?)_serializationManager.Read(typeof(WindowDescriptor), mappingElement); + + if (descriptor?.Size.X == 0 || descriptor?.Size.Y == 0) { + descriptor.Size.X = 480; + descriptor.Size.Y = 480; + } + } + // Creating a new popup - var popup = new BrowsePopup(pBrowse.Window, pBrowse.Size, _clyde.MainWindow); + var popup = new BrowsePopup(pBrowse.Window, (480, 480), _clyde.MainWindow, descriptor); popup.Closed += () => { Windows.Remove(pBrowse.Window); }; outputBrowser = popup.Browser; @@ -264,7 +292,7 @@ private void RxWinGet(MsgWinGet message) { MsgPromptResponse response = new() { PromptId = message.PromptId, Type = DreamValueType.Text, - Value = WinGet(message.ControlId, message.QueryValue, forceSnowflake:true) + Value = WinGet(message.ControlId, message.QueryValue, forceSnowflake: true) }; _netManager.ClientSendMessage(response); @@ -366,7 +394,7 @@ public void FrameUpdate(FrameEventArgs frameEventArgs) { } else if (Menus.TryGetValue(windowId, out var menu)) { if (menu.MenuElementsById.TryGetValue(elementId, out var menuElement)) return menuElement; - } else if(MacroSets.TryGetValue(windowId, out var macroSet)) { + } else if (MacroSets.TryGetValue(windowId, out var macroSet)) { if (macroSet.Macros.TryGetValue(elementId, out var macroElement)) return macroElement; } @@ -516,7 +544,7 @@ public void RunCommand(string fullCommand, bool repeating = false) { var result = HandleEmbeddedWinget(null, currentArg.ToString(), out var hadWinget); // 64x64 or 64,64 gets split into two "64 64" args - if (hadWinget && result.Split(['x', ',']) is {Length: 2} wingetSplit && + if (hadWinget && result.Split(['x', ',']) is { Length: 2 } wingetSplit && float.TryParse(wingetSplit[0], out _) && float.TryParse(wingetSplit[1], out _)) { args.Add(wingetSplit[0]); args.Add(wingetSplit[1]); @@ -535,7 +563,7 @@ public void RunCommand(string fullCommand, bool repeating = false) { var result = HandleEmbeddedWinget(null, currentArg.ToString(), out var hadWinget); // 64x64 or 64,64 gets split into two "64 64" args - if (hadWinget && result.Split(['x', ',']) is {Length: 2} wingetSplit && + if (hadWinget && result.Split(['x', ',']) is { Length: 2 } wingetSplit && float.TryParse(wingetSplit[0], out _) && float.TryParse(wingetSplit[1], out _)) { args.Add(wingetSplit[0]); args.Add(wingetSplit[1]); @@ -554,7 +582,7 @@ public void RunCommand(string fullCommand, bool repeating = false) { var result = HandleEmbeddedWinget(null, arg, out var hadWinget); // 64x64 or 64,64 gets split into two "64 64" args - if (hadWinget && result.Split(['x', ',']) is {Length: 2} wingetSplit && + if (hadWinget && result.Split(['x', ',']) is { Length: 2 } wingetSplit && float.TryParse(wingetSplit[0], out _) && float.TryParse(wingetSplit[1], out _)) { args.Add(wingetSplit[0]); args.Add(wingetSplit[1]); @@ -614,35 +642,36 @@ public string HandleEmbeddedWinget(string? controlId, string value, out bool had string result = value; int startPos = result.IndexOf("[[", StringComparison.Ordinal); - while(startPos > -1){ + while (startPos > -1) { int endPos = result.IndexOf("]]", startPos, StringComparison.Ordinal); - if(endPos == -1) + if (endPos == -1) break; - string inner = result.Substring(startPos+2, endPos-startPos-2); + string inner = result.Substring(startPos + 2, endPos - startPos - 2); string[] elementSplit = inner.Split('.'); string innerControlId = controlId ?? ""; - if(elementSplit.Length > 1){ - innerControlId = (string.IsNullOrEmpty(innerControlId) ? "" : innerControlId+".")+string.Join(".", elementSplit[..^1]); + if (elementSplit.Length > 1) { + innerControlId = (string.IsNullOrEmpty(innerControlId) ? "" : innerControlId + ".") + string.Join(".", elementSplit[..^1]); inner = elementSplit[^1]; } string innerResult = WinGet(innerControlId, inner); hadWinget = true; - result = result.Substring(0, startPos) + innerResult + result.Substring(endPos+2); + result = result.Substring(0, startPos) + innerResult + result.Substring(endPos + 2); startPos = result.IndexOf("[[", StringComparison.Ordinal); } return result; } - public void WinSet(string? controlId, string winsetParams) { + private DMFParser? ParseDmfParams(string dmfParams, out Func CheckErrors) { + CheckErrors = null!; DMFParser parser; - try{ - var lexer = new DMFLexer(winsetParams); + try { + var lexer = new DMFLexer(dmfParams); parser = new DMFParser(lexer, _serializationManager); } catch (Exception e) { _sawmill.Error($"Error parsing winset: {e}"); - return; + return null; } bool CheckParserErrors() { @@ -656,6 +685,17 @@ bool CheckParserErrors() { return true; } + CheckErrors = CheckParserErrors; + return parser; + } + + public void WinSet(string? controlId, string winsetParams) { + DMFParser? parser = ParseDmfParams(winsetParams, out var CheckParserErrors); + + if (parser == null) { + return; + } + if (string.IsNullOrEmpty(controlId)) { List winSets = parser.GlobalWinSet(); @@ -678,26 +718,26 @@ bool CheckParserErrors() { _sawmill.Error($"Invalid global winset \"{winsetParams}\""); } } else { - if(winSet.TrueStatements is not null) { + if (winSet.TrueStatements is not null) { InterfaceElement? conditionalElement = FindElementWithId(elementId); - if(conditionalElement is null) + if (conditionalElement is null) _sawmill.Error($"Invalid element on ternary condition \"{elementId}\""); else - if(conditionalElement.TryGetProperty(winSet.Attribute, out var conditionalCheckValue) && conditionalCheckValue.Equals(winSet.Value)) { - foreach(DMFWinSet statement in winSet.TrueStatements) { + if (conditionalElement.TryGetProperty(winSet.Attribute, out var conditionalCheckValue) && conditionalCheckValue.Equals(winSet.Value)) { + foreach (DMFWinSet statement in winSet.TrueStatements) { string statementElementId = statement.Element ?? elementId; InterfaceElement? statementElement = FindElementWithId(statementElementId); - if(statementElement is not null) { + if (statementElement is not null) { statementElement.SetProperty(statement.Attribute, HandleEmbeddedWinget(statementElementId, statement.Value, out _), manualWinset: true); } else { _sawmill.Error($"Invalid element on ternary \"{statementElementId}\""); } } - } else if (winSet.FalseStatements is not null){ - foreach(DMFWinSet statement in winSet.FalseStatements) { + } else if (winSet.FalseStatements is not null) { + foreach (DMFWinSet statement in winSet.FalseStatements) { string statementElementId = statement.Element ?? elementId; InterfaceElement? statementElement = FindElementWithId(statementElementId); - if(statementElement is not null) { + if (statementElement is not null) { statementElement.SetProperty(statement.Attribute, HandleEmbeddedWinget(statementElementId, statement.Value, out _), manualWinset: true); } else { _sawmill.Error($"Invalid element on ternary \"{statementElementId}\""); @@ -750,16 +790,16 @@ bool ParseAndTryGet(InterfaceElement element, string query, out string result) { //parse "as blah" from query if it's there string[] querySplit = query.Split(" as "); IDMFProperty propResult; - if(querySplit.Length != 2) //must be "thing as blah" or "thing". Anything else is invalid. - if(element.TryGetProperty(query, out propResult!)){ + if (querySplit.Length != 2) //must be "thing as blah" or "thing". Anything else is invalid. + if (element.TryGetProperty(query, out propResult!)) { result = forceJson ? propResult.AsJson() : forceSnowflake ? propResult.AsSnowflake() : propResult.AsRaw(); return true; } else { result = ""; return false; } - else{ - if(!element.TryGetProperty(querySplit[0], out propResult!)) { + else { + if (!element.TryGetProperty(querySplit[0], out propResult!)) { result = ""; return false; } @@ -772,7 +812,7 @@ bool ParseAndTryGet(InterfaceElement element, string query, out string result) { return true; } - switch(querySplit[1]){ + switch (querySplit[1]) { case "arg": result = propResult.AsArg(); break; @@ -812,12 +852,12 @@ string GetProperty(string elementId) { } var multiQuery = queryValue.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - if(multiQuery.Length > 1) { + if (multiQuery.Length > 1) { var result = ""; - foreach(var query in multiQuery) { + foreach (var query in multiQuery) { if (!ParseAndTryGet(element, query, out var queryResult)) _sawmill.Error($"Could not winget property {query} on {element.Id}"); - result += query+"="+queryResult + ";"; + result += query + "=" + queryResult + ";"; } return result.TrimEnd(';'); @@ -930,7 +970,7 @@ public void WinClone(string controlId, string cloneId) { private void Reset() { _uiManager.MainViewport.Visible = false; //close windows if they're open, and clear all child ui elements - foreach (var window in Windows.Values){ + foreach (var window in Windows.Values) { window.CloseChildWindow(); window.UIElement.RemoveAllChildren(); } diff --git a/OpenDreamRuntime/DreamConnection.cs b/OpenDreamRuntime/DreamConnection.cs index 52256253f9..3940365932 100644 --- a/OpenDreamRuntime/DreamConnection.cs +++ b/OpenDreamRuntime/DreamConnection.cs @@ -1,6 +1,3 @@ -using System.Threading.Tasks; -using System.Web; -using DMCompiler.Bytecode; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs.Native; @@ -11,6 +8,8 @@ using Robust.Shared.Enums; using Robust.Shared.Player; using SpaceWizards.Sodium; +using System.Threading.Tasks; +using System.Web; namespace OpenDreamRuntime; @@ -34,7 +33,8 @@ public sealed partial class DreamConnection { [ViewVariables] public DreamObjectClient? Client { get; private set; } [ViewVariables] public string Key { get; } - [ViewVariables] public DreamObjectMob? Mob { + [ViewVariables] + public DreamObjectMob? Mob { get => _mob; set { if (_mob != value) { @@ -66,7 +66,8 @@ [ViewVariables] public DreamObjectMob? Mob { } } - [ViewVariables] public DreamObjectMovable? Eye { + [ViewVariables] + public DreamObjectMovable? Eye { get; set { value?.IncRef(); @@ -158,7 +159,8 @@ public void UpdateStat() { } return DreamValue.Null; - } finally { + } + finally { _currentlyUpdatingStat = false; } }).Dispose(); @@ -448,7 +450,7 @@ public void BrowseResource(DreamResource resource, string filename) { } public void HandleBrowseResourceRequest(string filename) { - if(_permittedBrowseRscFiles.TryGetValue(filename, out var dreamResource)) { + if (_permittedBrowseRscFiles.TryGetValue(filename, out var dreamResource)) { var msg = new MsgBrowseResourceResponse() { Filename = filename, Data = dreamResource.ResourceData!, //honestly if this is null, something mega fucked up has happened and we should error hard @@ -462,7 +464,8 @@ public void HandleBrowseResourceRequest(string filename) { public void Browse(string? body, string? options) { string? window = null; - Vector2i size = (480, 480); + + List unhandledOptions = new List(); if (options != null) { foreach (string option in options.Split(',', ';', '&')) { @@ -475,19 +478,17 @@ public void Browse(string? body, string? options) { if (key == "window") { window = value; - } else if (key == "size") { - string[] sizeSeparated = value.Split("x", 2); - - size = (int.Parse(sizeSeparated[0]), int.Parse(sizeSeparated[1])); + } else { + unhandledOptions.Add(optionTrimmed); } } } } var msg = new MsgBrowse() { - Size = size, Window = window, - HtmlSource = body + HtmlSource = body, + Options = string.Join(';', unhandledOptions) }; Session?.Channel.SendMessage(msg); diff --git a/OpenDreamShared/Network/Messages/MsgBrowse.cs b/OpenDreamShared/Network/Messages/MsgBrowse.cs index cfe606464d..82731b8577 100644 --- a/OpenDreamShared/Network/Messages/MsgBrowse.cs +++ b/OpenDreamShared/Network/Messages/MsgBrowse.cs @@ -1,5 +1,4 @@ using Lidgren.Network; -using Robust.Shared.Maths; using Robust.Shared.Network; using Robust.Shared.Serialization; @@ -9,33 +8,34 @@ public sealed class MsgBrowse : NetMessage { public string? Window; public string? HtmlSource; - public Vector2i Size; + public string? Options; public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { var hasWindow = buffer.ReadBoolean(); var hasHtml = buffer.ReadBoolean(); + var hasOptions = buffer.ReadBoolean(); buffer.ReadPadBits(); if (hasWindow) Window = buffer.ReadString(); if (hasHtml) HtmlSource = buffer.ReadString(); - - Size = (buffer.ReadUInt16(), buffer.ReadUInt16()); + if (hasOptions) + Options = buffer.ReadString(); } public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { buffer.Write(Window != null); buffer.Write(HtmlSource != null); + buffer.Write(Options != null); buffer.WritePadBits(); if (Window != null) buffer.Write(Window); if (HtmlSource != null) buffer.Write(HtmlSource); - - buffer.Write((ushort) Size.X); - buffer.Write((ushort) Size.Y); + if (Options != null) + buffer.Write(Options); } } } From c33441401a21efaf81f4424e8e4b1c2513e164ea Mon Sep 17 00:00:00 2001 From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Sat, 6 Jun 2026 08:24:44 +0100 Subject: [PATCH 2/2] Fixes HTML windows not rendering at the correct size --- OpenDreamClient/Interface/DreamInterfaceManager.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/OpenDreamClient/Interface/DreamInterfaceManager.cs b/OpenDreamClient/Interface/DreamInterfaceManager.cs index 67df704641..4249dc2eba 100644 --- a/OpenDreamClient/Interface/DreamInterfaceManager.cs +++ b/OpenDreamClient/Interface/DreamInterfaceManager.cs @@ -226,6 +226,7 @@ private void RxBrowse(MsgBrowse pBrowse) { DMFParser? parser; WindowDescriptor? descriptor = null; + (int x, int y) size = (480, 480); // Handle using options to set the initial properties of the window if (pBrowse.Options != null @@ -242,13 +243,16 @@ private void RxBrowse(MsgBrowse pBrowse) { descriptor = (WindowDescriptor?)_serializationManager.Read(typeof(WindowDescriptor), mappingElement); if (descriptor?.Size.X == 0 || descriptor?.Size.Y == 0) { - descriptor.Size.X = 480; - descriptor.Size.Y = 480; + descriptor.Size.X = size.x; + descriptor.Size.Y = size.y; + } else if (descriptor != null) { + size.x = descriptor.Size.X; + size.y = descriptor.Size.Y; } } // Creating a new popup - var popup = new BrowsePopup(pBrowse.Window, (480, 480), _clyde.MainWindow, descriptor); + var popup = new BrowsePopup(pBrowse.Window, size, _clyde.MainWindow, descriptor); popup.Closed += () => { Windows.Remove(pBrowse.Window); }; outputBrowser = popup.Browser;