Compare commits

...

16 Commits

Author SHA1 Message Date
Pieter-Jan Briers
32fa6ebb53 Version: 136.0.2 2024-03-10 20:47:22 +01:00
Pieter-Jan Briers
27378d3620 Backport 859f150404
(cherry picked from commit 24d5c26fa6)
(cherry picked from commit b89d13d39ffa53b9ca687946c6b56e86449d50bd)
2024-03-10 20:47:22 +01:00
Vera Aguilera Puerto
dd12110c34 Version: 136.0.1 2023-07-12 08:55:01 +02:00
Vera Aguilera Puerto
a811cfc1a1 Changelog for CEF bugfix 2023-07-12 08:53:51 +02:00
Amy
229a45bea2 Enables debugging and error handling on Linux with CEF enabled (#4181)
Co-authored-by: amylizzle <amylizzle@users.noreply.github.com>
2023-07-12 08:42:30 +02:00
metalgearsloth
78376ccca1 Fix grid fixture warnings (#4180) 2023-07-10 18:09:18 +10:00
metalgearsloth
e4a1415627 Fix erroneous Vector2.Length call (#4178) 2023-07-10 17:53:54 +10:00
metalgearsloth
69589195e0 MapLoader perf stuff (#4179) 2023-07-10 17:53:46 +10:00
Leon Friedrich
ce3b92aea2 Try prevent infinite measure/arrange loops (#4176) 2023-07-10 17:36:22 +10:00
Leon Friedrich
5dc980ae92 Revert #3827 (#4177) 2023-07-10 17:35:55 +10:00
ElectroJr
6edeafeed1 Version: 136.0.0 2023-07-08 22:22:28 -04:00
Leon Friedrich
623aa6a0ae More StyleBox UiScale fixes. (#4175) 2023-07-09 12:19:42 +10:00
ElectroJr
1399b71572 Version: 135.0.0 2023-07-08 15:02:31 -04:00
eoineoineoin
bbf8827efd Make StyleBoxTexture respect UI zoom level (#4165) 2023-07-09 04:49:25 +10:00
ElectroJr
591c261ff5 Version: 134.0.0 2023-07-08 12:10:13 -04:00
TemporalOroboros
75f0cf9dd7 Ecs's UserInterfaceComponent. (#4079) 2023-07-09 02:02:28 +10:00
39 changed files with 538 additions and 421 deletions

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -54,6 +54,42 @@ END TEMPLATE-->
*None yet*
## 136.0.2
## 136.0.1
### Bugfixes
* Fixed debugging on Linux when CEF is enabled.
## 136.0.0
### New features
* Several more style box properties now scale with UI scale. Signature of some stylebox methods have been changed.
### Bugfixes
* Fixed OutputPanel scroll-bar not functioning properly.
## 135.0.0
### Breaking changes
* Style boxes now scale with the current UI scale. This affects how the the margins, padding, and style box textures are drawn and how controls are arranged. Various style box methods now need to be provided with the current UI scale.
## 134.0.0
### Breaking changes
* Several methods were moved out of the `UserInterface` components and into the UI system.
* The BUI constructor arguments have changed and now require an EntityUid to be given instead of a component.
## 133.0.0
### Breaking changes
@@ -66,7 +102,7 @@ END TEMPLATE-->
### Bugfixes
* Block some parent-child physics collisions.
* Static entities that are parented to other entities will no longer collide with their parent.
* Fix some miscellaneous doc comments and typos (e.g. PvsSystem and EntityManager).
* Fix ContentGetDirectoryEntries.

View File

@@ -85,12 +85,10 @@ namespace Robust.Client.WebView.Cef
_app = new RobustCefApp(_sawmill);
// We pass no main arguments...
CefRuntime.Initialize(new CefMainArgs(null), settings, _app, IntPtr.Zero);
// TODO CEF: After this point, debugging breaks. No, literally. My client crashes but ONLY with the debugger.
// I have tried using the DEBUG and RELEASE versions of libcef.so, stripped or non-stripped...
// And nothing seemed to work. Odd.
// So these arguments look like nonsense, but it turns out CEF is just *like that*.
// The first argument is literally nonsense, but it needs to be there as otherwise the second argument doesn't apply
// The second argument turns off CEF's bullshit error handling, which breaks dotnet's error handling.
CefRuntime.Initialize(new CefMainArgs(new string[]{"binary","--disable-in-process-stack-traces"}), settings, _app, IntPtr.Zero);
if (_cfg.GetCVar(WCVars.WebResProtocol))
{

View File

@@ -1,104 +1,19 @@
using System;
using System.Collections.Generic;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Robust.Client.GameObjects
{
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
public sealed class ClientUserInterfaceComponent : SharedUserInterfaceComponent, ISerializationHooks
public sealed class ClientUserInterfaceComponent : SharedUserInterfaceComponent
{
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntityNetworkManager _netMan = default!;
internal readonly Dictionary<Enum, BoundUserInterface> _openInterfaces =
new();
[ViewVariables]
internal readonly Dictionary<Enum, PrototypeData> _interfaces = new();
[ViewVariables]
public IEnumerable<BoundUserInterface> Interfaces => _openInterfaces.Values;
void ISerializationHooks.AfterDeserialization()
{
_interfaces.Clear();
foreach (var data in _interfaceData)
{
_interfaces[data.UiKey] = data;
}
}
internal void MessageReceived(BoundUIWrapMessage msg)
{
switch (msg.Message)
{
case OpenBoundInterfaceMessage _:
if (_openInterfaces.ContainsKey(msg.UiKey))
{
return;
}
OpenInterface(msg);
break;
case CloseBoundInterfaceMessage _:
Close(msg.UiKey, true);
break;
default:
if (_openInterfaces.TryGetValue(msg.UiKey, out var bi))
{
bi.InternalReceiveMessage(msg.Message);
}
break;
}
}
private void OpenInterface(BoundUIWrapMessage wrapped)
{
var data = _interfaces[wrapped.UiKey];
// TODO: This type should be cached, but I'm too lazy.
var type = _reflectionManager.LooseGetType(data.ClientType);
var boundInterface =
(BoundUserInterface) _dynamicTypeFactory.CreateInstance(type, new object[] {this, wrapped.UiKey});
boundInterface.Open();
_openInterfaces[wrapped.UiKey] = boundInterface;
var playerSession = _playerManager.LocalPlayer?.Session;
if(playerSession != null)
_entityManager.EventBus.RaiseLocalEvent(Owner, new BoundUIOpenedEvent(wrapped.UiKey, Owner, playerSession), true);
}
internal void Close(Enum uiKey, bool remoteCall)
{
if (!_openInterfaces.TryGetValue(uiKey, out var boundUserInterface))
{
return;
}
if (!remoteCall)
SendMessage(new CloseBoundInterfaceMessage(), uiKey);
_openInterfaces.Remove(uiKey);
boundUserInterface.Dispose();
var playerSession = _playerManager.LocalPlayer?.Session;
if(playerSession != null)
_entityManager.EventBus.RaiseLocalEvent(Owner, new BoundUIClosedEvent(uiKey, Owner, playerSession), true);
}
internal void SendMessage(BoundUserInterfaceMessage message, Enum uiKey)
{
_netMan.SendSystemNetworkMessage(new BoundUIWrapMessage(Owner, message, uiKey));
}
public readonly Dictionary<Enum, BoundUserInterface> OpenInterfaces = new();
}
/// <summary>
@@ -106,17 +21,22 @@ namespace Robust.Client.GameObjects
/// </summary>
public abstract class BoundUserInterface : IDisposable
{
protected ClientUserInterfaceComponent Owner { get; }
[Dependency] protected readonly IEntityManager EntMan = default!;
protected readonly UserInterfaceSystem UiSystem = default!;
public readonly Enum UiKey;
public EntityUid Owner { get; }
/// <summary>
/// The last received state object sent from the server.
/// </summary>
protected BoundUserInterfaceState? State { get; private set; }
protected BoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey)
protected BoundUserInterface(EntityUid owner, Enum uiKey)
{
IoCManager.InjectDependencies(this);
UiSystem = EntMan.System<UserInterfaceSystem>();
Owner = owner;
UiKey = uiKey;
}
@@ -148,7 +68,7 @@ namespace Robust.Client.GameObjects
/// </summary>
public void Close()
{
Owner.Close(UiKey, false);
UiSystem.TryCloseUi(Owner, UiKey);
}
/// <summary>
@@ -156,7 +76,7 @@ namespace Robust.Client.GameObjects
/// </summary>
public void SendMessage(BoundUserInterfaceMessage message)
{
Owner.SendMessage(message, UiKey);
UiSystem.SendUiMessage(this, message);
}
internal void InternalReceiveMessage(BoundUserInterfaceMessage message)

View File

@@ -2,25 +2,40 @@ using JetBrains.Annotations;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using System;
namespace Robust.Client.GameObjects
{
[UsedImplicitly]
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
{
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<BoundUIWrapMessage>(MessageReceived);
SubscribeLocalEvent<ClientUserInterfaceComponent, ComponentInit>(OnUserInterfaceInit);
SubscribeLocalEvent<ClientUserInterfaceComponent, ComponentShutdown>(OnUserInterfaceShutdown);
}
private void OnUserInterfaceInit(EntityUid uid, ClientUserInterfaceComponent component, ComponentInit args)
{
component._interfaces.Clear();
foreach (var data in component._interfaceData)
{
component._interfaces[data.UiKey] = data;
}
}
private void OnUserInterfaceShutdown(EntityUid uid, ClientUserInterfaceComponent component, ComponentShutdown args)
{
foreach (var bui in component.Interfaces)
foreach (var bui in component.OpenInterfaces.Values)
{
bui.Dispose();
}
@@ -29,25 +44,88 @@ namespace Robust.Client.GameObjects
private void MessageReceived(BoundUIWrapMessage ev)
{
var uid = ev.Entity;
if (!EntityManager.TryGetComponent<ClientUserInterfaceComponent>(uid, out var cmp))
if (!TryComp<ClientUserInterfaceComponent>(uid, out var cmp))
return;
var uiKey = ev.UiKey;
var message = ev.Message;
// This should probably not happen at this point, but better make extra sure!
if(_playerManager.LocalPlayer != null)
message.Session = _playerManager.LocalPlayer.Session;
message.Entity = uid;
message.UiKey = ev.UiKey;
message.UiKey = uiKey;
// Raise as object so the correct type is used.
RaiseLocalEvent(uid, (object)message, true);
cmp.MessageReceived(ev);
switch (message)
{
case OpenBoundInterfaceMessage _:
TryOpenUi(uid, uiKey, cmp);
break;
case CloseBoundInterfaceMessage _:
TryCloseUi(uid, uiKey, remoteCall: true, uiComp: cmp);
break;
default:
if (cmp.OpenInterfaces.TryGetValue(uiKey, out var bui))
bui.InternalReceiveMessage(message);
break;
}
}
internal void Send(BoundUIWrapMessage msg)
private bool TryOpenUi(EntityUid uid, Enum uiKey, ClientUserInterfaceComponent? uiComp = null)
{
RaiseNetworkEvent(msg);
if (!Resolve(uid, ref uiComp))
return false;
if (uiComp.OpenInterfaces.ContainsKey(uiKey))
return false;
var data = uiComp._interfaces[uiKey];
// TODO: This type should be cached, but I'm too lazy.
var type = _reflectionManager.LooseGetType(data.ClientType);
var boundInterface =
(BoundUserInterface) _dynamicTypeFactory.CreateInstance(type, new object[] {uid, uiKey});
boundInterface.Open();
uiComp.OpenInterfaces[uiKey] = boundInterface;
var playerSession = _playerManager.LocalPlayer?.Session;
if(playerSession != null)
RaiseLocalEvent(uid, new BoundUIOpenedEvent(uiKey, uid, playerSession), true);
return true;
}
internal bool TryCloseUi(EntityUid uid, Enum uiKey, bool remoteCall = false, ClientUserInterfaceComponent? uiComp = null)
{
if (!Resolve(uid, ref uiComp))
return false;
if (!uiComp.OpenInterfaces.TryGetValue(uiKey, out var boundUserInterface))
return false;
if (!remoteCall)
SendUiMessage(boundUserInterface, new CloseBoundInterfaceMessage());
uiComp.OpenInterfaces.Remove(uiKey);
boundUserInterface.Dispose();
var playerSession = _playerManager.LocalPlayer?.Session;
if(playerSession != null)
RaiseLocalEvent(uid, new BoundUIClosedEvent(uiKey, uid, playerSession), true);
return true;
}
internal void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
{
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, msg, bui.UiKey));
}
}
}

View File

@@ -47,7 +47,7 @@ namespace Robust.Client.GameObjects
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
_label.Visible = true;
_label.Text = $"Speed: {body.LinearVelocity.Length:0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Numerics;
using JetBrains.Annotations;
using Robust.Shared.Maths;
@@ -38,10 +38,16 @@ namespace Robust.Client.Graphics
_paddingTop = other._paddingTop;
}
/// <summary>
/// Minimum size, in virtual pixels.
/// </summary>
public Vector2 MinimumSize =>
new(GetContentMargin(Margin.Left) + GetContentMargin(Margin.Right),
GetContentMargin(Margin.Top) + GetContentMargin(Margin.Bottom));
/// <summary>
/// Left content margin, in virtual pixels.
/// </summary>
public float? ContentMarginLeftOverride
{
get => _contentMarginLeftOverride;
@@ -56,6 +62,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Top content margin, in virtual pixels.
/// </summary>
public float? ContentMarginTopOverride
{
get => _contentMarginTopOverride;
@@ -70,6 +79,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Right content margin, in virtual pixels.
/// </summary>
public float? ContentMarginRightOverride
{
get => _contentMarginRightOverride;
@@ -84,6 +96,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Bottom content margin, in virtual pixels.
/// </summary>
public float? ContentMarginBottomOverride
{
get => _contentMarginBottomOverride;
@@ -98,6 +113,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Left padding, in virtual pixels.
/// </summary>
public float PaddingLeft
{
get => _paddingLeft;
@@ -112,6 +130,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Bottom padding, in virtual pixels.
/// </summary>
public float PaddingBottom
{
get => _paddingBottom;
@@ -126,6 +147,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Right padding, in virtual pixels.
/// </summary>
public float PaddingRight
{
get => _paddingRight;
@@ -140,6 +164,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Top padding, in virtual pixels.
/// </summary>
public float PaddingTop
{
get => _paddingTop;
@@ -154,6 +181,9 @@ namespace Robust.Client.Graphics
}
}
/// <summary>
/// Padding, in virtual pixels.
/// </summary>
public Thickness Padding
{
set
@@ -166,11 +196,11 @@ namespace Robust.Client.Graphics
}
/// <summary>
/// Draw this style box to the screen at the specified coordinates.
/// Draw this style box to the screen at the specified coordinates. This is using physical pixels, not virtual pixels.
/// </summary>
/// <param name="handle"></param>
/// <param name="box"></param>
public void Draw(DrawingHandleScreen handle, UIBox2 box)
public void Draw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
{
box = new UIBox2(
box.Left + PaddingLeft,
@@ -179,11 +209,11 @@ namespace Robust.Client.Graphics
box.Bottom - PaddingBottom
);
DoDraw(handle, box);
DoDraw(handle, box, uiScale);
}
/// <summary>
/// Gets the offset from a margin of the box to where content should actually be drawn.
/// Gets the offset from a margin of the box to where content should actually be drawn. This is in virtual pixels.
/// </summary>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="margin"/> is a compound is not a single margin flag.
@@ -213,6 +243,9 @@ namespace Robust.Client.Graphics
return contentMargin + GetPadding(margin);
}
/// <summary>
/// Gets the padding. This is in virtual pixels.
/// </summary>
public float GetPadding(Margin margin)
{
switch (margin)
@@ -231,26 +264,27 @@ namespace Robust.Client.Graphics
}
/// <summary>
/// Returns the offsets of the content region of this box,
/// if this box is drawn from the given position.
/// Returns the offsets of the content region of this box when drawn from the given position. Input and
/// output positions are in real pixels, though virtual pixels can also be used if the ui scale is set to 1.
/// </summary>
public Vector2 GetContentOffset(Vector2 basePosition)
public Vector2 GetContentOffset(Vector2 basePosition, float uiScale)
{
return basePosition + new Vector2(GetContentMargin(Margin.Left), GetContentMargin(Margin.Top));
return basePosition + uiScale * new Vector2(GetContentMargin(Margin.Left), GetContentMargin(Margin.Top));
}
/// <summary>
/// Gets the box considered the "contents" of this style box, when drawn at a specific size.
/// Gets the box considered the "contents" of this style box, when drawn at a specific size. Input and output
/// boxes are in virtual pixels, though virtual pixels can also be used if the ui scale is set to 1.
/// </summary>
/// <exception cref="ArgumentException">
/// <paramref name="baseBox"/> is too small and the resultant box would have negative dimensions.
/// </exception>
public UIBox2 GetContentBox(UIBox2 baseBox)
public UIBox2 GetContentBox(UIBox2 baseBox, float uiScale)
{
var left = baseBox.Left + GetContentMargin(Margin.Left);
var top = baseBox.Top + GetContentMargin(Margin.Top);
var right = baseBox.Right - GetContentMargin(Margin.Right);
var bottom = baseBox.Bottom - GetContentMargin(Margin.Bottom);
var left = baseBox.Left + GetContentMargin(Margin.Left) * uiScale;
var top = baseBox.Top + GetContentMargin(Margin.Top) * uiScale;
var right = baseBox.Right - GetContentMargin(Margin.Right) * uiScale;
var bottom = baseBox.Bottom - GetContentMargin(Margin.Bottom) * uiScale;
return new UIBox2(left, top, right, bottom);
}
@@ -258,19 +292,22 @@ namespace Robust.Client.Graphics
/// <summary>
/// Gets the draw box, positioned at <paramref name="position"/>,
/// that envelops a box with the given dimensions perfectly given this box's content margins.
/// Input and output values are in physical pixels, though virtual pixels can also be used if the ui scale
/// is set to 1.
/// </summary>
/// <remarks>
/// It's basically a reverse <see cref="GetContentBox"/>.
/// </remarks>
/// <param name="position">The position at which the new box should be drawn.</param>
/// <param name="dimensions">The dimensions of the content box inside this new box.</param>
/// <param name="uiScale">Scales the content margin border size</param>
/// <returns>
/// A box that, when ran through <see cref="GetContentBox"/>,
/// has a content box of size <paramref name="dimensions"/>
/// </returns>
public UIBox2 GetEnvelopBox(Vector2 position, Vector2 dimensions)
public UIBox2 GetEnvelopBox(Vector2 position, Vector2 dimensions, float uiScale)
{
return UIBox2.FromDimensions(position, dimensions + MinimumSize);
return UIBox2.FromDimensions(position, dimensions + MinimumSize * uiScale);
}
public void SetContentMarginOverride(Margin margin, float value)
@@ -319,7 +356,13 @@ namespace Robust.Client.Graphics
}
}
protected abstract void DoDraw(DrawingHandleScreen handle, UIBox2 box);
/// <summary>
/// Draw the style box in the given UI Box.
/// </summary>
/// <param name="handle"></param>
/// <param name="box">The area to draw in, in physical pixels</param>
/// <param name="uiScale">The UI scale to use.</param>
protected abstract void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale);
protected virtual float GetDefaultContentMargin(Margin margin)
{

View File

@@ -4,7 +4,7 @@ namespace Robust.Client.Graphics
{
public sealed class StyleBoxEmpty : StyleBox
{
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box)
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
{
// It's empty what more do you want?
}

View File

@@ -7,11 +7,16 @@ namespace Robust.Client.Graphics
{
public Color BackgroundColor { get; set; }
public Color BorderColor { get; set; }
/// <summary>
/// Thickness of the border, in virtual pixels.
/// </summary>
public Thickness BorderThickness { get; set; }
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box)
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
{
var (btl, btt, btr, btb) = BorderThickness;
var thickness = BorderThickness.Scale(uiScale);
var (btl, btt, btr, btb) = thickness;
if (btl > 0)
handle.DrawRect(new UIBox2(box.Left, box.Top, box.Left + btl, box.Bottom), BorderColor);
@@ -24,7 +29,7 @@ namespace Robust.Client.Graphics
if (btb > 0)
handle.DrawRect(new UIBox2(box.Left, box.Bottom - btb, box.Right, box.Bottom), BorderColor);
handle.DrawRect(BorderThickness.Deflate(box), BackgroundColor);
handle.DrawRect(thickness.Deflate(box), BackgroundColor);
}
public StyleBoxFlat()

View File

@@ -38,21 +38,57 @@ namespace Robust.Client.Graphics
Texture = copy.Texture;
Modulate = copy.Modulate;
TextureScale = copy.TextureScale;
}
/// <summary>
/// Left expansion size, in virtual pixels.
/// </summary>
/// <remarks>
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
/// <see cref="TextureScale"/>.
/// </remarks>
public float ExpandMarginLeft { get; set; }
/// <summary>
/// Top expansion size, in virtual pixels.
/// </summary>
/// <remarks>
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
/// <see cref="TextureScale"/>.
/// </remarks>
public float ExpandMarginTop { get; set; }
/// <summary>
/// Bottom expansion size, in virtual pixels.
/// </summary>
/// <remarks>
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
/// <see cref="TextureScale"/>.
/// </remarks>
public float ExpandMarginBottom { get; set; }
/// <summary>
/// Right expansion size, in virtual pixels.
/// </summary>
/// <remarks>
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
/// <see cref="TextureScale"/>.
/// </remarks>
public float ExpandMarginRight { get; set; }
public StretchMode Mode { get; set; } = StretchMode.Stretch;
private float _patchMarginLeft;
// Distance of the left patch margin from the image. In texture space.
/// <summary>
/// Distance of the left patch margin from the image. In texture space.
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
/// </summary>
public float PatchMarginLeft
{
get => _patchMarginLeft;
@@ -69,7 +105,10 @@ namespace Robust.Client.Graphics
private float _patchMarginRight;
// Distance of the right patch margin from the image. In texture space.
/// <summary>
/// Distance of the right patch margin from the image. In texture space.
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
/// </summary>
public float PatchMarginRight
{
get => _patchMarginRight;
@@ -86,7 +125,10 @@ namespace Robust.Client.Graphics
private float _patchMarginTop;
// Distance of the top patch margin from the image. In texture space.
/// <summary>
/// Distance of the top patch margin from the image. In texture space.
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
/// </summary>
public float PatchMarginTop
{
get => _patchMarginTop;
@@ -103,7 +145,10 @@ namespace Robust.Client.Graphics
private float _patchMarginBottom;
// Distance of the bottom patch margin from the image. In texture space.
/// <summary>
/// Distance of the bottom patch margin from the image. In texture space.
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
/// </summary>
public float PatchMarginBottom
{
get => _patchMarginBottom;
@@ -122,7 +167,9 @@ namespace Robust.Client.Graphics
public Texture? Texture { get; set; }
// Applies an additional x/y scale to the teture
/// <summary>
/// Additional scaling to use when drawing the texture.
/// </summary>
public Vector2 TextureScale { get; set; } = Vector2.One;
public void SetPatchMargin(Margin margin, float value)
@@ -171,7 +218,7 @@ namespace Robust.Client.Graphics
}
}
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box)
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
{
if (Texture == null)
{
@@ -179,13 +226,13 @@ namespace Robust.Client.Graphics
}
box = new UIBox2(
box.Left - ExpandMarginLeft,
box.Top - ExpandMarginTop,
box.Right + ExpandMarginRight,
box.Bottom + ExpandMarginBottom);
box.Left - ExpandMarginLeft * uiScale,
box.Top - ExpandMarginTop * uiScale,
box.Right + ExpandMarginRight * uiScale,
box.Bottom + ExpandMarginBottom * uiScale);
var scaledMargin = new UIBox2(PatchMarginLeft * TextureScale.X, PatchMarginTop * TextureScale.Y,
PatchMarginRight * TextureScale.X, PatchMarginBottom * TextureScale.Y);
var scaledMargin = new UIBox2(PatchMarginLeft * TextureScale.X * uiScale, PatchMarginTop * TextureScale.Y * uiScale,
PatchMarginRight * TextureScale.X * uiScale, PatchMarginBottom * TextureScale.Y * uiScale);
if (PatchMarginLeft > 0)
{
@@ -204,7 +251,7 @@ namespace Robust.Client.Graphics
new UIBox2(0, scaledMargin.Top, scaledMargin.Left, box.Height - scaledMargin.Bottom)
.Translated(box.TopLeft);
DrawStretchingArea(handle, leftBox,
new UIBox2(0, PatchMarginTop, PatchMarginLeft, Texture.Height - PatchMarginBottom));
new UIBox2(0, PatchMarginTop, PatchMarginLeft, Texture.Height - PatchMarginBottom), uiScale);
}
if (PatchMarginBottom > 0)
@@ -239,7 +286,7 @@ namespace Robust.Client.Graphics
DrawStretchingArea(handle, rightBox,
new UIBox2(Texture.Width - PatchMarginRight, PatchMarginTop,
Texture.Width,
Texture.Height - PatchMarginBottom));
Texture.Height - PatchMarginBottom), uiScale);
}
if (PatchMarginBottom > 0)
@@ -261,7 +308,7 @@ namespace Robust.Client.Graphics
new UIBox2(scaledMargin.Left, 0, box.Width - scaledMargin.Right, scaledMargin.Top)
.Translated(box.TopLeft);
DrawStretchingArea(handle, topBox,
new UIBox2(PatchMarginLeft, 0, Texture.Width - PatchMarginRight, PatchMarginTop));
new UIBox2(PatchMarginLeft, 0, Texture.Width - PatchMarginRight, PatchMarginTop), uiScale);
}
if (PatchMarginBottom > 0)
@@ -275,7 +322,7 @@ namespace Robust.Client.Graphics
DrawStretchingArea(handle, bottomBox,
new UIBox2(PatchMarginLeft, Texture.Height - PatchMarginBottom,
Texture.Width - PatchMarginRight,
Texture.Height));
Texture.Height), uiScale);
}
// Draw center
@@ -284,11 +331,11 @@ namespace Robust.Client.Graphics
box.Height - scaledMargin.Bottom).Translated(box.TopLeft);
DrawStretchingArea(handle, centerBox, new UIBox2(PatchMarginLeft, PatchMarginTop, Texture.Width - PatchMarginRight,
Texture.Height - PatchMarginBottom));
Texture.Height - PatchMarginBottom), uiScale);
}
}
private void DrawStretchingArea(DrawingHandleScreen handle, UIBox2 area, UIBox2 texCoords)
private void DrawStretchingArea(DrawingHandleScreen handle, UIBox2 area, UIBox2 texCoords, float uiScale)
{
if (Mode == StretchMode.Stretch)
{
@@ -301,8 +348,8 @@ namespace Robust.Client.Graphics
// TODO: this is an insanely expensive way to do tiling, seriously.
// This should 100% be implemented in a shader instead.
var sectionWidth = texCoords.Width * TextureScale.X;
var sectionHeight = texCoords.Height * TextureScale.Y;
var sectionWidth = texCoords.Width * TextureScale.X * uiScale;
var sectionHeight = texCoords.Height * TextureScale.Y * uiScale;
var invScale = Vector2.One / TextureScale;
for (var x = area.Left; area.Right - x > 0; x += sectionWidth)
@@ -328,13 +375,13 @@ namespace Robust.Client.Graphics
switch (margin)
{
case Margin.Top:
return PatchMarginTop;
return PatchMarginTop * TextureScale.Y;
case Margin.Bottom:
return PatchMarginBottom;
return PatchMarginBottom * TextureScale.Y;
case Margin.Right:
return PatchMarginRight;
return PatchMarginRight * TextureScale.X;
case Margin.Left:
return PatchMarginLeft;
return PatchMarginLeft * TextureScale.X;
default:
throw new ArgumentOutOfRangeException(nameof(margin), margin, null);
}

View File

@@ -14,6 +14,7 @@ namespace Robust.Client.Physics
{
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
public bool EnableDebug
{
@@ -27,7 +28,7 @@ namespace Robust.Client.Physics
if (_enableDebug)
{
var overlay = new GridSplitNodeOverlay(EntityManager, _map, this);
var overlay = new GridSplitNodeOverlay(_map, this, _transform);
_overlay.AddOverlay(overlay);
RaiseNetworkEvent(new RequestGridNodesMessage());
}
@@ -39,7 +40,7 @@ namespace Robust.Client.Physics
}
}
private bool _enableDebug = false;
private bool _enableDebug;
private readonly Dictionary<EntityUid, Dictionary<Vector2i, List<List<Vector2i>>>> _nodes = new();
private readonly Dictionary<EntityUid, List<(Vector2, Vector2)>> _connections = new();
@@ -69,71 +70,76 @@ namespace Robust.Client.Physics
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private IEntityManager _entManager;
private IMapManager _mapManager;
private GridFixtureSystem _system;
private readonly IMapManager _mapManager;
private readonly GridFixtureSystem _system;
private readonly SharedTransformSystem _transform;
public GridSplitNodeOverlay(IEntityManager entManager, IMapManager mapManager, GridFixtureSystem system)
public GridSplitNodeOverlay(IMapManager mapManager, GridFixtureSystem system, SharedTransformSystem transform)
{
_entManager = entManager;
_mapManager = mapManager;
_system = system;
_transform = transform;
}
protected internal override void Draw(in OverlayDrawArgs args)
{
var worldHandle = args.WorldHandle;
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
foreach (var iGrid in _mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds))
{
// May not have received nodes yet.
if (!_system._nodes.TryGetValue(iGrid.Owner, out var nodes)) continue;
var state = (_system, _transform, args.WorldBounds, worldHandle);
var gridXform = xformQuery.GetComponent(iGrid.Owner);
worldHandle.SetTransform(gridXform.WorldMatrix);
var chunkEnumerator = iGrid.GetMapChunks(args.WorldBounds);
while (chunkEnumerator.MoveNext(out var chunk))
_mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds, ref state,
static (EntityUid uid, MapGridComponent grid,
ref (GridFixtureSystem system, SharedTransformSystem transform, Box2Rotated worldBounds, DrawingHandleWorld worldHandle) tuple) =>
{
if (!nodes.TryGetValue(chunk.Indices, out var chunkNodes)) continue;
// May not have received nodes yet.
if (!tuple.system._nodes.TryGetValue(uid, out var nodes))
return true;
for (var i = 0; i < chunkNodes.Count; i++)
tuple.worldHandle.SetTransform(tuple.transform.GetWorldMatrix(uid));
var chunkEnumerator = grid.GetMapChunks(tuple.worldBounds);
while (chunkEnumerator.MoveNext(out var chunk))
{
var group = chunkNodes[i];
var offset = chunk.Indices * chunk.ChunkSize;
var color = GetColor(chunk, i);
if (!nodes.TryGetValue(chunk.Indices, out var chunkNodes)) continue;
foreach (var index in group)
for (var i = 0; i < chunkNodes.Count; i++)
{
worldHandle.DrawRect(new Box2(offset + index, offset + index + 1).Enlarged(-0.1f), color);
var group = chunkNodes[i];
var offset = chunk.Indices * chunk.ChunkSize;
var color = GetColor(chunk, i);
foreach (var index in group)
{
tuple.worldHandle.DrawRect(new Box2(offset + index, offset + index + 1).Enlarged(-0.1f), color);
}
}
}
}
var connections = _system._connections[iGrid.Owner];
var connections = tuple.system._connections[uid];
foreach (var (start, end) in connections)
{
worldHandle.DrawLine(start, end, Color.Aquamarine);
}
}
foreach (var (start, end) in connections)
{
tuple.worldHandle.DrawLine(start, end, Color.Aquamarine);
}
static Color GetColor(MapChunk chunk, int index)
{
// Just want something that doesn't give similar indices at 0,0 but is also deterministic.
// Add an offset to yIndex so we at least have some colour that isn't grey at 0,0
var actualIndex = chunk.Indices.X * 20 + (chunk.Indices.Y + 20) * 35 + index * 50;
var red = (byte) (actualIndex % 255);
var green = (byte) (actualIndex * 20 % 255);
var blue = (byte) (actualIndex * 30 % 255);
return new Color(red, green, blue, 85);
}
return true;
}, true);
worldHandle.SetTransform(Matrix3.Identity);
}
private Color GetColor(MapChunk chunk, int index)
{
// Just want something that doesn't give similar indices at 0,0 but is also deterministic.
// Add an offset to yIndex so we at least have some colour that isn't grey at 0,0
var actualIndex = chunk.Indices.X * 20 + (chunk.Indices.Y + 20) * 35 + index * 50;
var red = (byte) (actualIndex % 255);
var green = (byte) (actualIndex * 20 % 255);
var blue = (byte) (actualIndex * 30 % 255);
return new Color(red, green, blue, 85);
}
}
}
}

View File

@@ -17,7 +17,7 @@
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" />
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
<PackageReference Include="NVorbis" Version="0.10.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" />

View File

@@ -32,6 +32,7 @@ namespace Robust.Client.UserInterface
private VAlignment _verticalAlignment = VAlignment.Stretch;
private Thickness _margin;
private bool _measuring;
private bool _arranging;
/// <summary>
/// The desired minimum size this control needs for layout to avoid cutting off content or such.
@@ -469,13 +470,12 @@ namespace Robust.Client.UserInterface
/// </summary>
public void InvalidateMeasure()
{
if (!IsMeasureValid)
if (!IsMeasureValid || _measuring)
return;
IsMeasureValid = false;
IsArrangeValid = false;
UserInterfaceManagerInternal.QueueMeasureUpdate(this);
InvalidateArrange();
}
/// <summary>
@@ -484,7 +484,7 @@ namespace Robust.Client.UserInterface
/// </summary>
public void InvalidateArrange()
{
if (!IsArrangeValid)
if (!IsArrangeValid || _arranging)
{
// Already queued for a layout update, don't bother.
return;
@@ -508,7 +508,16 @@ namespace Robust.Client.UserInterface
if (!IsMeasureValid || PreviousMeasure != availableSize)
{
IsMeasureValid = true;
var desired = MeasureCore(availableSize);
_measuring = true;
Vector2 desired;
try
{
desired = MeasureCore(availableSize);
}
finally
{
_measuring = false;
}
if (desired.X < 0 || desired.Y < 0 || !float.IsFinite(desired.X) || !float.IsFinite(desired.Y))
throw new InvalidOperationException("Invalid size returned from Measure()");
@@ -540,16 +549,7 @@ namespace Robust.Client.UserInterface
var constrained = ApplySizeConstraints(this, withoutMargin);
Vector2 measured;
try
{
_measuring = true;
measured = MeasureOverride(constrained);
}
finally
{
_measuring = false;
}
var measured = MeasureOverride(constrained);
if (!float.IsNaN(SetWidth))
{
@@ -604,14 +604,22 @@ namespace Robust.Client.UserInterface
/// </summary>
public void Arrange(UIBox2 finalRect)
{
if (!IsMeasureValid)
Measure(PreviousMeasure ?? finalRect.Size);
if (!IsArrangeValid || PreviousArrange != finalRect)
_arranging = true;
try
{
IsArrangeValid = true;
ArrangeCore(finalRect);
PreviousArrange = finalRect;
if (!IsMeasureValid)
Measure(PreviousMeasure ?? finalRect.Size);
if (!IsArrangeValid || PreviousArrange != finalRect)
{
IsArrangeValid = true;
ArrangeCore(finalRect);
PreviousArrange = finalRect;
}
}
finally
{
_arranging = false;
}
}

View File

@@ -148,7 +148,6 @@ namespace Robust.Client.UserInterface
internal void DoStyleUpdate()
{
_stylingDirty = false;
_styleProperties.Clear();
if (_stylesheetUpdateNeeded)
@@ -229,6 +228,10 @@ namespace Robust.Client.UserInterface
}
StylePropertiesChanged();
// Setting this at the end of the function to prevent style updates from ever re-queueing a style update,
// which would cause an infinite loop.
_stylingDirty = false;
}
protected virtual void StylePropertiesChanged()

View File

@@ -35,7 +35,7 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
var boxSize = ActualStyleBox.MinimumSize / UIScale;
var boxSize = ActualStyleBox.MinimumSize;
var childBox = Vector2.Max(availableSize - boxSize, Vector2.Zero);
var min = Vector2.Zero;
foreach (var child in Children)
@@ -49,11 +49,12 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
var contentBox = ActualStyleBox.GetContentBox(UIBox2.FromDimensions(Vector2.Zero, finalSize * UIScale));
var box = UIBox2.FromDimensions(Vector2.Zero, finalSize);
var contentBox = ActualStyleBox.GetContentBox(box, 1);
foreach (var child in Children)
{
child.ArrangePixel((UIBox2i) contentBox);
child.Arrange(contentBox);
}
return finalSize;
@@ -65,7 +66,7 @@ namespace Robust.Client.UserInterface.Controls
var style = ActualStyleBox;
var drawBox = PixelSizeBox;
style.Draw(handle, drawBox);
style.Draw(handle, drawBox, UIScale);
}
protected override void DrawModeChanged()

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
@@ -14,6 +14,7 @@ namespace Robust.Client.UserInterface.Controls
public class ItemList : Control, IList<ItemList.Item>
{
private bool _isAtBottom = true;
/// The size of all the child widgets, in pixels
private int _totalContentHeight;
private VScrollBar _scrollBar;
@@ -63,7 +64,7 @@ namespace Robust.Client.UserInterface.Controls
}
itemHeight = Math.Max(itemHeight, ActualFont.GetHeight(UIScale));
itemHeight += ActualItemBackground.MinimumSize.Y;
itemHeight += ActualItemBackground.MinimumSize.Y * UIScale;
_totalContentHeight += (int)Math.Ceiling(itemHeight);
}
@@ -333,7 +334,7 @@ namespace Robust.Client.UserInterface.Controls
var offset = -_scrollBar.Value;
listBg.Draw(handle, PixelSizeBox);
listBg.Draw(handle, PixelSizeBox, UIScale);
foreach (var item in _itemList)
{
@@ -354,16 +355,16 @@ namespace Robust.Client.UserInterface.Controls
}
itemHeight = Math.Max(itemHeight, font.GetHeight(UIScale));
itemHeight += ActualItemBackground.MinimumSize.Y;
itemHeight += ActualItemBackground.MinimumSize.Y * UIScale;
var region = UIBox2.FromDimensions(0, offset, PixelWidth, itemHeight);
item.Region = region;
if (region.Intersects(sizeBox))
{
bg.Draw(handle, item.Region.Value);
bg.Draw(handle, item.Region.Value, UIScale);
var contentBox = bg.GetContentBox(item.Region.Value);
var contentBox = bg.GetContentBox(item.Region.Value, UIScale);
var drawOffset = contentBox.TopLeft;
if (item.Icon != null)
{
@@ -420,7 +421,7 @@ namespace Robust.Client.UserInterface.Controls
var size = Vector2.Zero;
if (ActualBackground != null)
{
size += ActualBackground.MinimumSize / UIScale;
size += ActualBackground.MinimumSize;
}
return size;
@@ -496,19 +497,11 @@ namespace Robust.Client.UserInterface.Controls
return font.GetHeight(UIScale) * 2;
}
[Pure]
private UIBox2 _getContentBox()
{
var style = ActualBackground;
return style?.GetContentBox(SizeBox) ?? SizeBox;
}
protected override void Resized()
{
base.Resized();
var styleBoxSize = ActualBackground?.MinimumSize.Y ?? 0;
var styleBoxSize = (ActualBackground?.MinimumSize.Y ?? 0) * UIScale;
_scrollBar.Page = PixelSize.Y - styleBoxSize;
RecalculateContentHeight();
}
@@ -522,7 +515,7 @@ namespace Robust.Client.UserInterface.Controls
private void _updateScrollbarVisibility()
{
_scrollBar.Visible = _totalContentHeight + ActualBackground.MinimumSize.Y > PixelHeight;
_scrollBar.Visible = _totalContentHeight + ActualBackground.MinimumSize.Y > Height;
}
public abstract class ItemListEventArgs : EventArgs

View File

@@ -261,7 +261,7 @@ namespace Robust.Client.UserInterface.Controls
if (_mouseSelectingText)
{
var style = _getStyleBox();
var contentBox = style.GetContentBox(PixelSizeBox);
var contentBox = style.GetContentBox(PixelSizeBox, UIScale);
if (_lastMousePosition < contentBox.Left)
{
@@ -283,16 +283,14 @@ namespace Robust.Client.UserInterface.Controls
{
var font = _getFont();
var style = _getStyleBox();
return new Vector2(0, font.GetHeight(UIScale) / UIScale) + style.MinimumSize / UIScale;
return new Vector2(0, font.GetHeight(1.0f)) + style.MinimumSize;
}
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
var style = _getStyleBox();
_renderBox.ArrangePixel(
(UIBox2i) style.GetContentBox(
UIBox2.FromDimensions(Vector2.Zero, finalSize * UIScale)));
var box = UIBox2.FromDimensions(Vector2.Zero, finalSize);
_renderBox.Arrange(style.GetContentBox(box, 1));
return finalSize;
}
@@ -750,7 +748,7 @@ namespace Robust.Client.UserInterface.Controls
private int GetIndexAtPos(float horizontalPos)
{
var style = _getStyleBox();
var contentBox = style.GetContentBox(PixelSizeBox);
var contentBox = style.GetContentBox(PixelSizeBox, UIScale);
var clickPosX = horizontalPos * UIScale;
@@ -807,7 +805,7 @@ namespace Robust.Client.UserInterface.Controls
public float GetOffsetAtIndex(int index)
{
var style = _getStyleBox();
var contentBox = style.GetContentBox(PixelSizeBox);
var contentBox = style.GetContentBox(PixelSizeBox, UIScale);
var font = _getFont();
var i = 0;
@@ -897,7 +895,7 @@ namespace Robust.Client.UserInterface.Controls
{
base.Draw(handle);
_getStyleBox().Draw(handle, PixelSizeBox);
_getStyleBox().Draw(handle, PixelSizeBox, UIScale);
}
public sealed class LineEditEventArgs : EventArgs

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
@@ -124,7 +124,7 @@ namespace Robust.Client.UserInterface.Controls
var style = _getStyleBox();
var font = _getFont();
style?.Draw(handle, PixelSizeBox);
style?.Draw(handle, PixelSizeBox, UIScale);
var contentBox = _getContentBox();
var entryOffset = -_scrollBar.Value;
@@ -171,7 +171,7 @@ namespace Robust.Client.UserInterface.Controls
var styleBoxSize = _getStyleBox()?.MinimumSize.Y ?? 0;
_scrollBar.Page = PixelSize.Y - styleBoxSize;
_scrollBar.Page = UIScale * (Height - styleBoxSize);
_invalidateEntries();
}
@@ -224,6 +224,7 @@ namespace Robust.Client.UserInterface.Controls
[System.Diagnostics.Contracts.Pure]
private float _getScrollSpeed()
{
// The scroll speed depends on the UI scale because the scroll bar is working with physical pixels.
return GetScrollSpeed(_getFont(), UIScale);
}
@@ -231,7 +232,7 @@ namespace Robust.Client.UserInterface.Controls
private UIBox2 _getContentBox()
{
var style = _getStyleBox();
var box = style?.GetContentBox(PixelSizeBox) ?? PixelSizeBox;
var box = style?.GetContentBox(PixelSizeBox, UIScale) ?? PixelSizeBox;
box.Right = Math.Max(box.Left, box.Right - _scrollBar.DesiredPixelSize.X);
return box;
}

View File

@@ -16,12 +16,12 @@ namespace Robust.Client.UserInterface.Controls
base.Draw(handle);
var style = _getStyleBox();
style?.Draw(handle, PixelSizeBox);
style?.Draw(handle, PixelSizeBox, UIScale);
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
var styleSize = (_getStyleBox()?.MinimumSize ?? Vector2.Zero) / UIScale;
var styleSize = _getStyleBox()?.MinimumSize ?? Vector2.Zero;
var measureSize = Vector2.Max(availableSize - styleSize, Vector2.Zero);
var childSize = Vector2.Zero;
foreach (var child in Children)
@@ -35,13 +35,12 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
var pixelSize = finalSize * UIScale;
var ourSize = UIBox2.FromDimensions(Vector2.Zero, pixelSize);
var contentBox = _getStyleBox()?.GetContentBox(ourSize) ?? ourSize;
var ourSize = UIBox2.FromDimensions(Vector2.Zero, finalSize);
var contentBox = _getStyleBox()?.GetContentBox(ourSize, 1) ?? ourSize;
foreach (var child in Children)
{
child.ArrangePixel((UIBox2i) contentBox);
child.Arrange(contentBox);
}
return finalSize;

View File

@@ -63,7 +63,7 @@ namespace Robust.Client.UserInterface.Controls
base.Draw(handle);
var bg = _getBackground();
bg?.Draw(handle, PixelSizeBox);
bg?.Draw(handle, PixelSizeBox, UIScale);
var fg = _getForeground();
if (fg == null)
@@ -74,7 +74,7 @@ namespace Robust.Client.UserInterface.Controls
var size = PixelWidth * GetAsRatio() - minSize.X;
if (size > 0)
{
fg.Draw(handle, UIBox2.FromDimensions(0, 0, minSize.X + size, PixelHeight));
fg.Draw(handle, UIBox2.FromDimensions(0, 0, minSize.X + size, PixelHeight), UIScale);
}
}

View File

@@ -82,7 +82,7 @@ namespace Robust.Client.UserInterface.Controls
protected internal override void Draw(DrawingHandleScreen handle)
{
var styleBox = _getGrabberStyleBox();
styleBox?.Draw(handle, _getGrabberBox());
styleBox?.Draw(handle, _getGrabberBox(), UIScale);
}
protected internal override void MouseExited()

View File

@@ -223,23 +223,8 @@ namespace Robust.Client.UserInterface.Controls
var first = GetChild(0);
var second = GetChild(1);
var firstDesiredSize = firstMinSize ?? (Vertical ? first.DesiredSize.Y : first.DesiredSize.X);
var secondDesiredSize = secondMinSize ?? (Vertical ? second.DesiredSize.Y : second.DesiredSize.X);
var firstOrientedMinSize = Vertical ? first.MinSize.Y : first.MinSize.X;
var secondOrientedMinSize = Vertical ? second.MinSize.Y : second.MinSize.X;
if (firstOrientedMinSize > firstDesiredSize && firstOrientedMinSize != 0)
{
first.Measure(controlSize);
}
if (secondOrientedMinSize > secondDesiredSize && secondOrientedMinSize != 0)
{
second.Measure(controlSize);
}
firstMinSize = Vertical ? first.DesiredSize.Y : first.DesiredSize.X;
secondMinSize = Vertical ? second.DesiredSize.Y : second.DesiredSize.X;
firstMinSize ??= (Vertical ? first.DesiredSize.Y : first.DesiredSize.X);
secondMinSize ??= (Vertical ? second.DesiredSize.Y : second.DesiredSize.X);
var size = Vertical ? controlSize.Y : controlSize.X;
_splitStart = MathHelper.Clamp(_splitStart, firstMinSize.Value,

View File

@@ -143,7 +143,7 @@ namespace Robust.Client.UserInterface.Controls
var panel = _getPanel();
var panelBox = new UIBox2(0, headerSize, PixelWidth, PixelHeight);
panel?.Draw(handle, panelBox);
panel?.Draw(handle, panelBox, UIScale);
var font = _getFont();
var boxActive = _getTabBoxActive();
@@ -185,10 +185,10 @@ namespace Robust.Client.UserInterface.Controls
if (box != null)
{
var drawBox = box.GetEnvelopBox(topLeft, size);
var drawBox = box.GetEnvelopBox(topLeft, size, UIScale);
boxAdvance = drawBox.Width;
box.Draw(handle, drawBox);
contentBox = box.GetContentBox(drawBox);
box.Draw(handle, drawBox, UIScale);
contentBox = box.GetContentBox(drawBox, UIScale);
}
else
{
@@ -223,7 +223,7 @@ namespace Robust.Client.UserInterface.Controls
}
var panel = _getPanel();
var panelSize = (panel?.MinimumSize ?? Vector2.Zero) / UIScale;
var panelSize = (panel?.MinimumSize ?? Vector2.Zero);
var contentsSize = availableSize - headerSize - panelSize;
@@ -252,7 +252,7 @@ namespace Robust.Client.UserInterface.Controls
var contentBox = new UIBox2i(0, headerSize, (int) (finalSize.X * UIScale), (int) (finalSize.Y * UIScale));
if (panel != null)
{
contentBox = (UIBox2i) panel.GetContentBox(contentBox);
contentBox = (UIBox2i) panel.GetContentBox(contentBox, UIScale);
}
var control = GetChild(_currentTab);
@@ -322,6 +322,7 @@ namespace Robust.Client.UserInterface.Controls
}
}
// Returns the size of the header, in real pixels
[System.Diagnostics.Contracts.Pure]
private int _getHeaderSize()
{
@@ -333,8 +334,8 @@ namespace Robust.Client.UserInterface.Controls
var inactive = _getTabBoxInactive();
var font = _getFont();
var activeSize = active?.MinimumSize ?? Vector2.Zero;
var inactiveSize = inactive?.MinimumSize ?? Vector2.Zero;
var activeSize = (active?.MinimumSize ?? Vector2.Zero) * UIScale;
var inactiveSize = (inactive?.MinimumSize ?? Vector2.Zero) * UIScale;
headerSize = (int) MathF.Max(activeSize.Y, inactiveSize.Y);
headerSize += font.GetHeight(UIScale);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Numerics;
using Robust.Client.Graphics;
@@ -117,7 +117,7 @@ namespace Robust.Client.UserInterface.Controls
var background = _getBackground();
if (background != null)
{
position -= background.GetContentOffset(Vector2.Zero);
position -= background.GetContentOffset(Vector2.Zero, UIScale);
}
var vOffset = -_scrollBar.Value;
@@ -187,8 +187,8 @@ namespace Robust.Client.UserInterface.Controls
if (background != null)
{
background.Draw(handle, PixelSizeBox);
var (bho, bvo) = background.GetContentOffset(Vector2.Zero);
background.Draw(handle, PixelSizeBox, UIScale);
var (bho, bvo) = background.GetContentOffset(Vector2.Zero, UIScale);
vOffset += bvo;
hOffset += bho;
}
@@ -214,12 +214,13 @@ namespace Robust.Client.UserInterface.Controls
var selected = item.Index == _selectedIndex;
if (selected)
{
itemSelected.Draw(handle, UIBox2.FromDimensions(hOffset, vOffset, PixelWidth - hOffset, itemHeight));
var imageTarget = UIBox2.FromDimensions(hOffset, vOffset, PixelWidth - hOffset, itemHeight);
itemSelected.Draw(handle, imageTarget, UIScale);
}
if (!string.IsNullOrWhiteSpace(item.Text))
{
var offset = itemSelected.GetContentOffset(Vector2.Zero);
var offset = itemSelected.GetContentOffset(Vector2.Zero, UIScale);
var baseLine = offset + new Vector2(hOffset, vOffset + font.GetAscent(UIScale));
foreach (var rune in item.Text.EnumerateRunes())
{

View File

@@ -152,12 +152,17 @@ internal sealed partial class UserInterfaceManager
_styleUpdateQueue.Enqueue(control);
}
/// <summary>
/// Queues a control so that it gets remeasured in the next frame update. Does not queue an arrange update.
/// </summary>
public void QueueMeasureUpdate(Control control)
{
_measureUpdateQueue.Enqueue(control);
_arrangeUpdateQueue.Enqueue(control);
}
/// <summary>
/// Queues a control so that it gets rearranged in the next frame update. Does not queue a measure update.
/// </summary>
public void QueueArrangeUpdate(Control control)
{
_arrangeUpdateQueue.Enqueue(control);

View File

@@ -38,6 +38,7 @@ internal sealed partial class UserInterfaceManager
newRoot.StyleSheetUpdate();
newRoot.InvalidateMeasure();
QueueMeasureUpdate(newRoot);
QueueArrangeUpdate(newRoot);
if (window.IsFocused)
FocusRoot(newRoot);

View File

@@ -26,6 +26,7 @@ using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Client.UserInterface
@@ -54,6 +55,12 @@ namespace Robust.Client.UserInterface
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IRuntimeLog _runtime = default!;
/// <summary>
/// Upper limit on the number of times that controls can be measured / arranged each tick before being deferred
/// to the next frame update. This is just meant to prevent infinite loops from completely locking up the UI.
/// </summary>
public const int ControlUpdateLimit = 25_000;
[ViewVariables] public InterfaceTheme ThemeDefaults { get; private set; } = default!;
[ViewVariables]
public Stylesheet? Stylesheet
@@ -220,6 +227,12 @@ namespace Robust.Client.UserInterface
var total = 0;
while (_styleUpdateQueue.Count != 0)
{
if (total >= ControlUpdateLimit)
{
_sawmillUI.Warning($"Hit style update limit. Queued: {_styleUpdateQueue.Count}. Next in queue: {_styleUpdateQueue.Peek()}. Parent: {_styleUpdateQueue.Peek().Parent}");
break;
}
var control = _styleUpdateQueue.Dequeue();
if (control.Disposed)
@@ -237,12 +250,20 @@ namespace Robust.Client.UserInterface
var total = 0;
while (_measureUpdateQueue.Count != 0)
{
if (total >= ControlUpdateLimit)
{
_sawmillUI.Warning($"Hit measure update limit. Queued: {_measureUpdateQueue.Count}. Next in queue: {_measureUpdateQueue.Peek()}. Parent: {_measureUpdateQueue.Peek().Parent}");
break;
}
var control = _measureUpdateQueue.Dequeue();
if (control.Disposed)
continue;
RunMeasure(control);
if (!control.IsMeasureValid && control.IsInsideTree)
_sawmillUI.Warning($"Control's measure is invalid after measuring. Control: {control}. Parent: {control.Parent}.");
total += 1;
}
@@ -254,12 +275,19 @@ namespace Robust.Client.UserInterface
var total = 0;
while (_arrangeUpdateQueue.Count != 0)
{
if (total >= ControlUpdateLimit)
{
_sawmillUI.Warning($"Hit arrange update limit. Queued: {_arrangeUpdateQueue.Count}. Next in queue: {_arrangeUpdateQueue.Peek()}. Parent: {_arrangeUpdateQueue.Peek().Parent}");
break;
}
var control = _arrangeUpdateQueue.Dequeue();
if (control.Disposed)
continue;
RunArrange(control);
if (!control.IsArrangeValid && control.IsInsideTree)
_sawmillUI.Warning($"Control's arrangement is invalid after arranging. Control: {control}. Parent: {control.Parent}.");
total += 1;
}

View File

@@ -3,8 +3,7 @@ using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using static Robust.Shared.GameObjects.SharedUserInterfaceComponent;
namespace Robust.Server.GameObjects
@@ -16,22 +15,10 @@ namespace Robust.Server.GameObjects
/// <seealso cref="BoundUserInterface"/>
[PublicAPI]
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
public sealed class ServerUserInterfaceComponent : SharedUserInterfaceComponent, ISerializationHooks
public sealed class ServerUserInterfaceComponent : SharedUserInterfaceComponent
{
internal readonly Dictionary<Enum, BoundUserInterface> _interfaces =
new();
public IReadOnlyDictionary<Enum, BoundUserInterface> Interfaces => _interfaces;
void ISerializationHooks.AfterDeserialization()
{
_interfaces.Clear();
foreach (var prototypeData in _interfaceData)
{
_interfaces[prototypeData.UiKey] = new BoundUserInterface(prototypeData, this);
}
}
[ViewVariables]
public readonly Dictionary<Enum, BoundUserInterface> Interfaces = new();
}
[RegisterComponent]
@@ -49,8 +36,7 @@ namespace Robust.Server.GameObjects
public float InteractionRangeSqrd;
public Enum UiKey { get; }
public ServerUserInterfaceComponent Component { get; }
public EntityUid Owner => Component.Owner;
public EntityUid Owner { get; }
internal readonly HashSet<IPlayerSession> _subscribedSessions = new();
internal BoundUIWrapMessage? LastStateMsg;
@@ -66,72 +52,15 @@ namespace Robust.Server.GameObjects
/// </summary>
public IReadOnlySet<IPlayerSession> SubscribedSessions => _subscribedSessions;
[Obsolete("Use system events")]
public event Action<ServerBoundUserInterfaceMessage>? OnReceiveMessage;
public BoundUserInterface(PrototypeData data, ServerUserInterfaceComponent owner)
public BoundUserInterface(PrototypeData data, EntityUid owner)
{
RequireInputValidation = data.RequireInputValidation;
UiKey = data.UiKey;
Component = owner;
Owner = owner;
// One Abs(), because negative values imply no limit
InteractionRangeSqrd = data.InteractionRange * MathF.Abs(data.InteractionRange);
}
[Obsolete("Use UserInterfaceSystem")]
public void SetState(BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<UserInterfaceSystem>().SetUiState(this, state, session, clearOverrides);
}
[Obsolete("Use UserInterfaceSystem")]
public void Toggle(IPlayerSession session)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<UserInterfaceSystem>().ToggleUi(this, session);
}
[Obsolete("Use UserInterfaceSystem")]
public bool Open(IPlayerSession session)
{
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<UserInterfaceSystem>().OpenUi(this, session);
}
[Obsolete("Use UserInterfaceSystem")]
public bool Close(IPlayerSession session)
{
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<UserInterfaceSystem>().CloseUi(this, session);
}
[Obsolete("Use UserInterfaceSystem")]
public void CloseAll()
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<UserInterfaceSystem>().CloseAll(this);
}
[Obsolete("Just check SubscribedSessions.Contains")]
public bool SessionHasOpen(IPlayerSession session)
{
if (session == null) throw new ArgumentNullException(nameof(session));
return _subscribedSessions.Contains(session);
}
[Obsolete("Use UserInterfaceSystem")]
public void SendMessage(BoundUserInterfaceMessage message)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<UserInterfaceSystem>().SendUiMessage(this, message);
}
[Obsolete("Use UserInterfaceSystem")]
public void SendMessage(BoundUserInterfaceMessage message, IPlayerSession session)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<UserInterfaceSystem>().TrySendUiMessage(this, message, session);
}
internal void InvokeOnReceiveMessage(ServerBoundUserInterfaceMessage message)
{
OnReceiveMessage?.Invoke(message);
}
}
[PublicAPI]

View File

@@ -424,12 +424,16 @@ public sealed class MapLoaderSystem : EntitySystem
}
var entities = (SequenceDataNode) metaDef["entities"];
EntityPrototype? proto = null;
if (type != null)
_prototypeManager.TryIndex(type, out proto);
foreach (var entityDef in entities.Cast<MappingDataNode>())
{
var uid = entityDef.Get<ValueDataNode>("uid").AsInt();
var entity = _serverEntityManager.AllocEntity(type);
var entity = _serverEntityManager.AllocEntity(proto);
data.Entities.Add(entity);
data.UidEntityMap.Add(uid, entity);
data.EntitiesToDeserialize.Add(entity, entityDef);
@@ -462,11 +466,13 @@ public sealed class MapLoaderSystem : EntitySystem
}
else if (ev.RenamedPrototypes.TryGetValue(typeNode.Value, out var newType))
{
entity = _serverEntityManager.AllocEntity(newType);
_prototypeManager.TryIndex<EntityPrototype>(newType, out var prototype);
entity = _serverEntityManager.AllocEntity(prototype);
}
else
{
entity = _serverEntityManager.AllocEntity(typeNode.Value);
_prototypeManager.TryIndex<EntityPrototype>(typeNode.Value, out var prototype);
entity = _serverEntityManager.AllocEntity(prototype);
}
}
else

View File

@@ -14,13 +14,12 @@ namespace Robust.Server.GameObjects
[UsedImplicitly]
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
{
[Dependency] private readonly IPlayerManager _playerMan = default!;
[Dependency] private readonly TransformSystem _xformSys = default!;
private readonly List<IPlayerSession> _sessionCache = new();
private Dictionary<IPlayerSession, List<BoundUserInterface>> _openInterfaces = new();
[Dependency] private readonly IPlayerManager _playerMan = default!;
private readonly Dictionary<IPlayerSession, List<BoundUserInterface>> _openInterfaces = new();
/// <inheritdoc />
public override void Initialize()
@@ -28,6 +27,7 @@ namespace Robust.Server.GameObjects
base.Initialize();
SubscribeNetworkEvent<BoundUIWrapMessage>(OnMessageReceived);
SubscribeLocalEvent<ServerUserInterfaceComponent, ComponentInit>(OnUserInterfaceInit);
SubscribeLocalEvent<ServerUserInterfaceComponent, ComponentShutdown>(OnUserInterfaceShutdown);
_playerMan.PlayerStatusChanged += OnPlayerStatusChanged;
}
@@ -53,6 +53,16 @@ namespace Robust.Server.GameObjects
}
}
private void OnUserInterfaceInit(EntityUid uid, ServerUserInterfaceComponent component, ComponentInit args)
{
component.Interfaces.Clear();
foreach (var prototypeData in component._interfaceData)
{
component.Interfaces[prototypeData.UiKey] = new BoundUserInterface(prototypeData, uid);
}
}
private void OnUserInterfaceShutdown(EntityUid uid, ServerUserInterfaceComponent component, ComponentShutdown args)
{
if (!TryComp(uid, out ActiveUserInterfaceComponent? activeUis))
@@ -73,15 +83,15 @@ namespace Robust.Server.GameObjects
if (!TryComp(uid, out ServerUserInterfaceComponent? uiComp) || args.SenderSession is not IPlayerSession session)
return;
if (!uiComp._interfaces.TryGetValue(msg.UiKey, out var ui))
if (!uiComp.Interfaces.TryGetValue(msg.UiKey, out var ui))
{
Log.Debug("Got BoundInterfaceMessageWrapMessage for unknown UI key: {0}", msg.UiKey);
Log.Debug($"Got BoundInterfaceMessageWrapMessage for unknown UI key: {msg.UiKey}");
return;
}
if (!ui.SubscribedSessions.Contains(session))
{
Log.Debug($"UI {msg.UiKey} got BoundInterfaceMessageWrapMessage from a client who was not subscribed: {session}", msg.UiKey);
Log.Debug($"UI {msg.UiKey} got BoundInterfaceMessageWrapMessage from a client who was not subscribed: {session}");
return;
}
@@ -109,11 +119,6 @@ namespace Robust.Server.GameObjects
// Raise as object so the correct type is used.
RaiseLocalEvent(uid, (object)message, true);
// Once we have populated our message's wrapped message, we will wrap it up into a message that can be sent
// to old component-code.
var WrappedUnwrappedMessageMessageMessage = new ServerBoundUserInterfaceMessage(message, session);
ui.InvokeOnReceiveMessage(WrappedUnwrappedMessageMessageMessage);
}
/// <inheritdoc />
@@ -194,7 +199,7 @@ namespace Robust.Server.GameObjects
private void ActivateInterface(BoundUserInterface ui)
{
EnsureComp<ActiveUserInterfaceComponent>(ui.Component.Owner).Interfaces.Add(ui);
EnsureComp<ActiveUserInterfaceComponent>(ui.Owner).Interfaces.Add(ui);
}
#region Get BUI
@@ -203,7 +208,7 @@ namespace Robust.Server.GameObjects
if (!Resolve(uid, ref ui))
return false;
return ui._interfaces.ContainsKey(uiKey);
return ui.Interfaces.ContainsKey(uiKey);
}
public BoundUserInterface GetUi(EntityUid uid, Enum uiKey, ServerUserInterfaceComponent? ui = null)
@@ -211,7 +216,7 @@ namespace Robust.Server.GameObjects
if (!Resolve(uid, ref ui))
throw new InvalidOperationException($"Cannot get {typeof(BoundUserInterface)} from an entity without {typeof(ServerUserInterfaceComponent)}!");
return ui._interfaces[uiKey];
return ui.Interfaces[uiKey];
}
public BoundUserInterface? GetUiOrNull(EntityUid uid, Enum uiKey, ServerUserInterfaceComponent? ui = null)
@@ -224,7 +229,7 @@ namespace Robust.Server.GameObjects
{
bui = null;
return Resolve(uid, ref ui, false) && ui._interfaces.TryGetValue(uiKey, out bui);
return Resolve(uid, ref ui, false) && ui.Interfaces.TryGetValue(uiKey, out bui);
}
/// <summary>
@@ -294,9 +299,9 @@ namespace Robust.Server.GameObjects
/// The player session to send this new state to.
/// Set to null for sending it to every subscribed player session.
/// </param>
public void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
public static void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
{
var msg = new BoundUIWrapMessage(bui.Component.Owner, new UpdateBoundStateMessage(state), bui.UiKey);
var msg = new BoundUIWrapMessage(bui.Owner, new UpdateBoundStateMessage(state), bui.UiKey);
if (session == null)
{
bui.LastStateMsg = msg;
@@ -356,9 +361,9 @@ namespace Robust.Server.GameObjects
return false;
_openInterfaces.GetOrNew(session).Add(bui);
RaiseLocalEvent(bui.Component.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Component.Owner, session));
RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session));
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Component.Owner, new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
// Fun fact, clients needs to have BUIs open before they can receive the state.....
if (bui.LastStateMsg != null)
@@ -387,14 +392,14 @@ namespace Robust.Server.GameObjects
if (!bui._subscribedSessions.Remove(session))
return false;
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Component.Owner, new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
CloseShared(bui, session, activeUis);
return true;
}
private void CloseShared(BoundUserInterface bui, IPlayerSession session, ActiveUserInterfaceComponent? activeUis = null)
{
var owner = bui.Component.Owner;
var owner = bui.Owner;
bui._subscribedSessions.Remove(session);
bui.PlayerStateOverrides.Remove(session);
@@ -404,7 +409,7 @@ namespace Robust.Server.GameObjects
RaiseLocalEvent(owner, new BoundUIClosedEvent(bui.UiKey, owner, session));
if (bui._subscribedSessions.Count == 0)
DeactivateInterface(bui.Component.Owner, bui, activeUis);
DeactivateInterface(bui.Owner, bui, activeUis);
}
/// <summary>
@@ -465,7 +470,7 @@ namespace Robust.Server.GameObjects
/// </summary>
public void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage message)
{
var msg = new BoundUIWrapMessage(bui.Component.Owner, message, bui.UiKey);
var msg = new BoundUIWrapMessage(bui.Owner, message, bui.UiKey);
foreach (var session in bui.SubscribedSessions)
{
RaiseNetworkEvent(msg, session.ConnectedClient);
@@ -491,7 +496,7 @@ namespace Robust.Server.GameObjects
if (!bui.SubscribedSessions.Contains(session))
return false;
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Component.Owner, message, bui.UiKey), session.ConnectedClient);
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, message, bui.UiKey), session.ConnectedClient);
return true;
}

View File

@@ -8,7 +8,7 @@ namespace Robust.Server.GameObjects
// These methods are used by the map loader to do multi-stage entity construction during map load.
// I would recommend you refer to the MapLoader for usage.
EntityUid AllocEntity(string? prototypeName, EntityUid uid = default);
EntityUid AllocEntity(EntityPrototype? prototype, EntityUid uid = default);
void FinishEntityLoad(EntityUid entity, IEntityLoadContext? context = null);

View File

@@ -54,9 +54,9 @@ namespace Robust.Server.GameObjects
base.Initialize();
}
EntityUid IServerEntityManagerInternal.AllocEntity(string? prototypeName, EntityUid uid)
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype, EntityUid uid)
{
return AllocEntity(prototypeName, out _, uid);
return AllocEntity(prototype, out _, uid);
}
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, IEntityLoadContext? context)

View File

@@ -40,6 +40,11 @@ namespace Robust.Shared.Maths
Right = right;
Bottom = bottom;
}
public Thickness Scale(float scale)
{
return new Thickness(Left * scale, Top * scale, Right * scale, Bottom * scale);
}
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
public readonly bool Equals(Thickness other)

View File

@@ -675,29 +675,20 @@ namespace Robust.Shared.GameObjects
/// Allocates an entity and stores it but does not load components or do initialization.
/// </summary>
private protected EntityUid AllocEntity(
string? prototypeName,
EntityPrototype? prototype,
out MetaDataComponent metadata,
EntityUid uid = default)
{
EntityPrototype? prototype = null;
if (!string.IsNullOrWhiteSpace(prototypeName))
{
// If the prototype doesn't exist then we throw BEFORE we allocate the entity.
prototype = PrototypeManager.Index<EntityPrototype>(prototypeName);
}
var entity = AllocEntity(out metadata, uid);
metadata._entityPrototype = prototype;
Dirty(metadata, metadata);
Dirty(entity, metadata, metadata);
return entity;
}
/// <summary>
/// Allocates an entity and stores it but does not load components or do initialization.
/// </summary>
private protected EntityUid AllocEntity(out MetaDataComponent metadata, EntityUid uid = default)
private EntityUid AllocEntity(out MetaDataComponent metadata, EntityUid uid = default)
{
if (uid == default)
{
@@ -735,7 +726,9 @@ namespace Robust.Shared.GameObjects
if (prototypeName == null)
return AllocEntity(out _, uid);
var entity = AllocEntity(prototypeName, out var metadata, uid);
PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype);
var entity = AllocEntity(prototype, out var metadata, uid);
try
{
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, this, _serManager, context);

View File

@@ -131,6 +131,11 @@ namespace Robust.Shared.Map
void FindGridsIntersecting<TState>(MapId mapId, Box2 worldAABB, ref TState state, GridCallback<TState> callback, bool approx = false, bool includeMap = true);
void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, GridCallback callback, bool approx = false, bool includeMap = true);
void FindGridsIntersecting<TState>(MapId mapId, Box2Rotated worldBounds, ref TState state, GridCallback<TState> callback, bool approx = false, bool includeMap = true);
/// <summary>
/// Returns the grids intersecting this AABB.
/// </summary>

View File

@@ -107,6 +107,18 @@ internal partial class MapManager
state = state2.state;
}
public void FindGridsIntersecting(MapId mapId, Box2Rotated worldBounds, GridCallback callback, bool approx = false,
bool includeMap = true)
{
FindGridsIntersecting(mapId, worldBounds.CalcBoundingBox(), callback, approx, includeMap);
}
public void FindGridsIntersecting<TState>(MapId mapId, Box2Rotated worldBounds, ref TState state, GridCallback<TState> callback,
bool approx = false, bool includeMap = true)
{
FindGridsIntersecting(mapId, worldBounds.CalcBoundingBox(), ref state, callback, approx, includeMap);
}
private static bool IsIntersecting(
Box2 aabb,
EntityUid gridUid,

View File

@@ -3,6 +3,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.Utility;

View File

@@ -19,7 +19,7 @@
<PackageReference Include="Linguini.Bundle" Version="0.1.3" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.2-beta2" />
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />

View File

@@ -16,7 +16,7 @@ namespace Robust.UnitTesting.Client.Graphics
var styleBox = new StyleBoxFlat();
Assert.That(
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50)),
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50), 1),
Is.EqualTo(new UIBox2(0, 0, 50, 50)));
styleBox.ContentMarginLeftOverride = 3;
@@ -25,12 +25,16 @@ namespace Robust.UnitTesting.Client.Graphics
styleBox.ContentMarginBottomOverride = 11;
Assert.That(
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50)),
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50), 1),
Is.EqualTo(new UIBox2(0, 0, 60, 66)));
Assert.That(
styleBox.GetEnvelopBox(new Vector2(10, 10), new Vector2(50, 50)),
styleBox.GetEnvelopBox(new Vector2(10, 10), new Vector2(50, 50), 1),
Is.EqualTo(new UIBox2(10, 10, 70, 76)));
Assert.That(
styleBox.GetEnvelopBox(new Vector2(10, 10), new Vector2(50, 50), 2.0f),
Is.EqualTo(new UIBox2(10, 10, 80, 92)));
}
}
}