Compare commits

..

4 Commits

Author SHA1 Message Date
PJB3005
281dd0626f Version: 266.0.2 2025-09-19 09:17:23 +02:00
Skye
42eb441a8d Fix resource loading on non-Windows platforms (#6201)
(cherry picked from commit 51bbc5dc45)
2025-09-19 09:17:23 +02:00
PJB3005
4d7022e101 Version: 266.0.1 2025-09-14 14:55:14 +02:00
PJB3005
a9305107d2 Squashed commit of the following:
commit d4f265c314
Author: PJB3005 <pieterjan.briers+git@gmail.com>
Date:   Sun Sep 14 14:32:44 2025 +0200

    Fix incorrect path combine in DirLoader and WritableDirProvider

    This (and the other couple past commits) reported by Elelzedel.

commit 7654d38612
Author: PJB3005 <pieterjan.briers+git@gmail.com>
Date:   Sat Sep 13 22:50:51 2025 +0200

    Move CEF cache out of data directory

    Don't want content messing with this...

commit cdcc255123
Author: PJB3005 <pieterjan.briers+git@gmail.com>
Date:   Sat Sep 13 19:11:16 2025 +0200

    Make Robust.Client.WebView.Cef.Program internal.

commit 2f56a6a110
Author: PJB3005 <pieterjan.briers+git@gmail.com>
Date:   Sat Sep 13 19:10:46 2025 +0200

    Update SpaceWizards.NFluidSynth to 0.2.2

commit 16fc48cef2
Author: PJB3005 <pieterjan.briers+git@gmail.com>
Date:   Sat Sep 13 19:09:43 2025 +0200

    Hide IWritableDirProvider.RootDir on client

    This shouldn't be exposed.

(cherry picked from commit 2f07159336bc640e41fbbccfdec4133a68c13bdb)
(cherry picked from commit d6c3212c74373ed2420cc4be2cf10fcd899c2106)
(cherry picked from commit bfa70d7e2ca6758901b680547fcfa9b24e0610b7)
2025-09-14 14:55:13 +02:00
160 changed files with 8867 additions and 3531 deletions

View File

@@ -10,7 +10,7 @@ jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-latest, windows-latest] # , macos-latest] - temporarily disabled due to libfreetype.dll errors.
runs-on: ${{ matrix.os }}

View File

@@ -26,7 +26,7 @@ jobs:
dotnet-version: 9.0.x
- name: Package client
run: Tools/package_client_build.py
run: Tools/package_client_build.py -p windows mac linux
- name: Shuffle files around
run: |

View File

@@ -44,11 +44,10 @@
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageVersion Include="Nett" Version="0.15.0" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
<PackageVersion Include="Pidgin" Version="3.3.0" />
<PackageVersion Include="Robust.Natives" Version="0.2.1" />
<PackageVersion Include="Robust.Natives.Zstd" Version="0.1.0-zstd1.5.7" />
<PackageVersion Include="Robust.Natives" Version="0.1.1" />
<PackageVersion Include="Robust.Natives.Cef" Version="131.3.5" />
<PackageVersion Include="Robust.Shared.AuthLib" Version="0.1.2" />
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
@@ -59,11 +58,8 @@
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.2.2" />
<PackageVersion Include="SpaceWizards.Sdl" Version="1.0.0" />
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.1.0" />
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
<PackageVersion Include="libsodium" Version="1.0.20.1" />
<PackageVersion Include="System.Management" Version="9.0.8" />
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.1" />
<PackageVersion Include="TerraFX.Interop.Xlib" Version="6.4.0" />
<PackageVersion Include="VorbisPizza" Version="1.3.0" />

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,165 +54,10 @@ END TEMPLATE-->
*None yet*
## 267.2.2
## 266.0.2
## 267.2.1
## 267.2.0
### New features
* Sprites and Sprite layers have a new `Loop` data field that can be set to false to automatically pause animations once they have finished.
### Bugfixes
* Fixed `CollectionExtensions.TryGetValue` throwing an exception when given a negative list index.
* Fixed `EntityManager.PredictedQueueDeleteEntity()` not deferring changes for networked entities until the end of the tick.
* Fixed `EntityManager.IsQueuedForDeletion` not returning true foe entities getting deleted via `PredictedQueueDeleteEntity()`
### Other
* `IResourceManager.GetContentRoots()` has been obsoleted and returns no more results.
### Internal
* `IResourceManager.GetContentRoots()` has been replaced with a similar method on `IResourceManagerInternal`. This new method returns `string`s instead of `ResPath`s, and usage code has been updated to use these paths correctly.
## 267.1.0
### New features
* Animation:
* `AnimationTrackProperty.KeyFrame` can now have easings functions applied.
* Graphics:
* `PointLightComponent` now has two fields, `falloff` and `curveFactor`, for controlling light falloff and the shape of the light attenuation curve.
* `IClydeViewport` now has an `Id` and `ClearCachedResources` event. Together, these allow you to properly cache rendering resources per viewport.
* Miscellaneous:
* Added `display.max_fps` CVar.
* Added `IGameTiming.FrameStartTime`.
* Sandbox:
* Added `System.WeakReference<T>`.
* Added `SpaceWizards.Sodium.CryptoGenericHashBlake2B.Hash()`.
* Added `System.Globalization.UnicodeCategory`.
* Serialization:
* Added a new entity yaml deserialization option (`SerializationOptions.EntityExceptionBehaviour`) that can optionally make deserialization more exception tolerant.
* Tooling:
* `devwindow` now has a tab listing active `IRenderTarget`s, allowing insight into resource consumption.
* `loadgrid` now creates a map if passed an invalid map ID.
* Added game version information to F3 overlay.
* Added completions to more map commands.
* UI system:
* `Control.OrderedChildCollection` (gotten from `.Children`) now implements `IReadOnlyList<Control>`, allowing it to be indexed directly.
* Added `WrapContainer` control. This lays out multiple elements along an axis, wrapping them if there's not enough space. It comes with many options and can handle multiple axes.
* Popups/modals now work in secondary windows. This entails putting roots for these on each UI root.
* If you are not using `OSWindow` and are instead creating secondary windows manually, you need to call `WindowRoot.CreateRootControls()` manually for this to work.
* Added `Axis` enum, `IAxisImplementation` interface and axis implementations. These allow writing general-purpose UI layout code that can work on multiple axis at once.
* WebView:
* Added `web.remote_debug_port` CVar to change Chromium's remote debug port.
### Bugfixes
* Audio:
* Fix audio occlusion & velocity being calculated with the audio entity instead of the source entity.
* Bound UI:
* Try to fix an assert related to `UserInterfaceComponent` delta states.
* Configuration:
* The client no longer tries to send `CLIENT | REPLICATED` CVars when not connected to a server. This could cause test failures.
* Math:
* Fixed `Matrix3Helpers.TransformBounds()` returning an incorrect result. Now it effectively behaves like `Matrix3Helpers.TransformBox()` and has been marked as obsolete.
* Physics:
* Work around an undiagnosed crash processing entities without parents.
* Serialization:
* Fix `[DataRecord]`s with computed get-only properties.
* Resources:
* Fix some edge case broken path joining in `DirLoader` and `WritableDirProvider`.
* Tests:
* Fix `PlacementManager.CurrentMousePosition` in integration tests.
* UI system:
* Animations for the debug console and scrolling are no longer framerate dependent.
* Fix `OutputPanel.SetMessage` triggering a scrolling animation when editing messages other than the last one.
* Fix word wrapping with two-`char` runes in `RichTextLabel` and `OutputPanel`.
* WebView:
* Multiple clients with WebView can now run at the same time, thanks to better CEF cache management.
### Other
* Audio:
* Improved error logging for invalid file names in `SharedAudioSystem`.
* Configuration:
* Fix crash if more than 255 `REPLICATED` CVars exist. Also increased the max size of the CVar replication message.
* Entities:
* Transform:
* `AnchorEntity` logs instead of using an assert for invalid arguments.
* Containers:
* `SharedContainerSystem.CleanContainer` now uses `PredictedDel()` instead.
* Networking:
* The client now logs an error when attempting to send a network message without server connection. Previously, it would be silently dropped.
* `net.interp` and `net.buffer_size` CVars are now `REPLICATED`.
* Graphics:
* The function used for pointlight attenuation has been modified to be c1 continuous as opposed to simply c0 continuous, resulting in smoother boundary behavior.
* RSI validator no longer allows empty (`""`) state names.
* Packaging:
* Server packaging now excludes all files in the `Audio/` directory.
* Server packaging now excludes engine resources `EngineFonts/` and `Midi/`.
* ACZ explicitly specifies manifest charset as UTF-8.
* Serialization:
* `CurTime`-relative `TimeSpan` values that are `MaxValue` now deserialize without overflow.
* `SpriteSpecifier.Texture` will now fail to validate if the path is inside a `.rsi`. Use RSI sprite specifiers instead.
* Resources:
* `IWritableDirProvider.RootDir` is now null on clients.
* WebView:
* CEF cache is no longer in the content-accessible user data directory.
### Internal
* Added some debug commands for debugging viewport resource management: `vp_clear_all_cached` & `vp_test_finalize`
* `uitest` command now supports command argument for tab selection, like `uitest2`.
* Rewrote `BoxContainer` implementation to make use of new axis system.
* Moved `uitest2` and `devwindow` to use the `OSWindow` control.
* SDL3 binding has been moved to `SpaceWizards.Sdl` NuGet package.
* `dmetamem` command has been moved from `DEBUG` to `TOOLS`.
* Consolidate `AttachToGridOrMap` with `TryGetMapOrGridCoordinates`.
* Secondary window render targets have clear names specified.
* Updated `SpaceWizards.NFluidsynth` to `0.2.2`.
* `Robust.Client.WebView.Cef.Program` is now internal.
* `download_manifest_file.py` script in repo now always decodes as UTF-8 correctly.
* Added a new debug assert to game state processing.
## 267.0.0
### Breaking changes
* When a player disconnects, the relevant callbacks are now fired *after* removing the channel from `INetManager`.
### New features
* Engine builds are now published for ARM64 & FreeBSD.
* CPU model names are now detected on Windows & Linux ARM64.
* Toolshed's `spawn:in` command now works on entities without `Physics` component.
### Bugfixes
* SDL3 windowing backend fixes:
* Avoid macOS freezes with multiple windows.
* Fix macOS rendering breaking when closing secondary windows.
* File dialogs properly associate parent windows.
* Fix IME positions not working with UI scaling properly.
* Properly specify library names for loading native library.
* WinBit threads don't permanently stay stuck when their window closes.
* Checking for the "`null`" literal in serialization is now culture invariant.
### Other
* Compat mode on the client now defaults to on for Windows Snapdragon devices, to work around driver bugs.
* Update various libraries & natives. This enables out-of-the-box ARM64 support on all platforms and is a long-overdue modernization.
* Key name displays now use proper Unicode symbols for macOS ⌥ and ⌘.
* Automated CI for RobustToolbox runs on macOS again.
* Autocompletions for `ProtoId<T>` in Toolshed now use `PrototypeIdsLimited` instead of arbitrarily cutting out if more than 256 of a prototype exists.
## 266.0.1
## 266.0.0

View File

@@ -1,3 +0,0 @@
generic-map = map
generic-grid = grid
generic-mapid = map Id

View File

@@ -577,5 +577,3 @@ cmd-localization_set_culture-desc = Set DefaultCulture for the client Localizati
cmd-localization_set_culture-help = Usage: localization_set_culture <cultureName>
cmd-localization_set_culture-culture-name = <cultureName>
cmd-localization_set_culture-changed = Localization changed to { $code } ({ $nativeName } / { $englishName })
cmd-addmap-hint-2 = runMapInit [true / false]

View File

@@ -8,18 +8,3 @@ dev-window-tab-textures-info = Width: { $width } Height: { $height }
PixelType: { $pixelType } sRGB: { $srgb }
Name: { $name }
Est. memory usage: { $bytes }
## "Render Targets" dev window tab
dev-window-tab-render-targets-title = Render Targets
dev-window-tab-render-targets-reload = Reload
dev-window-tab-render-targets-filter = Filter
dev-window-tab-render-targets-column-id = ID
dev-window-tab-render-targets-column-name = Name
dev-window-tab-render-targets-column-size = Size
dev-window-tab-render-targets-column-type = Type
dev-window-tab-render-targets-column-vram = VRAM
dev-window-tab-render-targets-column-thumbnail = Thumbnail
dev-window-tab-render-targets-value-null = null
dev-window-tab-render-targets-value-not-available = Not available
dev-window-tab-render-targets-summary = Total VRAM: { $vram }

View File

@@ -2,7 +2,6 @@ input-key-Escape = Escape
input-key-Control = Control
input-key-Shift = Shift
input-key-Alt = Alt
input-key-Alt-mac = ⌥
input-key-Menu = Menu
input-key-F1 = F1
input-key-F2 = F2
@@ -71,8 +70,8 @@ input-key-MouseButton9 = Mouse 9
input-key-LSystem-win = Left Win
input-key-RSystem-win = Right Win
input-key-LSystem-mac = Left
input-key-RSystem-mac = Right
input-key-LSystem-mac = Left Cmd
input-key-RSystem-mac = Right Cmd
input-key-LSystem-linux = Left Meta
input-key-RSystem-linux = Right Meta

View File

@@ -14,8 +14,6 @@ uniform highp vec2 lightCenter;
uniform highp float lightRange;
uniform highp float lightPower;
uniform highp float lightSoftness;
uniform highp float lightFalloff;
uniform highp float lightCurveFactor;
uniform highp float lightIndex;
uniform sampler2D shadowMap;
@@ -49,15 +47,8 @@ void fragment()
discard;
}
// this implementation of light attenuation primarily adapted from
// https://lisyarus.github.io/blog/posts/point-light-attenuation.html
highp float sqr_dist = dot(diff, diff) + LIGHTING_HEIGHT;
highp float s = clamp(sqrt(sqr_dist) / lightRange, 0.0, 1.0);
highp float s2 = s * s;
// controls curve by lerping between two variants (inverse-shape and inversequadratic-shape)
highp float curveFactor = mix(s, s2, clamp(lightCurveFactor, 0.0, 1.0));
highp float val = clamp(((1.0 - s2) * (1.0 - s2)) / (1.0 + lightFalloff * curveFactor), 0.0, 1.0);
highp float dist = dot(diff, diff) + LIGHTING_HEIGHT;
highp float val = clamp((1.0 - clamp(sqrt(dist) / lightRange, 0.0, 1.0)) * (1.0 / (sqrt(dist + 1.0))), 0.0, 1.0);
val *= lightPower;
val *= mask;

View File

@@ -1,51 +0,0 @@
using System;
using System.IO;
using Robust.Client.Utility;
namespace Robust.Client.WebView.Cef;
internal sealed partial class WebViewManagerCef
{
private const string BaseCacheName = "cef_cache";
private const string LockFileName = "robust.lock";
private FileStream? _lockFileStream;
private const int MaxAttempts = 15; // This probably shouldn't be a cvar because the only reason you'd need it change for legit just botting the game.
private string FindAndLockCacheDirectory()
{
var rootDir = Path.Combine(UserDataDir.GetRootUserDataDir(_gameController), BaseCacheName);
for (var i = 0; i < MaxAttempts; i++)
{
var cacheDirPath = Path.Combine(rootDir, i.ToString());
if (TryLockCacheDir(i, cacheDirPath))
return cacheDirPath;
}
throw new Exception("Unable to locate available CEF cache directory!");
}
private bool TryLockCacheDir(int attempt, string path)
{
_sawmill.Verbose($"Trying to lock cache directory {attempt}");
// Does not fail if directory already exists.
Directory.CreateDirectory(path);
var lockFilePath = Path.Combine(path, LockFileName);
try
{
var file = File.Open(lockFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
_lockFileStream = file;
_sawmill.Debug($"Successfully locked CEF cache directory {attempt}");
return true;
}
catch (IOException ex)
{
_sawmill.Error($"Failed to lock cache directory {attempt}: {ex}");
return false;
}
}
}

View File

@@ -61,9 +61,12 @@ namespace Robust.Client.WebView.Cef
if (cefResourcesPath == null)
throw new InvalidOperationException("Unable to locate cef_resources directory!");
var remoteDebugPort = _cfg.GetCVar(WCVars.WebRemoteDebugPort);
var cachePath = FindAndLockCacheDirectory();
var cachePath = "";
if (_resourceManager.UserData is WritableDirProvider userData)
{
var rootDir = UserDataDir.GetRootUserDataDir(_gameController);
cachePath = Path.Combine(rootDir, "cef_cache", "0");
}
var settings = new CefSettings()
{
@@ -73,7 +76,7 @@ namespace Robust.Client.WebView.Cef
BrowserSubprocessPath = subProcessPath,
LocalesDirPath = Path.Combine(cefResourcesPath, "locales"),
ResourcesDirPath = cefResourcesPath,
RemoteDebuggingPort = remoteDebugPort,
RemoteDebuggingPort = 9222,
CookieableSchemesList = "usr,res",
CachePath = cachePath,
};

View File

@@ -26,16 +26,4 @@ public static class WCVars
/// </summary>
public static readonly CVarDef<bool> WebHeadless =
CVarDef.Create("web.headless", false, CVar.CLIENTONLY);
#if TOOLS
private const int DefaultRemoteDebugPort = 9222;
#else
private const int DefaultRemoteDebugPort = 0;
#endif
/// <summary>
/// If not 0, the port number used for Chromium's remote debugging.
/// </summary>
public static readonly CVarDef<int> WebRemoteDebugPort =
CVarDef.Create("web.remote_debug_port", DefaultRemoteDebugPort, CVar.CLIENTONLY);
}

View File

@@ -54,14 +54,8 @@ namespace Robust.Client.Animations
}
else
{
var next = KeyFrames[nextKeyFrame];
// Get us a scale 0 -> 1 here.
var t = playingTime / next.KeyTime;
// Apply easing to time parameter, if one was specified
if (next.Easing != null)
t = next.Easing(t);
var t = playingTime / KeyFrames[nextKeyFrame].KeyTime;
switch (InterpolationMode)
{
@@ -153,20 +147,10 @@ namespace Robust.Client.Animations
/// </summary>
public readonly float KeyTime;
/// <summary>
/// An easing function to apply when interpolating to this keyframe's value.
/// Modifies the time parameter (0..1) of the interpolation between the previous keyframe and this one.
/// </summary>
/// <remarks>
/// See <see cref="Easings"/> for examples of easing functions, or provide your own.
/// </remarks>
public readonly Func<float, float>? Easing;
public KeyFrame(object value, float keyTime, Func<float, float>? easing = null)
public KeyFrame(object value, float keyTime)
{
Value = value;
KeyTime = keyTime;
Easing = easing;
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Client.Audio.Sources;
using Robust.Client.ResourceManagement;
using Robust.Shared;
@@ -144,7 +145,7 @@ internal sealed partial class AudioManager : IAudioInternal
private static void RemoveEfx((int sourceHandle, int filterHandle) handles)
{
if (handles.filterHandle != 0)
ALC.EFX.DeleteFilter(handles.filterHandle);
EFX.DeleteFilter(handles.filterHandle);
}
private void _checkAlcError(ALDevice device,

View File

@@ -1,3 +1,4 @@
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Client.Audio.Effects;
using Robust.Shared.Audio.Components;
using Robust.Shared.GameObjects;

View File

@@ -372,13 +372,13 @@ public sealed partial class AudioSystem : SharedAudioSystem
return;
}
var parentUid = xform.ParentUid;
Vector2 worldPos;
component.Volume = component.Params.Volume;
// Handle grid audio differently by using grid position.
if ((component.Flags & AudioFlags.GridAudio) != 0x0)
{
var parentUid = xform.ParentUid;
worldPos = _maps.GetGridPosition(parentUid);
}
else
@@ -412,7 +412,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
}
else
{
var occlusion = GetOcclusion(listener, delta, distance, parentUid);
var occlusion = GetOcclusion(listener, delta, distance, entity);
component.Occlusion = occlusion;
}
@@ -420,11 +420,11 @@ public sealed partial class AudioSystem : SharedAudioSystem
component.Position = worldPos;
// Make race cars go NYYEEOOOOOMMMMM
if (_physicsQuery.TryGetComponent(parentUid, out var physicsComp))
if (_physicsQuery.TryGetComponent(entity, out var physicsComp))
{
// This actually gets the tracked entity's xform & iterates up though the parents for the second time. Bit
// inefficient.
var velocity = _physics.GetMapLinearVelocity(parentUid, physicsComp);
var velocity = _physics.GetMapLinearVelocity(entity, physicsComp, xform);
component.Velocity = velocity;
}
}
@@ -589,11 +589,6 @@ public sealed partial class AudioSystem : SharedAudioSystem
var playing = CreateAndStartPlayingStream(audioParams, specifier, stream);
_xformSys.SetCoordinates(playing.Entity, new EntityCoordinates(entity, Vector2.Zero));
// Since we're playing the sound immediately in the middle of a tick, we need to force ProcessStream -now-
// to set occlusion/position/velocity etc
// otherwise predicted positional sounds will sound very incorrect in several possible ways (e#5802, e#6175) until the next tick
ProcessStream(playing.Entity, playing.Component, Transform(playing.Entity), GetListenerCoordinates());
return playing;
}
@@ -637,10 +632,6 @@ public sealed partial class AudioSystem : SharedAudioSystem
var playing = CreateAndStartPlayingStream(audioParams, specifier, stream);
_xformSys.SetCoordinates(playing.Entity, coordinates);
// see PlayEntity for why this is necessary
ProcessStream(playing.Entity, playing.Component, Transform(playing.Entity), GetListenerCoordinates());
return playing;
}
@@ -723,6 +714,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
offset = Math.Clamp(offset, 0f, maxOffset);
source.PlaybackPosition = offset;
// For server we will rely on the adjusted one but locally we will have to adjust it ourselves.
ApplyAudioParams(comp.Params, comp);
source.StartPlaying();
return (entity, comp);
}

View File

@@ -1,6 +1,6 @@
using System;
using System.Numerics;
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Effects;
using Robust.Shared.Maths;
@@ -16,16 +16,16 @@ internal sealed class AudioEffect : IAudioEffect
public AudioEffect(IAudioInternal manager)
{
Handle = ALC.EFX.GenEffect();
Handle = EFX.GenEffect();
_master = manager;
ALC.EFX.Effect(Handle, EffectInteger.EffectType, (int) EffectType.EaxReverb);
EFX.Effect(Handle, EffectInteger.EffectType, (int) EffectType.EaxReverb);
}
public void Dispose()
{
if (Handle != 0)
{
ALC.EFX.DeleteEffect(Handle);
EFX.DeleteEffect(Handle);
Handle = 0;
}
}
@@ -44,14 +44,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDensity, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbDensity, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDensity, value);
EFX.Effect(Handle, EffectFloat.EaxReverbDensity, value);
_master._checkAlError();
}
}
@@ -62,14 +62,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDiffusion, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbDiffusion, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDiffusion, value);
EFX.Effect(Handle, EffectFloat.EaxReverbDiffusion, value);
_master._checkAlError();
}
}
@@ -80,14 +80,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbGain, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbGain, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbGain, value);
EFX.Effect(Handle, EffectFloat.EaxReverbGain, value);
_master._checkAlError();
}
}
@@ -98,14 +98,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbGainHF, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbGainHF, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbGainHF, value);
EFX.Effect(Handle, EffectFloat.EaxReverbGainHF, value);
_master._checkAlError();
}
}
@@ -116,14 +116,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbGainLF, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbGainLF, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbGainLF, value);
EFX.Effect(Handle, EffectFloat.EaxReverbGainLF, value);
_master._checkAlError();
}
}
@@ -134,14 +134,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayTime, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayTime, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDecayTime, value);
EFX.Effect(Handle, EffectFloat.EaxReverbDecayTime, value);
_master._checkAlError();
}
}
@@ -152,14 +152,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayHFRatio, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayHFRatio, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDecayHFRatio, value);
EFX.Effect(Handle, EffectFloat.EaxReverbDecayHFRatio, value);
_master._checkAlError();
}
}
@@ -170,14 +170,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayLFRatio, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayLFRatio, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDecayLFRatio, value);
EFX.Effect(Handle, EffectFloat.EaxReverbDecayLFRatio, value);
_master._checkAlError();
}
}
@@ -188,14 +188,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsGain, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsGain, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsGain, value);
EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsGain, value);
_master._checkAlError();
}
}
@@ -206,14 +206,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsDelay, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsDelay, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsDelay, value);
EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsDelay, value);
_master._checkAlError();
}
}
@@ -224,7 +224,7 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
var value = ALC.EFX.GetEffect(Handle, EffectVector3.EaxReverbReflectionsPan);
var value = EFX.GetEffect(Handle, EffectVector3.EaxReverbReflectionsPan);
_master._checkAlError();
return new Vector3(value.X, value.Z, value.Y);
}
@@ -232,7 +232,7 @@ internal sealed class AudioEffect : IAudioEffect
{
_checkDisposed();
var openVec = new OpenTK.Mathematics.Vector3(value.X, value.Y, value.Z);
ALC.EFX.Effect(Handle, EffectVector3.EaxReverbReflectionsPan, ref openVec);
EFX.Effect(Handle, EffectVector3.EaxReverbReflectionsPan, ref openVec);
_master._checkAlError();
}
}
@@ -243,14 +243,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbGain, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbGain, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbGain, value);
EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbGain, value);
_master._checkAlError();
}
}
@@ -261,14 +261,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbDelay, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbDelay, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbDelay, value);
EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbDelay, value);
_master._checkAlError();
}
}
@@ -279,7 +279,7 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
var value = ALC.EFX.GetEffect(Handle, EffectVector3.EaxReverbLateReverbPan);
var value = EFX.GetEffect(Handle, EffectVector3.EaxReverbLateReverbPan);
_master._checkAlError();
return new Vector3(value.X, value.Z, value.Y);
}
@@ -287,7 +287,7 @@ internal sealed class AudioEffect : IAudioEffect
{
_checkDisposed();
var openVec = new OpenTK.Mathematics.Vector3(value.X, value.Y, value.Z);
ALC.EFX.Effect(Handle, EffectVector3.EaxReverbLateReverbPan, ref openVec);
EFX.Effect(Handle, EffectVector3.EaxReverbLateReverbPan, ref openVec);
_master._checkAlError();
}
}
@@ -298,14 +298,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoTime, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoTime, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbEchoTime, value);
EFX.Effect(Handle, EffectFloat.EaxReverbEchoTime, value);
_master._checkAlError();
}
}
@@ -316,14 +316,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoDepth, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoDepth, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbEchoDepth, value);
EFX.Effect(Handle, EffectFloat.EaxReverbEchoDepth, value);
_master._checkAlError();
}
}
@@ -334,14 +334,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationTime, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationTime, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbModulationTime, value);
EFX.Effect(Handle, EffectFloat.EaxReverbModulationTime, value);
_master._checkAlError();
}
}
@@ -352,14 +352,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationDepth, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationDepth, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbModulationDepth, value);
EFX.Effect(Handle, EffectFloat.EaxReverbModulationDepth, value);
_master._checkAlError();
}
}
@@ -370,14 +370,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, value);
EFX.Effect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, value);
_master._checkAlError();
}
}
@@ -388,14 +388,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbHFReference, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbHFReference, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbHFReference, value);
EFX.Effect(Handle, EffectFloat.EaxReverbHFReference, value);
_master._checkAlError();
}
}
@@ -406,14 +406,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbLFReference, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbLFReference, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbLFReference, value);
EFX.Effect(Handle, EffectFloat.EaxReverbLFReference, value);
_master._checkAlError();
}
}
@@ -424,14 +424,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, out var value);
EFX.GetEffect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, value);
EFX.Effect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, value);
_master._checkAlError();
}
}
@@ -442,14 +442,14 @@ internal sealed class AudioEffect : IAudioEffect
get
{
_checkDisposed();
ALC.EFX.GetEffect(Handle, EffectInteger.EaxReverbDecayHFLimit, out var value);
EFX.GetEffect(Handle, EffectInteger.EaxReverbDecayHFLimit, out var value);
_master._checkAlError();
return value;
}
set
{
_checkDisposed();
ALC.EFX.Effect(Handle, EffectInteger.EaxReverbDecayHFLimit, value);
EFX.Effect(Handle, EffectInteger.EaxReverbDecayHFLimit, value);
_master._checkAlError();
}
}

View File

@@ -1,4 +1,4 @@
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Shared.Audio.Effects;
namespace Robust.Client.Audio.Effects;
@@ -6,13 +6,13 @@ namespace Robust.Client.Audio.Effects;
/// <inheritdoc />
internal sealed class AuxiliaryAudio : IAuxiliaryAudio
{
internal int Handle = ALC.EFX.GenAuxiliaryEffectSlot();
internal int Handle = EFX.GenAuxiliaryEffectSlot();
public void Dispose()
{
if (Handle != -1)
{
ALC.EFX.DeleteAuxiliaryEffectSlot(Handle);
EFX.DeleteAuxiliaryEffectSlot(Handle);
Handle = -1;
}
}
@@ -22,11 +22,11 @@ internal sealed class AuxiliaryAudio : IAuxiliaryAudio
{
if (effect is AudioEffect audEffect)
{
ALC.EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, audEffect.Handle);
EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, audEffect.Handle);
}
else
{
ALC.EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, 0);
EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, 0);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Numerics;
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Shared.Audio;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -76,7 +77,7 @@ internal sealed class AudioSource : BaseAudioSource
else
{
if (FilterHandle != 0)
ALC.EFX.DeleteFilter(FilterHandle);
EFX.DeleteFilter(FilterHandle);
AL.DeleteSource(SourceHandle);
Master.RemoveAudioSource(SourceHandle);

View File

@@ -1,6 +1,7 @@
using System;
using System.Numerics;
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Client.Audio.Effects;
using Robust.Shared.Audio.Effects;
using Robust.Shared.Audio.Sources;
@@ -81,9 +82,9 @@ public abstract class BaseAudioSource : IAudioSource
get
{
_checkDisposed();
var state = AL.GetSource(SourceHandle, ALGetSourcei.SourceState);
var state = AL.GetSourceState(SourceHandle);
Master._checkAlError();
return state == (int)ALSourceState.Playing;
return state == ALSourceState.Playing;
}
set
{
@@ -361,11 +362,11 @@ public abstract class BaseAudioSource : IAudioSource
if (audio is AuxiliaryAudio impAudio)
{
ALC.EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, impAudio.Handle, 0, 0);
EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, impAudio.Handle, 0, 0);
}
else
{
ALC.EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, 0, 0, 0);
EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, 0, 0, 0);
}
Master._checkAlError();
@@ -375,12 +376,12 @@ public abstract class BaseAudioSource : IAudioSource
{
if (FilterHandle == 0)
{
FilterHandle = ALC.EFX.GenFilter();
ALC.EFX.Filter(FilterHandle, FilterInteger.FilterType, (int) FilterType.Lowpass);
FilterHandle = EFX.GenFilter();
EFX.Filter(FilterHandle, FilterInteger.FilterType, (int) FilterType.Lowpass);
}
ALC.EFX.Filter(FilterHandle, FilterFloat.LowpassGain, gain);
ALC.EFX.Filter(FilterHandle, FilterFloat.LowpassGainHF, cutoff);
EFX.Filter(FilterHandle, FilterFloat.LowpassGain, gain);
EFX.Filter(FilterHandle, FilterFloat.LowpassGainHF, cutoff);
AL.Source(SourceHandle, ALSourcei.EfxDirectFilter, FilterHandle);
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Shared.Audio.Sources;
namespace Robust.Client.Audio.Sources;
@@ -36,9 +37,9 @@ internal sealed class BufferedAudioSource : BaseAudioSource, IBufferedAudioSourc
get
{
_checkDisposed();
var state = AL.GetSource(SourceHandle, ALGetSourcei.SourceState);
var state = AL.GetSourceState(SourceHandle);
_master._checkAlError();
return state == (int)ALSourceState.Playing;
return state == ALSourceState.Playing;
}
set
{
@@ -83,7 +84,7 @@ internal sealed class BufferedAudioSource : BaseAudioSource, IBufferedAudioSourc
else
{
if (FilterHandle != 0)
ALC.EFX.DeleteFilter(FilterHandle);
EFX.DeleteFilter(FilterHandle);
AL.DeleteSource(SourceHandle);
AL.DeleteBuffers(BufferHandles);

View File

@@ -71,7 +71,7 @@ internal sealed class ClientNetConfigurationManager : NetConfigurationManager, I
// Actually set the CVar
base.SetCVar(name, value, force);
if ((flags & CVar.REPLICATED) == 0 || !NetManager.IsConnected)
if ((flags & CVar.REPLICATED) == 0)
return;
var msg = new MsgConVars();

View File

@@ -4,7 +4,7 @@ using Robust.Shared.ContentPack;
namespace Robust.Client.Console.Commands
{
#if TOOLS
#if DEBUG
internal sealed class DumpMetadataMembersCommand : LocalizedCommands
{
public override string Command => "dmetamem";

View File

@@ -1,124 +0,0 @@
using System;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
using ItemJustification = Robust.Client.UserInterface.Controls.WrapContainer.ItemJustification;
namespace Robust.Client.Console.Commands;
internal sealed partial class UITestControl
{
private sealed class TabWrapContainer : Control
{
private readonly CheckBox _equalSizeBox;
private readonly CheckBox _reverseBox;
private readonly OptionButton _axisButton;
private readonly OptionButton _justifyButton;
private readonly LineEdit _separationEdit;
private readonly LineEdit _crossSeparationEdit;
public TabWrapContainer()
{
var container = new WrapContainer
{
MouseFilter = MouseFilterMode.Stop,
VerticalExpand = true,
};
var random = new Random(3005);
for (var i = 0; i < 35; i++)
{
var val = random.Next(1, 16);
var text = string.Create(val, 0, (span, _) => span.Fill('O'));
container.AddChild(new Button { Text = text });
}
AddChild(new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
Children =
{
new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
SeparationOverride = 4,
Children =
{
(_equalSizeBox = new CheckBox
{
Text = nameof(WrapContainer.EqualSize)
}),
(_reverseBox = new CheckBox
{
Text = nameof(WrapContainer.Reverse)
}),
(_axisButton = new OptionButton()),
(_justifyButton = new OptionButton()),
(_separationEdit = new LineEdit
{
PlaceHolder = "Separation",
SetWidth = 100,
}),
(_crossSeparationEdit = new LineEdit
{
PlaceHolder = "Cross Separation",
SetWidth = 100,
})
}
},
new PanelContainer
{
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.Black },
Children =
{
container
}
}
},
});
_axisButton.AddItem(nameof(Axis.Horizontal), (int)Axis.Horizontal);
_axisButton.AddItem(nameof(Axis.HorizontalReverse), (int)Axis.HorizontalReverse);
_axisButton.AddItem(nameof(Axis.Vertical), (int)Axis.Vertical);
_axisButton.AddItem(nameof(Axis.VerticalReverse), (int)Axis.VerticalReverse);
_axisButton.OnItemSelected += args =>
{
_axisButton.SelectId(args.Id);
container.LayoutAxis = (Axis)args.Id;
};
_justifyButton.AddItem(nameof(ItemJustification.Begin), (int)ItemJustification.Begin);
_justifyButton.AddItem(nameof(ItemJustification.Center), (int)ItemJustification.Center);
_justifyButton.AddItem(nameof(ItemJustification.End), (int)ItemJustification.End);
_justifyButton.OnItemSelected += args =>
{
_justifyButton.SelectId(args.Id);
container.Justification = (ItemJustification)args.Id;
};
_equalSizeBox.OnPressed += _ => container.EqualSize = _equalSizeBox.Pressed;
_reverseBox.OnPressed += _ => container.Reverse = _reverseBox.Pressed;
_separationEdit.OnTextChanged += args =>
{
if (!int.TryParse(args.Text, out var sep))
sep = 0;
container.SeparationOverride = sep;
};
_crossSeparationEdit.OnTextChanged += args =>
{
if (!int.TryParse(args.Text, out var sep))
sep = 0;
container.CrossSeparationOverride = sep;
};
}
}
}

View File

@@ -4,6 +4,8 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -42,10 +44,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
var progressBar = new ProgressBar { MaxValue = 10, Value = 5 };
vBox.AddChild(progressBar);
var optionButton = new OptionButton
{
ToolTip = "This button has a tooltip. Spooky!"
};
var optionButton = new OptionButton();
optionButton.AddItem("Honk");
optionButton.AddItem("Foo");
optionButton.AddItem("Bar");
@@ -156,7 +155,6 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
_sprite = new TabSpriteView();
_tabContainer.AddChild(_sprite);
_tabContainer.AddChild(TabCursorShapes());
_tabContainer.AddChild(new TabWrapContainer { Name = nameof(Tab.WrapContainer) });
}
public void OnClosed()
@@ -277,13 +275,32 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
RichText = 7,
SpriteView = 8,
TabCursorShapes = 9,
WrapContainer = 10,
}
}
internal abstract class BaseUITestCommand : LocalizedCommands
internal sealed class UITestCommand : LocalizedCommands
{
public sealed override void Execute(IConsoleShell shell, string argStr, string[] args)
public override string Command => "uitest";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var window = new DefaultWindow { MinSize = new(800, 600) };
var control = new UITestControl();
window.OnClose += control.OnClosed;
window.Contents.AddChild(control);
window.OpenCentered();
}
}
internal sealed class UITest2Command : LocalizedCommands
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IUserInterfaceManager _uiMgr = default!;
public override string Command => "uitest2";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length > 1)
{
@@ -304,10 +321,18 @@ internal abstract class BaseUITestCommand : LocalizedCommands
control.SelectTab(tab);
}
CreateWindow(control);
var window = _clyde.CreateWindow(new WindowCreateParameters
{
Title = Loc.GetString("cmd-uitest2-title"),
});
var root = _uiMgr.CreateWindowRoot(window);
window.DisposeOnClose = true;
window.RequestClosed += _ => control.OnClosed();
root.AddChild(control);
}
public sealed override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
@@ -318,35 +343,4 @@ internal abstract class BaseUITestCommand : LocalizedCommands
return CompletionResult.Empty;
}
protected abstract void CreateWindow(UITestControl control);
}
internal sealed class UITestCommand : BaseUITestCommand
{
public override string Command => "uitest";
protected override void CreateWindow(UITestControl control)
{
var window = new DefaultWindow { MinSize = new(800, 600) };
window.OnClose += control.OnClosed;
window.Contents.AddChild(control);
window.OpenCentered();
}
}
internal sealed class UITest2Command : BaseUITestCommand
{
public override string Command => "uitest2";
protected override void CreateWindow(UITestControl control)
{
var window = new OSWindow
{
Title = Loc.GetString("cmd-uitest2-title"),
};
window.AddChild(control);
window.Closed += control.OnClosed;
window.Show();
}
}

View File

@@ -1,44 +0,0 @@
#if TOOLS
using Robust.Client.Graphics;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands;
internal sealed class ViewportClearAllCachedCommand : IConsoleCommand
{
[Dependency] private readonly IClydeInternal _clyde = default!;
public string Command => "vp_clear_all_cached";
public string Description => "Fires IClydeViewport.ClearCachedResources on all viewports";
public string Help => "";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
_clyde.ViewportsClearAllCached();
}
}
internal sealed class ViewportTestFinalizeCommand : IConsoleCommand
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
public string Command => "vp_test_finalize";
public string Description => "Creates a viewport, renders it once, then leaks it (finalizes it).";
public string Help => "";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var vp = _clyde.CreateViewport(new Vector2i(1920, 1080), nameof(ViewportTestFinalizeCommand));
vp.Eye = _eyeManager.CurrentEye;
vp.Render();
// Leak it.
}
}
#endif // TOOLS

View File

@@ -8,7 +8,6 @@ using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using SDL3;
namespace Robust.Client
{
@@ -94,8 +93,6 @@ namespace Robust.Client
public void Run(DisplayMode mode, GameControllerOptions options, Func<ILogHandler>? logHandlerFactory = null)
{
_displayMode = mode;
if (!StartupSystemSplash(options, logHandlerFactory))
{
_logger.Fatal("Failed to start game controller!");

View File

@@ -110,8 +110,6 @@ namespace Robust.Client
private ResourceManifestData? _resourceManifest;
private DisplayMode _displayMode;
public void SetCommandLineArgs(CommandLineArgs args)
{
_commandLineArgs = args;
@@ -275,9 +273,6 @@ namespace Robust.Client
}
};
_configurationManager.OnValueChanged(CVars.DisplayMaxFPS, _ => UpdateVsyncConfig());
_configurationManager.OnValueChanged(CVars.DisplayVSync, _ => UpdateVsyncConfig(), invokeImmediately: true);
_clyde.Ready();
if (_resourceManifest!.AutoConnect &&
@@ -714,30 +709,6 @@ namespace Robust.Client
}
private void UpdateVsyncConfig()
{
if (_displayMode == DisplayMode.Headless)
return;
var vsync = _configurationManager.GetCVar(CVars.DisplayVSync);
var maxFps = Math.Clamp(_configurationManager.GetCVar(CVars.DisplayMaxFPS), 0, 10_000);
_clyde.VsyncEnabled = vsync;
if (_mainLoop == null)
return;
if (vsync || maxFps == 0)
{
_mainLoop.SleepMode = SleepMode.None;
}
else
{
_mainLoop.SleepMode = SleepMode.Limit;
_mainLoop.LimitMinFrameTime = TimeSpan.FromSeconds(1.0 / maxFps);
}
}
internal enum DisplayMode : byte
{
Headless,

View File

@@ -29,9 +29,6 @@ namespace Robust.Client.GameObjects
internal event Action? AfterStartup;
internal event Action? AfterShutdown;
private readonly Queue<EntityUid> _queuedPredictedDeletions = new();
private readonly HashSet<EntityUid> _queuedPredictedDeletionsSet = new();
public override void Initialize()
{
SetupNetworking();
@@ -216,34 +213,6 @@ namespace Robust.Client.GameObjects
}
}
using (histogram?.WithLabels("PredictedQueueDel").NewTimer())
{
while (_queuedPredictedDeletions.TryDequeue(out var uid))
{
if (!MetaQuery.TryGetComponentInternal(uid, out var meta))
continue;
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
continue;
var xform = TransformQuery.GetComponentInternal(uid);
if (meta.NetEntity.IsClientSide())
{
DeleteEntity(uid, meta, xform);
}
else
{
_xforms.DetachEntity(uid, xform, meta, null);
// base call bypasses IGameTiming.InPrediction check
// This is pretty janky and there should be a way for the client to dirty an entity outside of prediction
// TODO PREDICTION
base.Dirty(uid, xform, meta);
}
}
_queuedPredictedDeletionsSet.Clear();
}
base.TickUpdate(frameTime, noPredictions, histogram);
}
@@ -348,23 +317,18 @@ namespace Robust.Client.GameObjects
}
}
public override bool IsQueuedForDeletion(EntityUid uid)
=> QueuedDeletionsSet.Contains(uid) || _queuedPredictedDeletions.Contains(uid);
/// <inheritdoc />
public override void PredictedQueueDeleteEntity(Entity<MetaDataComponent?> ent)
public override void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
{
// Some UIs get disposed after entity-manager has shut down and already deleted all entities.
if (!Started)
if (IsQueuedForDeletion(ent.Owner)
|| !MetaQuery.Resolve(ent.Owner, ref ent.Comp1)
|| ent.Comp1.EntityLifeStage >= EntityLifeStage.Terminating
|| !TransformQuery.Resolve(ent.Owner, ref ent.Comp2))
{
return;
}
if (IsQueuedForDeletion(ent.Owner))
return;
if (!MetaQuery.Resolve(ent.Owner, ref ent.Comp, false))
return;
if (ent.Comp.NetEntity.IsClientSide())
if (ent.Comp1.NetEntity.IsClientSide())
{
// client-side QueueDeleteEntity re-fetches MetadataComp and checks IsClientSide().
// base call to skip that.
@@ -373,10 +337,7 @@ namespace Robust.Client.GameObjects
}
else
{
if (!_queuedPredictedDeletionsSet.Add(ent.Owner))
return;
_queuedPredictedDeletions.Enqueue(ent.Owner);
_xforms.DetachEntity(ent.Owner, ent.Comp2);
}
}
}

View File

@@ -294,16 +294,6 @@ namespace Robust.Client.GameObjects
LocalMatrix = Matrix3Helpers.CreateTransform(in offset, in rotation, in scale);
}
/// <summary>
/// If false, this will prevent any of this sprite's animated layers from looping their animation.
/// This will set <see cref="Layer.AutoAnimated"/> whenever any layer's animation finishes.
/// </summary>
/// <remarks>
/// If this is false, this effectively overrides each layer's own <see cref="Layer.Loop"/>.
/// </remarks>
[DataField]
public bool Loop = true;
/// <summary>
/// Update this sprite component to visibly match the current state of other at the time
/// this is called. Does not keep them perpetually in sync.
@@ -611,7 +601,6 @@ namespace Robust.Client.GameObjects
layer.RenderingStrategy = layerDatum.RenderingStrategy ?? layer.RenderingStrategy;
layer.Cycle = layerDatum.Cycle;
layer.Loop = layerDatum.Loop;
layer.Color = layerDatum.Color ?? layer.Color;
layer._rotation = layerDatum.Rotation ?? layer._rotation;
@@ -1168,15 +1157,6 @@ namespace Robust.Client.GameObjects
/// </remarks>
[ViewVariables] public bool Cycle;
/// <summary>
/// If false, this will prevent the layer's animation from looping.
/// This will set <see cref="AutoAnimated"/> to false once the animation finishes.
/// </summary>
/// <remarks>
/// This may be overriden by the parent's loop property.
/// </remarks>
[ViewVariables] public bool Loop = true;
// TODO SPRITE ACCESS
internal RSI.State? _actualState;
[ViewVariables] public RSI.State? ActualState => _actualState;
@@ -1356,8 +1336,6 @@ namespace Robust.Client.GameObjects
DirOffset = toClone.DirOffset;
_autoAnimated = toClone._autoAnimated;
RenderingStrategy = toClone.RenderingStrategy;
Cycle = toClone.Cycle;
Loop = toClone.Loop;
if (toClone.CopyToShaderParameters is { } copyToShaderParameters)
CopyToShaderParameters = new CopyToShaderParameters(copyToShaderParameters);
}
@@ -1685,25 +1663,17 @@ namespace Robust.Client.GameObjects
internal void AdvanceFrameAnimation(RSI.State state)
{
// Can't advance frames without more than 1 delay which is already checked above.
var delayCount = state.DelayCount;
while (AnimationTimeLeft < 0)
{
if (Reversed)
{
AnimationFrame -= 1;
// Animation finished, do we cycle back to positive or reset.
if (AnimationFrame < 0)
{
if (!Loop || !_parent.Loop)
{
// stop at first frame
AnimationFrame = 0;
AnimationTimeLeft = 0;
AutoAnimated = false;
return;
}
if (Cycle)
{
AnimationFrame = 1;
@@ -1721,17 +1691,9 @@ namespace Robust.Client.GameObjects
{
AnimationFrame += 1;
// Animation finished, do we reverse or reset.
if (AnimationFrame >= delayCount)
{
if (!Loop || !_parent.Loop)
{
// stop at last frame
AnimationFrame = delayCount - 1;
AnimationTimeLeft = 0;
AutoAnimated = false;
return;
}
if (Cycle)
{
AnimationFrame = delayCount - 2;
@@ -1749,7 +1711,6 @@ namespace Robust.Client.GameObjects
AnimationTimeLeft += state.GetDelay(AnimationFrame);
}
}
}
/// <summary>

View File

@@ -1,4 +1,3 @@
using System.Diagnostics.Contracts;
using Robust.Client.Graphics;
using Robust.Client.Map;
using Robust.Client.ResourceManagement;
@@ -10,14 +9,13 @@ namespace Robust.Client.GameObjects;
public sealed class MapSystem : SharedMapSystem
{
[Pure]
internal override MapId GetNextMapId()
protected override MapId GetNextMapId()
{
// Client-side map entities use negative map Ids to avoid conflict with server-side maps.
var id = new MapId(LastMapId - 1);
var id = new MapId(--LastMapId);
while (MapExists(id) || UsedIds.Contains(id))
{
id = new MapId(id.Value - 1);
id = new MapId(--LastMapId);
}
return id;
}

View File

@@ -29,8 +29,6 @@ namespace Robust.Client.GameObjects
component.Enabled = state.Enabled;
component.Offset = state.Offset;
component.Softness = state.Softness;
component.Falloff = state.Falloff;
component.CurveFactor = state.CurveFactor;
component.CastShadows = state.CastShadows;
component.Energy = state.Energy;
component.Radius = state.Radius;

View File

@@ -88,7 +88,6 @@ public sealed partial class SpriteSystem
target.Comp.RenderOrder = source.Comp.RenderOrder;
target.Comp.GranularLayersRendering = source.Comp.GranularLayersRendering;
target.Comp.Loop = source.Comp.Loop;
DirtyBounds(target!);
_tree.QueueTreeUpdate(target!);

View File

@@ -220,34 +220,24 @@ Had full state: {LastFullState != null}"
{
var compState = change.State;
if (compState is not IComponentDeltaState delta)
if (compState is IComponentDeltaState delta
&& compData.TryGetValue(change.NetID, out var old)) // May fail if relying on implicit data
{
compData[change.NetID] = compState;
continue;
DebugTools.Assert(old is not IComponentDeltaState, "last state is not a full state");
if (cloneDelta)
{
compState = delta.CreateNewFullState(old!);
}
else
{
delta.ApplyToFullState(old!);
compState = old;
}
DebugTools.Assert(compState is not IComponentDeltaState, "newly constructed state is not a full state");
}
if (!compData.TryGetValue(change.NetID, out var old))
{
// Either the server needs to ensure that the initial state it sends to a client is a full
// state, or the client needs to be able to construct an implicit full state (i.e., get-state
// code needs to be in shared code).
//
// Without this, the client won't be able to reset predicted changes made to this component.
DebugTools.Assert("Received delta state without having received or constructed an implicit full state");
continue;
}
DebugTools.Assert(old is not IComponentDeltaState, "last state is not a full state");
if (!cloneDelta)
{
delta.ApplyToFullState(old!);
continue;
}
var newFull = delta.CreateNewFullState(old!);
compData[change.NetID] = newFull;
DebugTools.Assert(newFull is not IComponentDeltaState, "constructed state is not a full state");
compData[change.NetID] = compState;
}
if (entityState.NetComponents == null)

View File

@@ -1,10 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Robust.Shared;
#if WINDOWS
using TerraFX.Interop.Windows;
using TerraFX.Interop.DirectX;
#endif
using Robust.Shared.Log;
namespace Robust.Client.Graphics.Clyde
{
@@ -17,8 +13,6 @@ namespace Robust.Client.Graphics.Clyde
private void InitGLContextManager()
{
CheckForceCompatMode();
// Advanced GL contexts currently disabled due to lack of testing etc.
if (OperatingSystem.IsWindows() && _cfg.GetCVar(CVars.DisplayAngle))
{
@@ -61,74 +55,6 @@ namespace Robust.Client.Graphics.Clyde
_glContext = new GLContextWindow(this);
}
private void CheckForceCompatMode()
{
#if WINDOWS
// Qualcomm (Snapdragon/Adreno) devices have broken OpenGL drivers on Windows.
if (CheckIsQualcommDevice())
{
_sawmillOgl.Info("We appear to be on a Qualcomm device. Enabling compat mode due to broken OpenGL driver");
_cfg.OverrideDefault(CVars.DisplayCompat, true);
}
#endif
}
#if WINDOWS
private static unsafe bool CheckIsQualcommDevice()
{
// Ideally we would check the OpenGL driver instead... but OpenGL is terrible so that's impossible.
// Let's just check with DXGI instead.
IDXGIFactory1* dxgiFactory;
ThrowIfFailed(
nameof(DirectX.CreateDXGIFactory1),
DirectX.CreateDXGIFactory1(Windows.__uuidof<IDXGIFactory1>(), (void**) &dxgiFactory));
try
{
uint idx = 0;
IDXGIAdapter* adapter;
while (dxgiFactory->EnumAdapters(idx, &adapter) != DXGI.DXGI_ERROR_NOT_FOUND)
{
try
{
DXGI_ADAPTER_DESC desc;
ThrowIfFailed("GetDesc", adapter->GetDesc(&desc));
var descString = ((ReadOnlySpan<char>)desc.Description).TrimEnd('\0');
if (descString.Contains("qualcomm", StringComparison.OrdinalIgnoreCase) ||
descString.Contains("snapdragon", StringComparison.OrdinalIgnoreCase) ||
descString.Contains("adreno", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
finally
{
adapter->Release();
}
idx += 1;
}
}
finally
{
dxgiFactory->Release();
}
return false;
}
private static void ThrowIfFailed(string methodName, HRESULT hr)
{
if (Windows.FAILED(hr))
{
Marshal.ThrowExceptionForHR(hr);
}
}
#endif
private struct GLContextSpec
{
public int Major;

View File

@@ -121,19 +121,6 @@ namespace Robust.Client.Graphics.Clyde
}
}
public void RenderNow(IRenderTarget renderTarget, Action<IRenderHandle> callback)
{
ClearRenderState();
_renderHandle.RenderInRenderTarget(
renderTarget,
() =>
{
callback(_renderHandle);
},
null);
}
private void RenderSingleWorldOverlay(Overlay overlay, Viewport vp, OverlaySpace space, in Box2 worldBox, in Box2Rotated worldBounds)
{
// Check that entity manager has started.

View File

@@ -451,8 +451,6 @@ namespace Robust.Client.Graphics.Clyde
var lastPower = float.NaN;
var lastColor = new Color(float.NaN, float.NaN, float.NaN, float.NaN);
var lastSoftness = float.NaN;
var lastFalloff = float.NaN;
var lastCurveFactor = float.NaN;
Texture? lastMask = null;
using (_prof.Group("Draw Lights"))
@@ -506,18 +504,6 @@ namespace Robust.Client.Graphics.Clyde
lightShader.SetUniformMaybe("lightSoftness", lastSoftness);
}
if (!MathHelper.CloseToPercent(lastFalloff, component.Falloff))
{
lastFalloff = component.Falloff;
lightShader.SetUniformMaybe("lightFalloff", lastFalloff);
}
if (!MathHelper.CloseToPercent(lastCurveFactor, component.CurveFactor))
{
lastCurveFactor = component.CurveFactor;
lightShader.SetUniformMaybe("lightCurveFactor", lastCurveFactor);
}
lightShader.SetUniformMaybe("lightCenter", lightPos);
lightShader.SetUniformMaybe("lightIndex",
component.CastShadows ? (i + 0.5f) / ShadowTexture.Height : -1);

View File

@@ -209,7 +209,6 @@ namespace Robust.Client.Graphics.Clyde
var pressure = estPixSize * size.X * size.Y;
var handle = AllocRid();
var renderTarget = new RenderTexture(size, textureObject, this, handle);
var data = new LoadedRenderTarget
{
IsWindow = false,
@@ -221,11 +220,10 @@ namespace Robust.Client.Graphics.Clyde
MemoryPressure = pressure,
ColorFormat = format.ColorFormat,
SampleParameters = sampleParameters,
Instance = new WeakReference<RenderTargetBase>(renderTarget),
Name = name,
};
//GC.AddMemoryPressure(pressure);
var renderTarget = new RenderTexture(size, textureObject, this, handle);
_renderTargets.Add(handle, data);
return renderTarget;
}
@@ -303,22 +301,10 @@ namespace Robust.Client.Graphics.Clyde
}
}
public IEnumerable<(RenderTargetBase, LoadedRenderTarget)> GetLoadedRenderTextures()
{
foreach (var loaded in _renderTargets.Values)
{
if (!loaded.Instance.TryGetTarget(out var instance))
continue;
yield return (instance, loaded);
}
}
internal sealed class LoadedRenderTarget
private sealed class LoadedRenderTarget
{
public bool IsWindow;
public WindowId WindowId;
public string? Name;
public Vector2i Size;
public bool IsSrgb;
@@ -339,11 +325,9 @@ namespace Robust.Client.Graphics.Clyde
public long MemoryPressure;
public TextureSampleParameters? SampleParameters;
public required WeakReference<RenderTargetBase> Instance;
}
internal abstract class RenderTargetBase : IRenderTarget
private abstract class RenderTargetBase : IRenderTarget
{
protected readonly Clyde Clyde;
private bool _disposed;
@@ -405,7 +389,7 @@ namespace Robust.Client.Graphics.Clyde
}
}
internal sealed class RenderTexture : RenderTargetBase, IRenderTexture
private sealed class RenderTexture : RenderTargetBase, IRenderTexture
{
public RenderTexture(Vector2i size, ClydeTexture texture, Clyde clyde, ClydeHandle handle)
: base(clyde, handle)

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Numerics;
using Robust.Client.UserInterface.CustomControls;
@@ -16,14 +15,10 @@ namespace Robust.Client.Graphics.Clyde
private readonly Dictionary<ClydeHandle, WeakReference<Viewport>> _viewports =
new();
private long _nextViewportId = 1;
private readonly ConcurrentQueue<ViewportDisposeData> _viewportDisposeQueue = new();
private Viewport CreateViewport(Vector2i size, TextureSampleParameters? sampleParameters = default, string? name = null)
{
var handle = AllocRid();
var viewport = new Viewport(_nextViewportId++, handle, name, this)
var viewport = new Viewport(handle, name, this)
{
Size = size,
RenderTarget = CreateRenderTarget(size,
@@ -64,43 +59,28 @@ namespace Robust.Client.Graphics.Clyde
private void FlushViewportDispose()
{
while (_viewportDisposeQueue.TryDequeue(out var data))
// Free of allocations unless a dead viewport is found.
List<ClydeHandle>? toRemove = null;
foreach (var (handle, viewportRef) in _viewports)
{
DisposeViewport(data);
if (!viewportRef.TryGetTarget(out _))
{
toRemove ??= new List<ClydeHandle>();
toRemove.Add(handle);
}
}
}
private void DisposeViewport(ViewportDisposeData disposeData)
{
_clydeSawmill.Warning($"Viewport {disposeData.Id} got leaked");
_viewports.Remove(disposeData.Handle);
if (disposeData.ClearEvent is not { } clearEvent)
if (toRemove == null)
{
return;
try
{
clearEvent(disposeData.ClearEventData);
}
catch (Exception ex)
foreach (var remove in toRemove)
{
_clydeSawmill.Error($"Caught exception while disposing viewport: {ex}");
_viewports.Remove(remove);
}
}
#if TOOLS
public void ViewportsClearAllCached()
{
foreach (var vpRef in _viewports.Values)
{
if (!vpRef.TryGetTarget(out var vp))
continue;
vp.FireClear();
}
}
#endif // TOOLS
private sealed class Viewport : IClydeViewport
{
private readonly ClydeHandle _handle;
@@ -126,20 +106,17 @@ namespace Robust.Client.Graphics.Clyde
public string? Name { get; }
public Viewport(long id, ClydeHandle handle, string? name, Clyde clyde)
public Viewport(ClydeHandle handle, string? name, Clyde clyde)
{
Name = name;
_handle = handle;
_clyde = clyde;
Id = id;
}
public Vector2i Size { get; set; }
public event Action<ClearCachedViewportResourcesEvent>? ClearCachedResources;
public Color? ClearColor { get; set; } = Color.Black;
public Vector2 RenderScale { get; set; } = Vector2.One;
public bool AutomaticRender { get; set; }
public long Id { get; }
void IClydeViewport.Render()
{
@@ -209,56 +186,20 @@ namespace Robust.Client.Graphics.Clyde
_clyde.RenderOverlaysDirect(this, control, handle, OverlaySpace.ScreenSpace, viewportBounds);
}
~Viewport()
{
_clyde._viewportDisposeQueue.Enqueue(DisposeData(referenceSelf: false));
}
public void Dispose()
{
GC.SuppressFinalize(this);
RenderTarget.Dispose();
LightRenderTarget.Dispose();
WallMaskRenderTarget.Dispose();
WallBleedIntermediateRenderTarget1.Dispose();
WallBleedIntermediateRenderTarget2.Dispose();
_clyde.DisposeViewport(DisposeData(referenceSelf: false));
}
private ViewportDisposeData DisposeData(bool referenceSelf)
{
return new ViewportDisposeData
{
Handle = _handle,
Id = Id,
ClearEvent = ClearCachedResources,
ClearEventData = MakeClearEvent(referenceSelf)
};
}
private ClearCachedViewportResourcesEvent MakeClearEvent(bool referenceSelf)
{
return new ClearCachedViewportResourcesEvent(Id, referenceSelf ? this : null);
}
public void FireClear()
{
ClearCachedResources?.Invoke(MakeClearEvent(referenceSelf: true));
_clyde._viewports.Remove(_handle);
}
IRenderTexture IClydeViewport.RenderTarget => RenderTarget;
IRenderTexture IClydeViewport.LightRenderTarget => LightRenderTarget;
public IEye? Eye { get; set; }
}
private sealed class ViewportDisposeData
{
public ClydeHandle Handle;
public long Id;
public Action<ClearCachedViewportResourcesEvent>? ClearEvent;
public ClearCachedViewportResourcesEvent ClearEventData;
}
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Robust.Client.Input;
@@ -102,10 +101,6 @@ namespace Robust.Client.Graphics.Clyde
_windowingThread = Thread.CurrentThread;
// Default to SDL3 on ARM64. GLFW is not feature complete there (lacking file dialog implementation)
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
_cfg.SetCVar(CVars.DisplayWindowingApi, "sdl3");
var windowingApi = _cfg.GetCVar(CVars.DisplayWindowingApi);
IWindowingImpl winImpl;
@@ -354,17 +349,15 @@ namespace Robust.Client.Graphics.Clyde
_windowHandles.Add(reg.Handle);
var rtId = AllocRid();
var renderTarget = new RenderWindow(this, rtId);
_renderTargets.Add(rtId, new LoadedRenderTarget
{
Size = reg.FramebufferSize,
IsWindow = true,
WindowId = reg.Id,
IsSrgb = true,
Instance = new WeakReference<RenderTargetBase>(renderTarget),
IsSrgb = true
});
reg.RenderTarget = renderTarget;
reg.RenderTarget = new RenderWindow(this, rtId);
_glContext!.WindowCreated(glSpec, reg);
}
@@ -381,8 +374,6 @@ namespace Robust.Client.Graphics.Clyde
if (reg.IsDisposed)
return;
_sawmillWin.Debug($"Destroying window {reg.Id}");
reg.IsDisposed = true;
_glContext!.WindowDestroyed(reg);
@@ -407,17 +398,10 @@ namespace Robust.Client.Graphics.Clyde
_glContext?.SwapAllBuffers();
}
public bool VsyncEnabled
private void VSyncChanged(bool newValue)
{
get => _vSync;
set
{
if (_vSync == value)
return;
_vSync = value;
_glContext?.UpdateVSync();
}
_vSync = newValue;
_glContext?.UpdateVSync();
}
private void WindowModeChanged(int mode)

View File

@@ -114,6 +114,7 @@ namespace Robust.Client.Graphics.Clyde
_proto.PrototypesReloaded += OnProtoReload;
_cfg.OnValueChanged(CVars.DisplayOGLCheckErrors, b => _checkGLErrors = b, true);
_cfg.OnValueChanged(CVars.DisplayVSync, VSyncChanged, true);
_cfg.OnValueChanged(CVars.DisplayWindowMode, WindowModeChanged, true);
_cfg.OnValueChanged(CVars.LightResolutionScale, LightResolutionScaleChanged, true);
_cfg.OnValueChanged(CVars.MaxShadowcastingLights, MaxShadowcastingLightsChanged, true);
@@ -127,11 +128,7 @@ namespace Robust.Client.Graphics.Clyde
// macOS cannot.
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux())
_cfg.OverrideDefault(CVars.DisplayThreadWindowApi, true);
#if MACOS
// Trust macOS to not need threaded window blitting.
// (threaded window blitting is a workaround to avoid having to frequently MakeCurrent() on Windows, as it is broken).
_cfg.OverrideDefault(CVars.DisplayThreadWindowBlit, false);
#endif
_threadWindowBlit = _cfg.GetCVar(CVars.DisplayThreadWindowBlit);
_threadWindowApi = _cfg.GetCVar(CVars.DisplayThreadWindowApi);

View File

@@ -34,7 +34,6 @@ namespace Robust.Client.Graphics.Clyde
public bool IsFocused => true;
private readonly List<IClydeWindow> _windows = new();
private int _nextWindowId = 2;
private long _nextViewportId = 1;
public ShaderInstance InstanceShader(ShaderSourceResource handle, bool? light = null, ShaderBlendMode? blend = null)
{
@@ -76,11 +75,6 @@ namespace Robust.Client.Graphics.Clyde
return [];
}
public IEnumerable<(Clyde.RenderTargetBase, Clyde.LoadedRenderTarget)> GetLoadedRenderTextures()
{
return [];
}
public ClydeDebugLayers DebugLayers { get; set; }
public string GetKeyName(Keyboard.Key key) => string.Empty;
@@ -246,7 +240,7 @@ namespace Robust.Client.Graphics.Clyde
public IClydeViewport CreateViewport(Vector2i size, TextureSampleParameters? sampleParameters,
string? name = null)
{
return new Viewport(_nextViewportId++, size);
return new Viewport(size);
}
public IEnumerable<IClydeMonitor> EnumerateMonitors()
@@ -313,19 +307,6 @@ namespace Robust.Client.Graphics.Clyde
public IFileDialogManagerImplementation? FileDialogImpl => null;
public bool VsyncEnabled { get; set; }
#if TOOLS
public void ViewportsClearAllCached()
{
throw new NotImplementedException();
}
#endif // TOOLS
public void RenderNow(IRenderTarget renderTarget, Action<IRenderHandle> callback)
{
}
private sealed class DummyCursor : ICursor
{
public void Dispose()
@@ -501,19 +482,15 @@ namespace Robust.Client.Graphics.Clyde
private sealed class Viewport : IClydeViewport
{
public Viewport(long id, Vector2i size)
public Viewport(Vector2i size)
{
Size = size;
Id = id;
}
public void Dispose()
{
ClearCachedResources?.Invoke(new ClearCachedViewportResourcesEvent(Id, null));
}
public long Id { get; }
public IRenderTexture RenderTarget { get; } =
new DummyRenderTexture(Vector2i.One, new DummyTexture(Vector2i.One));
@@ -522,7 +499,6 @@ namespace Robust.Client.Graphics.Clyde
public IEye? Eye { get; set; }
public Vector2i Size { get; }
public event Action<ClearCachedViewportResourcesEvent>? ClearCachedResources;
public Color? ClearColor { get; set; } = Color.Black;
public Vector2 RenderScale { get; set; }
public bool AutomaticRender { get; set; }

View File

@@ -102,8 +102,6 @@ namespace Robust.Client.Graphics.Clyde
{
var data = _windowData[reg.Id];
data.BlitDoneEvent?.Set();
// Set events so blit thread properly wakes up and notices it needs to shut down.
data.BlitStartEvent?.Set();
_windowData.Remove(reg.Id);
}
@@ -328,14 +326,11 @@ namespace Robust.Client.Graphics.Clyde
{
reg.RenderTexture?.Dispose();
reg.RenderTexture = Clyde.CreateRenderTarget(
reg.Reg.FramebufferSize,
new RenderTargetFormatParameters
{
ColorFormat = RenderTargetColorFormat.Rgba8Srgb,
HasDepthStencil = true
},
name: $"{reg.Reg.Id}-RenderTexture");
reg.RenderTexture = Clyde.CreateRenderTarget(reg.Reg.FramebufferSize, new RenderTargetFormatParameters
{
ColorFormat = RenderTargetColorFormat.Rgba8Srgb,
HasDepthStencil = true
});
// Necessary to correctly sync multi-context blitting.
reg.RenderTexture.MakeGLFence = true;
}

View File

@@ -0,0 +1,25 @@
/* SDL3-CS - C# Bindings for SDL3
*
* Copyright (c) 2024 Colin Jackson
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Colin "cryy22" Jackson <c@cryy22.art>
*
*/

View File

@@ -0,0 +1,56 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SDL3;
public static partial class SDL
{
// Extensions to SDL3-CS that aren't part of the main library.
[LibraryImport(nativeLibName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
public static unsafe partial void SDL_SetLogOutputFunction(delegate* unmanaged[Cdecl] <void*, int, SDL_LogPriority, byte*, void> callback, void* userdata);
[LibraryImport(nativeLibName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
public static unsafe partial SDLBool SDL_AddEventWatch(delegate* unmanaged[Cdecl] <void*, SDL_Event*, byte> filter, void* userdata);
[LibraryImport(nativeLibName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
public static unsafe partial void SDL_RemoveEventWatch(delegate* unmanaged[Cdecl] <void*, SDL_Event*, byte> filter, void* userdata);
[LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
public static unsafe partial void SDL_ShowFileDialogWithProperties(int type, delegate* unmanaged[Cdecl]<void*, byte**, int, void> callback, void* userdata, uint properties);
[LibraryImport(nativeLibName, EntryPoint = "SDL_WaitEvent")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
public static partial SDLBool SDL_WaitEventRef(ref SDL_Event @event);
public const byte SDL_BUTTON_LEFT = 1;
public const byte SDL_BUTTON_MIDDLE = 2;
public const byte SDL_BUTTON_RIGHT = 3;
public const byte SDL_BUTTON_X1 = 4;
public const byte SDL_BUTTON_X2 = 5;
public const int SDL_GL_CONTEXT_PROFILE_CORE = 0x0001;
public const int SDL_GL_CONTEXT_PROFILE_COMPATIBILITY = 0x0002;
public const int SDL_GL_CONTEXT_PROFILE_ES = 0x0004;
public const int SDL_GL_CONTEXT_DEBUG_FLAG = 0x0001;
public const int SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG = 0x0002;
public const int SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG = 0x0004;
public const int SDL_GL_CONTEXT_RESET_ISOLATION_FLAG = 0x0008;
public const int SDL_FILEDIALOG_OPENFILE = 0;
public const int SDL_FILEDIALOG_SAVEFILE = 1;
public const int SDL_FILEDIALOG_OPENFOLDER = 2;
public const string SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER = "SDL.filedialog.nfilters";
public const string SDL_PROP_FILE_DIALOG_FILTERS_POINTER = "SDL.filedialog.filters";
public static int SDL_VERSIONNUM_MAJOR(int version) => version / 1000000;
public static int SDL_VERSIONNUM_MINOR(int version) => version / 1000 % 1000;
public static int SDL_VERSIONNUM_MICRO(int version) => version % 1000;
}

File diff suppressed because it is too large Load Diff

View File

@@ -81,11 +81,6 @@ internal partial class Clyde
case EventQuit:
ProcessEventQuit();
break;
#if MACOS
case EventWindowDestroyed:
ProcessEventWindowDestroyed();
break;
#endif
default:
_sawmill.Error($"Unknown SDL3 event type: {evb.GetType().Name}");
break;
@@ -260,15 +255,5 @@ internal partial class Clyde
{
_clyde.SendInputModeChanged();
}
#if MACOS
private void ProcessEventWindowDestroyed()
{
// For some reason, on macOS, closing a secondary window
// causes the GL context on the primary thread to crap itself.
// Rebinding it seems to fix it.
GLMakeContextCurrent(_clyde._mainWindow);
}
#endif
}
}

View File

@@ -46,10 +46,6 @@ internal partial class Clyde
}
}
// NOTE: Giving a parent window is required to avoid the file dialog being blocking on macOS.
var mainWindow = (Sdl3WindowReg)_clyde._mainWindow!;
SDL.SDL_SetPointerProperty(props, SDL.SDL_PROP_FILE_DIALOG_WINDOW_POINTER, mainWindow.Sdl3Window);
var task = ShowFileDialogWithProperties(type, props);
SDL.SDL_DestroyProperties(props);

View File

@@ -278,9 +278,5 @@ internal partial class Clyde
private sealed class EventKeyMapChanged : EventBase;
private sealed class EventQuit : EventBase;
#if MACOS
private sealed class EventWindowDestroyed : EventBase;
#endif
}
}

View File

@@ -7,10 +7,8 @@ using Robust.Shared.Maths;
using SDL3;
using TerraFX.Interop.Windows;
using TerraFX.Interop.Xlib;
#if WINDOWS
using BOOL = TerraFX.Interop.Windows.BOOL;
using Windows = TerraFX.Interop.Windows.Windows;
#endif
using GLAttr = SDL3.SDL.SDL_GLAttr;
using X11Window = TerraFX.Interop.Xlib.Window;
@@ -144,12 +142,9 @@ internal partial class Clyde
});
}
private void WinThreadWinDestroy(CmdWinDestroy cmd)
private static void WinThreadWinDestroy(CmdWinDestroy cmd)
{
SDL.SDL_DestroyWindow(cmd.Window);
#if MACOS
SendEvent(new EventWindowDestroyed());
#endif
}
private (nint window, nint context) CreateSdl3WindowForRenderer(
@@ -466,7 +461,6 @@ internal partial class Clyde
var reg = (Sdl3WindowReg)window;
var windowPtr = WinPtr(reg);
#if WINDOWS
// On Windows, SwapBuffers does not correctly sync to the DWM compositor.
// This means OpenGL vsync is effectively broken by default on Windows.
// We manually sync via DwmFlush(). GLFW does this automatically, SDL3 does not.
@@ -479,7 +473,7 @@ internal partial class Clyde
var dwmFlush = false;
var swapInterval = 0;
if (!reg.Fullscreen && reg.SwapInterval > 0)
if (OperatingSystem.IsWindows() && !reg.Fullscreen && reg.SwapInterval > 0)
{
BOOL compositing;
// 6.2 is Windows 8
@@ -498,12 +492,9 @@ internal partial class Clyde
swapInterval = reg.SwapInterval;
}
}
#endif
//_sawmill.Debug($"Swapping: {window.Id} @ {_clyde._gameTiming.CurFrame}");
SDL.SDL_GL_SwapWindow(windowPtr);
#if WINDOWS
if (dwmFlush)
{
var i = swapInterval;
@@ -514,7 +505,6 @@ internal partial class Clyde
SDL.SDL_GL_SetSwapInterval(swapInterval);
}
#endif
}
public uint? WindowGetX11Id(WindowReg window)
@@ -557,18 +547,17 @@ internal partial class Clyde
public void TextInputSetRect(WindowReg reg, UIBox2i rect, int cursor)
{
var ratio = ((Sdl3WindowReg)reg).PixelRatio;
SendCmd(new CmdTextInputSetRect
{
Window = WinPtr(reg),
Rect = new SDL.SDL_Rect
{
x = (int)(rect.Left / ratio.X),
y = (int)(rect.Top / ratio.Y),
w = (int)(rect.Width / ratio.X),
h = (int)(rect.Height / ratio.Y)
x = rect.Left,
y = rect.Top,
w = rect.Width,
h = rect.Height
},
Cursor = (int)(cursor / ratio.X)
Cursor = cursor
});
}

View File

@@ -61,10 +61,6 @@ internal partial class Clyde
// https://github.com/libsdl-org/SDL/issues/11813
SDL.SDL_SetHint(SDL.SDL_HINT_WINDOWS_GAMEINPUT, "0");
#if MACOS
SDL.SDL_SetHint(SDL.SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, "1");
#endif
var res = SDL.SDL_Init(SDL.SDL_InitFlags.SDL_INIT_VIDEO | SDL.SDL_InitFlags.SDL_INIT_EVENTS);
if (!res)
{

View File

@@ -55,7 +55,6 @@ namespace Robust.Client.Graphics
Texture GetStockTexture(ClydeStockTexture stockTexture);
IEnumerable<(Clyde.Clyde.ClydeTexture, Clyde.Clyde.LoadedTexture)> GetLoadedTextures();
IEnumerable<(Clyde.Clyde.RenderTargetBase, Clyde.Clyde.LoadedRenderTarget)> GetLoadedRenderTextures();
ClydeDebugLayers DebugLayers { get; set; }
@@ -73,20 +72,5 @@ namespace Robust.Client.Graphics
void RunOnWindowThread(Action action);
IFileDialogManagerImplementation? FileDialogImpl { get; }
bool VsyncEnabled { get; set; }
// Viewports
#if TOOLS
/// <summary>
/// Fires <see cref="IClydeViewport.ClearCachedResources"/> on all viewports. For debugging.
/// </summary>
void ViewportsClearAllCached();
#endif // TOOLS
void RenderNow(IRenderTarget renderTarget, Action<IRenderHandle> callback);
}
}

View File

@@ -13,11 +13,6 @@ namespace Robust.Client.Graphics
/// </summary>
public interface IClydeViewport : IDisposable
{
/// <summary>
/// A unique ID for this viewport. No other viewport with this ID can ever exist in the app lifetime.
/// </summary>
long Id { get; }
/// <summary>
/// The render target that is rendered to when rendering this viewport.
/// </summary>
@@ -27,16 +22,6 @@ namespace Robust.Client.Graphics
IEye? Eye { get; set; }
Vector2i Size { get; }
/// <summary>
/// Raised when the viewport indicates that any cached rendering resources (e.g. render targets)
/// should be purged.
/// </summary>
/// <remarks>
/// This event is raised if the viewport is disposed (manually or via finalization).
/// However, code should expect this event to be raised at any time, even if the viewport is not disposed fully.
/// </remarks>
event Action<ClearCachedViewportResourcesEvent> ClearCachedResources;
/// <summary>
/// Color to clear the render target to before rendering. If null, no clearing will happen.
/// </summary>
@@ -100,23 +85,4 @@ namespace Robust.Client.Graphics
IViewportControl control,
in UIBox2i viewportBounds);
}
public struct ClearCachedViewportResourcesEvent
{
/// <summary>
/// The <see cref="IClydeViewport.Id"/> of the viewport.
/// </summary>
public readonly long ViewportId;
/// <summary>
/// The viewport itself. This is not available if the viewport was disposed.
/// </summary>
public readonly IClydeViewport? Viewport;
internal ClearCachedViewportResourcesEvent(long viewportId, IClydeViewport? viewport)
{
ViewportId = viewportId;
Viewport = viewport;
}
}
}

View File

@@ -197,13 +197,6 @@ namespace Robust.Client.Input
locId += "-linux";
}
#if MACOS
if (key == Key.Alt)
{
locId += "-mac";
}
#endif
if (loc.TryGetString(locId, out var name))
return name;

View File

@@ -562,7 +562,7 @@ namespace Robust.Client.Placement
}
coordinates = InputManager.MouseScreenPosition;
return coordinates.IsValid;
return true;
}
private bool CurrentEraserMouseCoordinates(out EntityCoordinates coordinates)

View File

@@ -11,6 +11,7 @@ namespace Robust.Client.Replays.Commands;
public abstract class BaseReplayCommand : LocalizedCommands
{
[Dependency] protected readonly IReplayPlaybackManager PlaybackManager = default!;
protected ILocalizationManager Loc => LocalizationManager;
public override string Description => Loc.GetString($"cmd-{Command.Replace('_','-')}-desc");

View File

@@ -18,9 +18,8 @@
<PackageReference Include="SpaceWizards.NFluidsynth" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" />
<PackageReference Include="OpenToolkit.Graphics" PrivateAssets="compile" />
<PackageReference Include="OpenTK.Audio.OpenAL" PrivateAssets="compile" />
<PackageReference Include="OpenTK.OpenAL" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.SharpFont" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sdl" PrivateAssets="compile" />
<PackageReference Include="Robust.Natives" />
<PackageReference Include="TerraFX.Interop.Windows" PrivateAssets="compile" />
<PackageReference Condition="'$(RobustToolsBuild)' == 'True'" Include="JetBrains.Profiler.Api" PrivateAssets="compile" />
@@ -64,8 +63,6 @@
<RobustLinkAssemblies Include="TerraFX.Interop.Windows" />
<RobustLinkAssemblies Include="TerraFX.Interop.Xlib" />
<RobustLinkAssemblies Include="OpenToolkit.Graphics" />
<RobustLinkAssemblies Include="SpaceWizards.Sdl" />
<RobustLinkAssemblies Include="SpaceWizards.SharpFont" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Properties.targets" />

View File

@@ -1,192 +0,0 @@
using System.Numerics;
using Robust.Shared.Maths;
namespace Robust.Client.UserInterface;
/// <summary>
/// Defines an axis that certain controls can be laid out along.
/// </summary>
/// <seealso cref="IAxisImplementation"/>
public enum Axis : byte
{
/// <summary>
/// Items are laid out left to right.
/// </summary>
Horizontal,
/// <summary>
/// Items are laid out right to left.
/// </summary>
HorizontalReverse,
/// <summary>
/// Items are laid out top to bottom.
/// </summary>
Vertical,
/// <summary>
/// Items are laid out bottom to top.
/// </summary>
VerticalReverse,
}
/// <summary>
/// Interface that implements the rules of an <see cref="Axis"/>.
/// </summary>
/// <remarks>
/// <para>
/// To make it easier to write code that supports all 4 layout axis, layout code is advised to use generics over this
/// type and its implementors.
/// </para>
/// <para>
/// An axis has a "main" and a "cross" axis. For example,
/// <see cref="HorizontalAxis"/> has the main axis go left to right, and the cross axis go top to bottom.
/// </para>
/// <para>
/// The functions in this interface primarily allow converting between "UI space" (the normal UI coordinate system) and
/// "axis space" (same as UI space for <see cref="HorizontalAxis"/>). This allows you to write all code as if you're
/// doing only horizontal layout, but automatically have it work on all axis.
/// </para>
/// </remarks>
/// <seealso cref="HorizontalAxis"/>
/// <seealso cref="HorizontalReverseAxis"/>
/// <seealso cref="VerticalAxis"/>
/// <seealso cref="VerticalReverseAxis"/>
public interface IAxisImplementation
{
//
// To/from axis space conversions
//
/// <summary>
/// Convert a size value (e.g. from <see cref="Control.DesiredSize"/>) from UI space to axis space.
/// </summary>
static abstract Vector2 SizeToAxis(Vector2 size);
/// <summary>
/// Convert a size value (e.g. for <see cref="Control.Measure"/>) from axis space to UI space.
/// </summary>
static abstract Vector2 SizeFromAxis(Vector2 size);
/// <summary>
/// Convert a box (e.g. for <see cref="Control.Arrange"/>) from axis space to UI space.
/// </summary>
/// <param name="box">The box to convert, in axis space.</param>
/// <param name="spaceSize">The amount of space, in UI space, that the layout is happening relative to.</param>
static abstract UIBox2 BoxFromAxis(UIBox2 box, Vector2 spaceSize);
//
// Control
//
/// <summary>
/// Gets the "expand flag" (<see cref="Control.HorizontalExpand"/> or <see cref="Control.VerticalExpand"/>) for a
/// control that is appropriate for the main axis.
/// </summary>
static abstract bool GetMainExpandFlag(Control control);
}
/// <summary>
/// Axis implementation for <see cref="Axis.Horizontal"/>.
/// </summary>
public struct HorizontalAxis : IAxisImplementation
{
public static Vector2 SizeToAxis(Vector2 size)
{
return size;
}
public static Vector2 SizeFromAxis(Vector2 size)
{
return size;
}
public static UIBox2 BoxFromAxis(UIBox2 box, Vector2 spaceSize)
{
return box;
}
public static bool GetMainExpandFlag(Control control)
{
return control.HorizontalExpand;
}
}
/// <summary>
/// Axis implementation for <see cref="Axis.HorizontalReverse"/>.
/// </summary>
public struct HorizontalReverseAxis : IAxisImplementation
{
public static Vector2 SizeToAxis(Vector2 size)
{
return size;
}
public static Vector2 SizeFromAxis(Vector2 size)
{
return size;
}
public static UIBox2 BoxFromAxis(UIBox2 box, Vector2 spaceSize)
{
return new UIBox2(spaceSize.X - box.Right, box.Top, spaceSize.X - box.Left, box.Bottom);
}
public static bool GetMainExpandFlag(Control control)
{
return control.HorizontalExpand;
}
}
/// <summary>
/// Axis implementation for <see cref="Axis.Vertical"/>.
/// </summary>
public struct VerticalAxis : IAxisImplementation
{
public static Vector2 SizeToAxis(Vector2 size)
{
return new Vector2(size.Y, size.X);
}
public static Vector2 SizeFromAxis(Vector2 size)
{
return new Vector2(size.Y, size.X);
}
public static UIBox2 BoxFromAxis(UIBox2 box, Vector2 spaceSize)
{
return new UIBox2(box.Top, box.Left, box.Bottom, box.Right);
}
public static bool GetMainExpandFlag(Control control)
{
return control.VerticalExpand;
}
}
/// <summary>
/// Axis implementation for <see cref="Axis.VerticalReverse"/>.
/// </summary>
public struct VerticalReverseAxis : IAxisImplementation
{
public static Vector2 SizeToAxis(Vector2 size)
{
return new Vector2(size.Y, size.X);
}
public static Vector2 SizeFromAxis(Vector2 size)
{
return new Vector2(size.Y, size.X);
}
public static UIBox2 BoxFromAxis(UIBox2 box, Vector2 spaceSize)
{
return new UIBox2(box.Top, spaceSize.Y - box.Right, box.Bottom, spaceSize.Y - box.Left);
}
public static bool GetMainExpandFlag(Control control)
{
return control.VerticalExpand;
}
}

View File

@@ -1049,7 +1049,7 @@ namespace Robust.Client.UserInterface
Ignore = 2,
}
public sealed class OrderedChildCollection : ICollection<Control>, IReadOnlyList<Control>
public sealed class OrderedChildCollection : ICollection<Control>, IReadOnlyCollection<Control>
{
private readonly Control Owner;
@@ -1101,7 +1101,6 @@ namespace Robust.Client.UserInterface
int ICollection<Control>.Count => Owner.ChildCount;
int IReadOnlyCollection<Control>.Count => Owner.ChildCount;
public Control this[int index] => Owner._orderedChildren[index];
public bool IsReadOnly => false;

View File

@@ -27,6 +27,8 @@ namespace Robust.Client.UserInterface.Controls
/// </remarks>
public AlignMode Align { get; set; }
private bool Vertical => Orientation == LayoutOrientation.Vertical;
public LayoutOrientation Orientation
{
get => _orientation;
@@ -54,24 +56,19 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
if (Orientation == LayoutOrientation.Vertical)
// Account for separation.
var separation = ActualSeparation * (Children.Where(c => c.Visible).Count() - 1);
var desiredSize = Vector2.Zero;
if (Vertical)
{
return MeasureItems<VerticalAxis>(availableSize);
desiredSize.Y += separation;
availableSize.Y = Math.Max(0, availableSize.Y - separation);
}
else
{
return MeasureItems<HorizontalAxis>(availableSize);
desiredSize.X += separation;
availableSize.X = Math.Max(0, availableSize.X - separation);
}
}
private Vector2 MeasureItems<TAxis>(Vector2 availableSize) where TAxis : IAxisImplementation
{
availableSize = TAxis.SizeToAxis(availableSize);
// Account for separation.
var separation = ActualSeparation * (Children.Where(c => c.Visible).Count() - 1);
var desiredSize = new Vector2(separation, 0);
availableSize.X = Math.Max(0, availableSize.X) - separation;
// First, we measure non-stretching children.
foreach (var child in Children)
@@ -79,74 +76,46 @@ namespace Robust.Client.UserInterface.Controls
if (!child.Visible)
continue;
child.Measure(TAxis.SizeFromAxis(availableSize));
var childDesired = TAxis.SizeToAxis(child.DesiredSize);
child.Measure(availableSize);
desiredSize.X += childDesired.X;
desiredSize.Y = Math.Max(desiredSize.Y, childDesired.Y);
availableSize.X = Math.Max(0, availableSize.X - childDesired.X);
if (Vertical)
{
desiredSize.Y += child.DesiredSize.Y;
desiredSize.X = Math.Max(desiredSize.X, child.DesiredSize.X);
availableSize.Y = Math.Max(0, availableSize.Y - child.DesiredSize.Y);
}
else
{
desiredSize.X += child.DesiredSize.X;
desiredSize.Y = Math.Max(desiredSize.Y, child.DesiredSize.Y);
availableSize.X = Math.Max(0, availableSize.X - child.DesiredSize.X);
}
}
return TAxis.SizeFromAxis(desiredSize);
return desiredSize;
}
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
var separation = ActualSeparation;
var visibleChildCount = Children.Where(c => c.Visible).Count();
if (Orientation == LayoutOrientation.Vertical)
{
LayOutItems<VerticalAxis>(default, finalSize, Align, Children, 0, ChildCount, separation);
}
else
{
LayOutItems<HorizontalAxis>(default, finalSize, Align, Children, 0, ChildCount, separation);
}
return finalSize;
}
internal static void LayOutItems<TAxis>(
Vector2 baseOffset,
Vector2 finalSize,
AlignMode align,
OrderedChildCollection children,
int start,
int end,
float separation,
Vector2? fixedSize = null)
where TAxis : IAxisImplementation
{
var realFinalSize = finalSize;
finalSize = TAxis.SizeToAxis(finalSize);
fixedSize = fixedSize == null ? null : TAxis.SizeToAxis(fixedSize.Value);
var visibleChildCount = 0;
for (var i = start; i < end; i++)
{
if (children[i].Visible)
visibleChildCount += 1;
}
var stretchAvail = finalSize.X;
var stretchAvail = Vertical ? finalSize.Y : finalSize.X;
stretchAvail -= separation * (visibleChildCount - 1);
stretchAvail = Math.Max(0, stretchAvail);
// Step one: figure out the sizes of all our children and whether they want to stretch.
var sizeList = new List<(Control control, float size, bool stretch)>(visibleChildCount);
var totalStretchRatio = 0f;
for (var i = start; i < end; i++)
foreach (var child in Children)
{
var child = children[i];
if (!child.Visible)
continue;
bool stretch = TAxis.GetMainExpandFlag(child);
bool stretch = Vertical ? child.VerticalExpand : child.HorizontalExpand;
if (!stretch)
{
var measuredSize = fixedSize ?? TAxis.SizeToAxis(child.DesiredSize);
var size = measuredSize.X;
var size = Vertical ? child.DesiredSize.Y : child.DesiredSize.X;
size = Math.Clamp(size, 0, stretchAvail);
stretchAvail -= size;
sizeList.Add((child, size, false));
@@ -177,8 +146,7 @@ namespace Robust.Client.UserInterface.Controls
continue;
var share = stretchAvail * control.SizeFlagsStretchRatio / totalStretchRatio;
var measuredSize = fixedSize ?? TAxis.SizeToAxis(control.DesiredSize);
var desired = measuredSize.X;
var desired = Vertical ? control.DesiredSize.Y : control.DesiredSize.X;
if (share >= desired)
{
sizeList[i] = (control, share, true);
@@ -196,7 +164,7 @@ namespace Robust.Client.UserInterface.Controls
else
{
// No stretching children -> offset the children based on the alignment.
switch (align)
switch (Align)
{
case AlignMode.Begin:
break;
@@ -222,14 +190,22 @@ namespace Robust.Client.UserInterface.Controls
first = false;
var targetBox = TAxis.BoxFromAxis(new UIBox2(offset, 0, offset + size, finalSize.Y), realFinalSize);
targetBox = targetBox.Translated(baseOffset);
UIBox2 targetBox;
if (Vertical)
{
targetBox = new UIBox2(0, offset, finalSize.X, offset + size);
}
else
{
targetBox = new UIBox2(offset, 0, offset + size, finalSize.Y);
}
control.Arrange(targetBox);
offset += size;
}
return finalSize;
}
public enum AlignMode : byte

View File

@@ -121,14 +121,11 @@ namespace Robust.Client.UserInterface.Controls
{
if (show)
{
if (Root == null)
throw new InvalidOperationException("No UI root! We can't pop up!");
var globalPos = GlobalPosition;
_popupVBox.Measure(Vector2Helpers.Infinity);
var (minX, minY) = _popupVBox.DesiredSize;
var box = UIBox2.FromDimensions(globalPos, new Vector2(Math.Max(minX, Width), minY));
Root.ModalRoot.AddChild(_popup);
UserInterfaceManager.ModalRoot.AddChild(_popup);
_popup.Open(box);
}
else
@@ -139,7 +136,7 @@ namespace Robust.Client.UserInterface.Controls
private void OnPopupHide()
{
_popup.Orphan();
UserInterfaceManager.ModalRoot.RemoveChild(_popup);
}

View File

@@ -135,13 +135,7 @@ namespace Robust.Client.UserInterface.Controls
ClydeWindow.Resized += OnWindowResized;
_root = UserInterfaceManager.CreateWindowRoot(ClydeWindow);
_root.CreateRootControls();
// Add ourselves *after* creating the root.
// This way root controls are valid in EnteredTree().
// We have to re-organize the controls after, of course.
_root.AddChild(this);
SetPositionFirst();
// Resize the window by our UIScale
ClydeWindow.Size = new((int)(ClydeWindow.Size.X * UIScale), (int)(ClydeWindow.Size.Y * UIScale));

View File

@@ -153,16 +153,13 @@ namespace Robust.Client.UserInterface.Controls
{
if (show)
{
if (Root == null)
throw new InvalidOperationException("No UI root! We can't pop up!");
var globalPos = GlobalPosition;
globalPos.Y += Size.Y + 1; // Place it below us, with a safety margin.
globalPos.Y -= Margin.SumVertical;
OptionsScroll.Measure(Window?.Size ?? Vector2Helpers.Infinity);
var (minX, minY) = OptionsScroll.DesiredSize;
var box = UIBox2.FromDimensions(globalPos, new Vector2(Math.Max(minX, Width), minY));
Root.ModalRoot.AddChild(_popup);
UserInterfaceManager.ModalRoot.AddChild(_popup);
_popup.Open(box);
}
else
@@ -173,7 +170,7 @@ namespace Robust.Client.UserInterface.Controls
private void OnPopupHide()
{
_popup.Orphan();
UserInterfaceManager.ModalRoot.RemoveChild(_popup);
}
private void ButtonOnPressed(ButtonEventArgs obj)

View File

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics.Contracts;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.RichText;
@@ -155,7 +154,6 @@ namespace Robust.Client.UserInterface.Controls
public void SetMessage(Index index, FormattedMessage message, Type[]? tagsAllowed = null, Color? defaultColor = null)
{
var atBottom = !_scrollDownButton.Visible;
var oldEntry = _entries[index];
var font = _getFont();
_totalContentHeight -= oldEntry.Height + font.GetLineSeparation(UIScale);
@@ -166,10 +164,6 @@ namespace Robust.Client.UserInterface.Controls
_entries[index] = entry;
AddNewItemHeight(font, in entry);
_scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight);
if (atBottom)
_scrollBar.Value = _scrollBar.MaxValue;
}
private void AddNewItemHeight(Font font, in RichTextEntry entry)
@@ -284,7 +278,7 @@ namespace Robust.Client.UserInterface.Controls
}
}
[Pure]
[System.Diagnostics.Contracts.Pure]
private Font _getFont()
{
if (TryGetStyleProperty<Font>("font", out var font))
@@ -295,7 +289,7 @@ namespace Robust.Client.UserInterface.Controls
return UserInterfaceManager.ThemeDefaults.DefaultFont;
}
[Pure]
[System.Diagnostics.Contracts.Pure]
private StyleBox? _getStyleBox()
{
if (StyleBoxOverride != null)
@@ -307,14 +301,14 @@ namespace Robust.Client.UserInterface.Controls
return box;
}
[Pure]
[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);
}
[Pure]
[System.Diagnostics.Contracts.Pure]
private UIBox2 _getContentBox()
{
var style = _getStyleBox();

View File

@@ -30,7 +30,7 @@ namespace Robust.Client.UserInterface.Controls
public bool CloseOnEscape { get; set; } = true;
public virtual void Open(UIBox2? box = null, Vector2? altPos = null, Vector2? altPosUp = null)
public virtual void Open(UIBox2? box = null, Vector2? altPos = null)
{
if (Visible)
{
@@ -44,12 +44,10 @@ namespace Robust.Client.UserInterface.Controls
if (box != null &&
(_desiredSize != box.Value.Size ||
PopupContainer.GetPopupOrigin(this) != box.Value.TopLeft ||
PopupContainer.GetAltOrigin(this) != altPos ||
PopupContainer.GetAltOriginUp(this) != altPosUp))
PopupContainer.GetAltOrigin(this) != altPos))
{
PopupContainer.SetPopupOrigin(this, box.Value.TopLeft);
PopupContainer.SetAltOrigin(this, altPos);
PopupContainer.SetAltOriginUp(this, altPosUp);
_desiredSize = box.Value.Size;
InvalidateMeasure();

View File

@@ -27,13 +27,6 @@ namespace Robust.Client.UserInterface.Controls
public static readonly AttachedProperty AltOriginProperty = AttachedProperty.Create("AltOrigin",
typeof(PopupContainer), typeof(Vector2?), changed: PopupOriginChangedCallback);
/// <summary>
/// Alternative position to bottom-left-align the popup if <see cref="PopupOriginProperty"/>
/// would put it off-screen vertically.
/// </summary>
public static readonly AttachedProperty AltOriginUpProperty = AttachedProperty.Create("AltOriginUp",
typeof(PopupContainer), typeof(Vector2?), changed: PopupOriginChangedCallback);
public PopupContainer()
{
RectClipContent = true;
@@ -54,21 +47,11 @@ namespace Robust.Client.UserInterface.Controls
return control.GetValue<Vector2?>(AltOriginProperty);
}
public static Vector2? GetAltOriginUp(Control control)
{
return control.GetValue<Vector2?>(AltOriginUpProperty);
}
public static void SetAltOrigin(Control control, Vector2? origin)
{
control.SetValue(AltOriginProperty, origin);
}
public static void SetAltOriginUp(Control control, Vector2? origin)
{
control.SetValue(AltOriginUpProperty, origin);
}
private static void PopupOriginChangedCallback(Control owner, AttachedPropertyChangedEventArgs eventArgs)
{
if (owner.Parent is PopupContainer container)
@@ -84,58 +67,47 @@ namespace Robust.Client.UserInterface.Controls
var size = child.DesiredSize;
var offset = child.GetValue<Vector2>(PopupOriginProperty);
var altPos = child.GetValue<Vector2?>(AltOriginProperty);
var altPosUp = child.GetValue<Vector2?>(AltOriginUpProperty);
var box = UIBox2.FromDimensions(offset, size);
var (r, b) = size + offset; // bottom right corner.
var isAltPos = false;
var isAltPosUp = false;
// Clamp the right edge.
if (box.Right > Width)
if (r > Width)
{
// Try to position at alt pos.
if (altPos != null && altPos.Value.X - size.X > 0)
{
// There is horizontal room at the alt pos so there we go.
isAltPos = true;
box = UIBox2.FromDimensions(new Vector2(altPos.Value.X - size.X, altPos.Value.Y), size);
offset = new Vector2(altPos.Value.X - size.X, altPos.Value.Y);
(_, b) = size + offset;
}
else
{
box = box.Translated(new Vector2(-(box.Right - Width), 0));
offset -= new Vector2(r - Width, 0);
}
}
// Clamp the bottom edge.
if (box.Bottom > Height)
if (b > Height)
{
// Try to position at alt pos.
if (altPosUp != null && altPosUp.Value.Y - size.Y > 0)
{
// There is vertical room at the alt pos so there we go.
isAltPosUp = true;
box = UIBox2.FromDimensions(new Vector2(altPosUp.Value.X, altPosUp.Value.Y - size.Y), size);
}
else
{
box = box.Translated(new Vector2(0, -(box.Bottom - Height)));
}
offset -= new Vector2(0, b - Height);
}
// Try to clamp the left edge.
if (box.Left < 0 && !isAltPos)
if (offset.X < 0 && !isAltPos)
{
box = box.Translated(new Vector2(-offset.X, 0));
offset -= new Vector2(offset.X, 0);
}
// Try to clamp the top edge.
if (box.Top < 0 && !isAltPosUp)
if (offset.Y < 0)
{
box = box.Translated(new Vector2(0, -offset.Y));
offset -= new Vector2(0, offset.Y);
}
child.Arrange(box);
child.Arrange(UIBox2.FromDimensions(offset, size));
}
return finalSize;

View File

@@ -74,7 +74,7 @@ namespace Robust.Client.UserInterface.Controls
else
{
_updating = true;
Value = UIAnimations.LerpAnimate(Value, ValueTarget, args.DeltaSeconds, 15);
Value = MathHelper.Lerp(Value, ValueTarget, Math.Min(args.DeltaSeconds * 15, 1));
_updating = false;
}
}

View File

@@ -1,270 +0,0 @@
using System;
using System.Numerics;
using Robust.Shared.Maths;
namespace Robust.Client.UserInterface.Controls;
internal sealed class TableContainer : Container
{
private int _columns = 1;
/// <summary>
/// The absolute minimum width a column can be forced to.
/// </summary>
/// <remarks>
/// <para>
/// If a column *asks* for less width than this (small contents), it can still be smaller.
/// But if it asks for more it cannot go below this width.
/// </para>
/// </remarks>
public float MinForcedColumnWidth { get; set; } = 50;
// Scratch space used while calculating layout, cached to avoid regular allocations during layout pass.
private ColumnData[] _columnDataCache = [];
private RowData[] _rowDataCache = [];
/// <summary>
/// How many columns should be displayed.
/// </summary>
public int Columns
{
get => _columns;
set
{
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1, nameof(value));
_columns = value;
}
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
ResetCachedArrays();
// Do a first pass measuring all child controls as if they're given infinite space.
// This gives us a maximum width the columns want, which we use to proportion them later.
var columnIdx = 0;
foreach (var child in Children)
{
ref var column = ref _columnDataCache[columnIdx];
child.Measure(new Vector2(float.PositiveInfinity, float.PositiveInfinity));
column.MaxWidth = Math.Max(column.MaxWidth, child.DesiredSize.X);
columnIdx += 1;
if (columnIdx == _columns)
columnIdx = 0;
}
// Calculate Slack and MinWidth for all columns. Also calculate sums for all columns.
var totalMinWidth = 0f;
var totalMaxWidth = 0f;
var totalSlack = 0f;
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
column.MinWidth = Math.Min(column.MaxWidth, MinForcedColumnWidth);
column.Slack = column.MaxWidth - column.MinWidth;
totalMinWidth += column.MinWidth;
totalMaxWidth += column.MaxWidth;
totalSlack += column.Slack;
}
if (totalMaxWidth <= availableSize.X)
{
// We want less horizontal space than we're given. Huh, that's convenient.
// Just set assigned width to be however much they asked for.
// We could probably skip the second measure pass in this scenario,
// but that's just an optimization, so I don't care right now.
//
// There's probably a very clever way to make this behavior work with the else block of logic,
// just by fiddling with the math.
// I'm dumb, it's 4:30 AM. Yeah, I *started* at 2 AM.
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
column.AssignedWidth = column.MaxWidth;
}
}
else
{
// We don't have enough horizontal space,
// at least without causing *some* sort of word wrapping (assuming text contents).
//
// Assign horizontal space proportional to the wanted maximum size of the columns.
var assignableWidth = Math.Max(0, availableSize.X - totalMinWidth);
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
var slackRatio = column.Slack / totalSlack;
column.AssignedWidth = column.MinWidth + slackRatio * assignableWidth;
}
}
// Go over controls for a second measuring pass, this time giving them their assigned measure width.
// This will give us a height to slot into per-row data.
// We still measure assuming infinite vertical space.
// This control can't properly handle being constrained on the Y axis.
columnIdx = 0;
var rowIdx = 0;
foreach (var child in Children)
{
ref var column = ref _columnDataCache[columnIdx];
ref var row = ref _rowDataCache[rowIdx];
child.Measure(new Vector2(column.AssignedWidth, float.PositiveInfinity));
row.MeasuredHeight = Math.Max(row.MeasuredHeight, child.DesiredSize.Y);
columnIdx += 1;
if (columnIdx == _columns)
{
columnIdx = 0;
rowIdx += 1;
}
}
// Sum up height of all rows to get final measured table height.
var totalHeight = 0f;
for (var r = 0; r < _rowDataCache.Length; r++)
{
ref var row = ref _rowDataCache[r];
totalHeight += row.MeasuredHeight;
}
return new Vector2(Math.Min(availableSize.X, totalMaxWidth), totalHeight);
}
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
// TODO: Expand to fit given vertical space.
// Calculate MinWidth and Slack sums again from column data.
// We could've cached these from measure but whatever.
var totalMinWidth = 0f;
var totalSlack = 0f;
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
totalMinWidth += column.MinWidth;
totalSlack += column.Slack;
}
// Calculate new width based on final given size, also assign horizontal positions of all columns.
var assignableWidth = Math.Max(0, finalSize.X - totalMinWidth);
var xPos = 0f;
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
var slackRatio = column.Slack / totalSlack;
column.ArrangedWidth = column.MinWidth + slackRatio * assignableWidth;
column.ArrangedX = xPos;
xPos += column.ArrangedWidth;
}
// Do actual arrangement row-by-row.
var arrangeY = 0f;
for (var r = 0; r < _rowDataCache.Length; r++)
{
ref var row = ref _rowDataCache[r];
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
var index = c + r * _columns;
if (index >= ChildCount) // Quit early if we don't actually fill out the row.
break;
var child = GetChild(c + r * _columns);
child.Arrange(UIBox2.FromDimensions(column.ArrangedX, arrangeY, column.ArrangedWidth, row.MeasuredHeight));
}
arrangeY += row.MeasuredHeight;
}
return finalSize with { Y = arrangeY };
}
/// <summary>
/// Ensure cached array space is allocated to correct size and is reset to a clean slate.
/// </summary>
private void ResetCachedArrays()
{
// 1-argument Array.Clear() is not currently available in sandbox (added in .NET 6).
if (_columnDataCache.Length != _columns)
_columnDataCache = new ColumnData[_columns];
Array.Clear(_columnDataCache, 0, _columnDataCache.Length);
var rowCount = ChildCount / _columns;
if (ChildCount % _columns != 0)
rowCount += 1;
if (rowCount != _rowDataCache.Length)
_rowDataCache = new RowData[rowCount];
Array.Clear(_rowDataCache, 0, _rowDataCache.Length);
}
/// <summary>
/// Per-column data used during layout.
/// </summary>
private struct ColumnData
{
// Measure data.
/// <summary>
/// The maximum width any control in this column wants, if given infinite space.
/// Maximum of all controls on the column.
/// </summary>
public float MaxWidth;
/// <summary>
/// The minimum width this column may be given.
/// This is either <see cref="MaxWidth"/> or <see cref="TableContainer.MinForcedColumnWidth"/>.
/// </summary>
public float MinWidth;
/// <summary>
/// Difference between max and min width; how much this column can expand from its minimum.
/// </summary>
public float Slack;
/// <summary>
/// How much horizontal space this column was assigned at measure time.
/// </summary>
public float AssignedWidth;
// Arrange data.
/// <summary>
/// How much horizontal space this column was assigned at arrange time.
/// </summary>
public float ArrangedWidth;
/// <summary>
/// The horizontal position this column was assigned at arrange time.
/// </summary>
public float ArrangedX;
}
private struct RowData
{
// Measure data.
/// <summary>
/// How much height the tallest control on this row was measured at,
/// measuring for infinite vertical space but assigned column width.
/// </summary>
public float MeasuredHeight;
}
}

View File

@@ -18,9 +18,6 @@ namespace Robust.Client.UserInterface.Controls
public Color? BackgroundColor { get; set; }
public virtual LayoutContainer PopupRoot => throw new NotSupportedException();
public virtual PopupContainer ModalRoot => throw new NotSupportedException();
private Color _styleBgColor;
internal Color ActualBgColor => BackgroundColor ?? _styleBgColor;

View File

@@ -1,14 +1,12 @@
using System;
using Robust.Client.Graphics;
using Robust.Client.Graphics;
using Robust.Shared;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Robust.Client.UserInterface.Controls
{
public sealed class WindowRoot : UIRoot
{
private PopupContainer? _modalRoot;
private LayoutContainer? _popupRoot;
internal WindowRoot(IClydeWindow window)
{
Window = window;
@@ -34,39 +32,5 @@ namespace Robust.Client.UserInterface.Controls
/// </remarks>
/// <seealso cref="CVars.ResAutoScaleEnabled"/>
public bool DisableAutoScaling { get; set; } = true;
public override PopupContainer ModalRoot => _modalRoot ?? throw new InvalidOperationException(
$"Tried to access root controls without calling {nameof(CreateRootControls)}!");
public override LayoutContainer PopupRoot => _popupRoot ?? throw new InvalidOperationException(
$"Tried to access root controls without calling {nameof(CreateRootControls)}!");
/// <summary>
/// Creates root controls (e.g. <see cref="UIRoot.ModalRoot"/>) that are necessary for the UI system to
/// fully function.
/// </summary>
/// <remarks>
/// This should be called *after* inserting the main content into this instance,
/// so that the created root controls (e.g. popups) correctly stay on top.
/// </remarks>
public void CreateRootControls()
{
if (_modalRoot != null)
throw new InvalidOperationException("We've already created root controls!");
_modalRoot = new PopupContainer
{
Name = nameof(ModalRoot),
MouseFilter = MouseFilterMode.Ignore,
};
AddChild(_modalRoot);
_popupRoot = new LayoutContainer
{
Name = nameof(PopupRoot),
MouseFilter = MouseFilterMode.Ignore
};
AddChild(_popupRoot);
}
}
}

View File

@@ -1,330 +0,0 @@
using System;
using System.Numerics;
using Robust.Shared.Collections;
using Robust.Shared.Maths;
namespace Robust.Client.UserInterface.Controls;
/// <summary>
/// Lays of children sequentially, "wrapping" them onto another row/column if necessary.
/// </summary>
public sealed class WrapContainer : Container
{
/// <summary>
/// Specifies the amount of space between two children, on the main axis.
/// </summary>
public const string StylePropertySeparation = "separation";
/// <summary>
/// Specifies the amount of space between two children, on the cross axis.
/// </summary>
public const string StylePropertyCrossSeparation = "cross-separation";
// Parameters.
private Axis _layoutAxis;
private ItemJustification _justification;
private bool _equalSize;
private bool _reverse;
private int? _separationOverride;
private int? _crossSeparationOverride;
// Cached layout data.
private ValueList<(int endIndex, float cross)> _rowIndices;
private float _lastMeasureCross;
/// <summary>
/// Specifies the amount of space between two children, on the main axis.
/// </summary>
/// <remarks>
/// This property overrides <see cref="StylePropertySeparation"/>, if set.
/// </remarks>
public int? SeparationOverride
{
get => _separationOverride;
set
{
_separationOverride = value;
InvalidateMeasure();
}
}
/// <summary>
/// Specifies the amount of space between two children, on the cross axis.
/// </summary>
/// <remarks>
/// This property overrides <see cref="StylePropertyCrossSeparation"/>, if set.
/// </remarks>
public int? CrossSeparationOverride
{
get => _crossSeparationOverride;
set
{
_crossSeparationOverride = value;
InvalidateMeasure();
}
}
/// <summary>
/// The <see cref="Axis"/> along which to lay out children.
/// </summary>
public Axis LayoutAxis
{
get => _layoutAxis;
set
{
_layoutAxis = value;
InvalidateMeasure();
}
}
/// <summary>
/// If true, all children will be laid out with the size of the largest child.
/// </summary>
public bool EqualSize
{
get => _equalSize;
set
{
_equalSize = value;
InvalidateMeasure();
}
}
/// <summary>
/// Determines where items will be laid out on an individual row/column.
/// </summary>
public ItemJustification Justification
{
get => _justification;
set
{
_justification = value;
InvalidateArrange();
}
}
/// <summary>
/// If true, reverses the order on which wrapping rows/columns are laid out.
/// </summary>
/// <remarks>
/// <para>
/// On horizontal axis, the first children are on the top row, and new rows are added <i>downwards</i>.
/// With <see cref="Reverse"/> set to true,
/// the first children are instead on the bottom row, with new rows growing upwards.
/// </para>
/// </remarks>
public bool Reverse
{
get => _reverse;
set
{
_reverse = value;
InvalidateArrange();
}
}
private int ActualSeparation
{
get
{
if (TryGetStyleProperty(StylePropertySeparation, out int separation))
{
return separation;
}
return SeparationOverride ?? 0;
}
}
private int ActualCrossSeparation
{
get
{
if (TryGetStyleProperty(StylePropertyCrossSeparation, out int separation))
{
return separation;
}
return CrossSeparationOverride ?? 0;
}
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
var axis = LayoutAxis;
return axis switch
{
Axis.Horizontal => MeasureImplementation<HorizontalAxis>(availableSize),
Axis.HorizontalReverse => MeasureImplementation<HorizontalReverseAxis>(availableSize),
Axis.Vertical => MeasureImplementation<VerticalAxis>(availableSize),
Axis.VerticalReverse => MeasureImplementation<VerticalReverseAxis>(availableSize),
_ => throw new ArgumentOutOfRangeException()
};
}
private Vector2 MeasureImplementation<TAxis>(Vector2 availableSize) where TAxis : IAxisImplementation
{
_rowIndices.Clear();
var realAvailableSize = availableSize;
availableSize = TAxis.SizeToAxis(availableSize);
// TODO: Round to pixels properly.
var separation = ActualSeparation;
var crossSeparation = ActualCrossSeparation;
var curMainSize = 0f;
var curCrossSize = 0f;
var totalCrossSize = 0f;
var maxMainSize = 0f;
var firstOnRow = true;
var equalDesiredSize = _equalSize ? TAxis.SizeToAxis(GetMaxMeasure(realAvailableSize)) : default;
var countLaidOut = 0;
foreach (var control in Children)
{
Vector2 controlDesiredSize;
if (_equalSize)
{
controlDesiredSize = equalDesiredSize;
}
else
{
control.Measure(realAvailableSize);
controlDesiredSize = TAxis.SizeToAxis(control.DesiredSize);
}
var controlMainSize = controlDesiredSize.X;
var spaceTaken = controlMainSize + (firstOnRow ? 0 : separation);
if (curMainSize + spaceTaken > availableSize.X)
{
// We've wrapped.
RowEnd(lastRow: false);
curMainSize = controlMainSize;
}
else
{
curMainSize += spaceTaken;
}
curCrossSize = Math.Max(curCrossSize, controlDesiredSize.Y);
firstOnRow = false;
countLaidOut += 1;
}
RowEnd(lastRow: true);
_lastMeasureCross = totalCrossSize;
return TAxis.SizeFromAxis(new Vector2(maxMainSize, totalCrossSize));
void RowEnd(bool lastRow)
{
maxMainSize = Math.Max(maxMainSize, curMainSize);
totalCrossSize += curCrossSize;
if (!lastRow)
totalCrossSize += crossSeparation;
_rowIndices.Add((countLaidOut, curCrossSize));
curCrossSize = 0;
}
}
private Vector2 GetMaxDesired()
{
var vec = Vector2.Zero;
foreach (var child in Children)
{
vec = Vector2.Max(vec, child.DesiredSize);
}
return vec;
}
private Vector2 GetMaxMeasure(Vector2 availableSize)
{
var vec = Vector2.Zero;
foreach (var child in Children)
{
child.Measure(availableSize);
vec = Vector2.Max(vec, child.DesiredSize);
}
return vec;
}
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
var axis = LayoutAxis;
return axis switch
{
Axis.Horizontal => ArrangeImplementation<HorizontalAxis>(finalSize),
Axis.HorizontalReverse => ArrangeImplementation<HorizontalReverseAxis>(finalSize),
Axis.Vertical => ArrangeImplementation<VerticalAxis>(finalSize),
Axis.VerticalReverse => ArrangeImplementation<VerticalReverseAxis>(finalSize),
_ => throw new ArgumentOutOfRangeException()
};
}
private Vector2 ArrangeImplementation<TAxis>(Vector2 finalSize) where TAxis : IAxisImplementation
{
var realFinalSize = finalSize;
finalSize = TAxis.SizeToAxis(finalSize);
var separation = ActualSeparation;
var crossSeparation = ActualCrossSeparation;
var baseOffset = Reverse ? new Vector2(0, _lastMeasureCross) : Vector2.Zero;
var fixedSize = _equalSize ? (Vector2?)GetMaxDesired() : null;
var start = 0;
for (var i = 0; i < _rowIndices.Count; i++)
{
var (endIndex, cross) = _rowIndices[i];
if (Reverse)
{
baseOffset.Y -= cross;
}
var box = TAxis.BoxFromAxis(UIBox2.FromDimensions(baseOffset, finalSize with { Y = cross }), realFinalSize);
BoxContainer.LayOutItems<TAxis>(
box.TopLeft,
box.Size,
(BoxContainer.AlignMode)_justification,
Children,
start,
endIndex,
separation,
fixedSize);
start = endIndex;
if (Reverse)
{
baseOffset.Y -= crossSeparation;
}
else
{
baseOffset.Y = baseOffset.Y + cross + crossSeparation;
}
}
return realFinalSize;
}
/// <summary>
/// Justification values for <see cref="WrapContainer.Justification"/>.
/// </summary>
public enum ItemJustification : byte
{
// These MUST match the values in BoxContainer.
Begin = BoxContainer.AlignMode.Begin,
Center = BoxContainer.AlignMode.Center,
End = BoxContainer.AlignMode.End
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
using Robust.Client.UserInterface.Controls;
using Robust.Shared;
@@ -225,13 +224,10 @@ public sealed partial class DebugConsole
if (_compPopup.Contents.ChildCount != 0)
{
var box = UIBox2.FromDimensions(
offset - _compPopup.Contents.Margin.Left,
CommandBar.GlobalPosition.Y + CommandBar.Height + 2,
5,
5);
var altPosUp = new Vector2(offset - _compPopup.Contents.Margin.Left, CommandBar.GlobalPosition.Y);
_compPopup.Open(box, altPosUp: altPosUp);
_compPopup.Open(
UIBox2.FromDimensions(
offset - _compPopup.Contents.Margin.Left, CommandBar.GlobalPosition.Y + CommandBar.Height + 2,
5, 5));
}
}

View File

@@ -83,7 +83,7 @@ namespace Robust.Client.UserInterface.CustomControls
_consoleHost.ClearText += OnClearText;
_cfg.OnValueChanged(CVars.ConMaxEntries, MaxEntriesChanged, true);
Root!.ModalRoot.AddChild(_compPopup);
UserInterfaceManager.ModalRoot.AddChild(_compPopup);
}
protected override void ExitedTree()
@@ -95,7 +95,7 @@ namespace Robust.Client.UserInterface.CustomControls
_consoleHost.ClearText -= OnClearText;
_cfg.UnsubValueChanged(CVars.ConMaxEntries, MaxEntriesChanged);
_compPopup.Orphan();
UserInterfaceManager.ModalRoot.RemoveChild(_compPopup);
}
private void MaxEntriesChanged(int value)

View File

@@ -1,39 +1,42 @@
using System;
using Robust.Client.GameStates;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.Profiling;
using Robust.Client.State;
using Robust.Client.Timing;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Network;
namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
{
internal sealed class DebugMonitors : BoxContainer, IDebugMonitors
{
[Dependency] private readonly IClientGameTiming _timing = default!;
[Dependency] private readonly IClientGameStateManager _state = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IClientNetManager _net = default!;
private readonly Control[] _monitors = new Control[Enum.GetNames<DebugMonitor>().Length];
public void Init()
//TODO: Think about a factory for this
public DebugMonitors(IClientGameTiming gameTiming, IPlayerManager playerManager, IEyeManager eyeManager,
IInputManager inputManager, IStateManager stateManager, IClyde displayManager, IClientNetManager netManager,
IMapManager mapManager)
{
Visible = false;
SeparationOverride = 2;
Orientation = LayoutOrientation.Vertical;
Add(DebugMonitor.Fps, new FpsCounter(_timing));
Add(DebugMonitor.Fps, new FpsCounter(gameTiming));
Add(DebugMonitor.Coords, new DebugCoordsPanel());
Add(DebugMonitor.Net, new DebugNetPanel(_net, _timing));
Add(DebugMonitor.Bandwidth, new DebugNetBandwidthPanel(_net, _timing));
Add(DebugMonitor.Time, new DebugTimePanel(_timing, _state));
Add(DebugMonitor.Frames, new FrameGraph(_timing, _cfg));
Add(DebugMonitor.Net, new DebugNetPanel(netManager, gameTiming));
Add(DebugMonitor.Bandwidth, new DebugNetBandwidthPanel(netManager, gameTiming));
Add(DebugMonitor.Time, new DebugTimePanel(gameTiming, IoCManager.Resolve<IClientGameStateManager>()));
Add(DebugMonitor.Frames, new FrameGraph(gameTiming, IoCManager.Resolve<IConfigurationManager>()));
Add(DebugMonitor.Memory, new DebugMemoryPanel());
Add(DebugMonitor.Clyde, new DebugClydePanel { HorizontalAlignment = HAlignment.Left });
Add(DebugMonitor.System, new DebugSystemPanel { HorizontalAlignment = HAlignment.Left });
Add(DebugMonitor.Version, new DebugVersionPanel(_cfg) {HorizontalAlignment = HAlignment.Left});
Add(DebugMonitor.Input, new DebugInputPanel { HorizontalAlignment = HAlignment.Left });
Add(DebugMonitor.Prof, new LiveProfileViewControl());

View File

@@ -1,39 +0,0 @@
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Configuration;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
{
internal sealed class DebugVersionPanel : PanelContainer
{
public DebugVersionPanel(IConfigurationManager cfg)
{
var contents = new Label
{
FontColorShadowOverride = Color.Black,
};
AddChild(contents);
PanelOverride = new StyleBoxFlat
{
BackgroundColor = new Color(35, 134, 37, 138),
ContentMarginLeftOverride = 5,
ContentMarginRightOverride = 5,
ContentMarginTopOverride = 5,
ContentMarginBottomOverride = 5,
};
MouseFilter = contents.MouseFilter = MouseFilterMode.Ignore;
// Set visible explicitly
Visible = true;
HorizontalAlignment = HAlignment.Left;
VerticalAlignment = VAlignment.Top;
var buildInfo = GameBuildInformation.GetBuildInfoFromConfig(cfg);
contents.Text = $"Fork ID: {buildInfo.ForkId}\nFork Version: {buildInfo.Version}";
}
}
}

View File

@@ -71,7 +71,7 @@ namespace Robust.Client.UserInterface.CustomControls
}
else
{
_curAnchorOffset = UIAnimations.LerpAnimate(_curAnchorOffset, targetOffset, args.DeltaSeconds, 20);
_curAnchorOffset = MathHelper.Lerp(_curAnchorOffset, targetOffset, args.DeltaSeconds * 20);
}
UpdateAnchorOffset();

View File

@@ -6,6 +6,5 @@
<DevWindowTabUI Name="UI" />
<DevWindowTabPerf Name="Perf" />
<DevWindowTabTextures Name="Textures" />
<DevWindowTabRenderTargets Name="RenderTargets" />
</TabContainer>
</Control>

View File

@@ -28,7 +28,6 @@ namespace Robust.Client.UserInterface
TabContainer.SetTabTitle(UI, "User Interface");
TabContainer.SetTabTitle(Perf, "Profiling");
TabContainer.SetTabTitle(Textures, Loc.GetString("dev-window-tab-textures-title"));
TabContainer.SetTabTitle(RenderTargets, Loc.GetString("dev-window-tab-render-targets-title"));
Stylesheet =
new DefaultStylesheet(IoCManager.Resolve<IResourceCache>(), IoCManager.Resolve<IUserInterfaceManager>()).Stylesheet;
@@ -42,14 +41,26 @@ namespace Robust.Client.UserInterface
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var window = new OSWindow
var clyde = IoCManager.Resolve<IClyde>();
var monitor = clyde.EnumerateMonitors().First();
if (args.Length > 0)
{
Title = "Robust Debug Window",
};
var control = new DevWindow();
window.AddChild(control);
var id = int.Parse(args[0]);
monitor = clyde.EnumerateMonitors().Single(m => m.Id == id);
}
window.Show();
var window = clyde.CreateWindow(new WindowCreateParameters
{
//Maximized = true,
Title = "Robust Debug Window",
//Monitor = monitor,
});
var root = IoCManager.Resolve<IUserInterfaceManager>().CreateWindowRoot(window);
window.DisposeOnClose = true;
var control = new DevWindow();
root.AddChild(control);
}
}
}

View File

@@ -1,26 +0,0 @@
<Control xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Robust.Client.UserInterface.DevWindowTabRenderTargets">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Button Text="{Loc 'dev-window-tab-render-targets-reload'}" Name="ReloadButton" />
</BoxContainer>
<LineEdit Name="FilterEdit" PlaceHolder="{Loc 'dev-window-tab-render-targets-filter'}" />
<ScrollContainer VerticalExpand="True" VScrollEnabled="True" HScrollEnabled="False">
<BoxContainer Orientation="Vertical">
<TableContainer Name="Table" Margin="2" Columns="6">
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-id'}" />
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-name'}" />
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-size'}" />
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-type'}" />
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-vram'}" />
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-thumbnail'}" />
</TableContainer>
</BoxContainer>
</ScrollContainer>
<BoxContainer Orientation="Horizontal">
<Label Name="TotalLabel" Margin="4" />
</BoxContainer>
</BoxContainer>
</Control>

View File

@@ -1,242 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Graphics;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using RTCF = Robust.Client.Graphics.RenderTargetColorFormat;
namespace Robust.Client.UserInterface;
[GenerateTypedNameReferences]
internal sealed partial class DevWindowTabRenderTargets : Control
{
[Dependency] private readonly IClydeInternal _clyde = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
private readonly Control[] _gridHeader;
private readonly StyleBoxFlat _styleAltRow = new() { BackgroundColor = Color.FromHex("#222") };
#if TOOLS
private readonly HashSet<IRenderTarget> _copyTextures = new();
#endif // TOOLS
public DevWindowTabRenderTargets()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_gridHeader = Table.Children.ToArray();
ReloadButton.OnPressed += _ => Reload();
FilterEdit.OnTextChanged += _ => Reload();
}
protected override void VisibilityChanged(bool newVisible)
{
base.VisibilityChanged(newVisible);
if (newVisible)
{
Reload();
}
else
{
// Clear to release memory when tab not visible.
Clear();
}
}
protected override void ExitedTree()
{
base.ExitedTree();
Clear();
}
private void Clear()
{
Table.RemoveAllChildren();
#if TOOLS
foreach (var copiedTexture in _copyTextures)
{
copiedTexture.Dispose();
}
_copyTextures.Clear();
#endif
}
private void Reload()
{
Table.RemoveAllChildren();
foreach (var header in _gridHeader)
{
Table.AddChild(header);
}
var totalVram = 0L;
var even = true;
var rts = _clyde.GetLoadedRenderTextures().OrderBy(x => x.Item1.Handle.Value).ToArray();
foreach (var (instance, loaded) in rts)
{
#if TOOLS
if (_copyTextures.Contains(instance))
continue;
#endif // TOOLS
if (!string.IsNullOrWhiteSpace(FilterEdit.Text))
{
if (loaded.Name is not { } name)
continue;
if (!name.Contains(FilterEdit.Text, StringComparison.CurrentCultureIgnoreCase))
continue;
}
AddColumnText(instance.Handle.Value.ToString());
if (loaded.Name != null)
AddColumnText(loaded.Name);
else if (loaded.WindowId != WindowId.Invalid)
AddColumnText(loaded.WindowId.ToString());
else
AddColumnText(_loc.GetString("dev-window-tab-render-targets-value-null"));
AddColumnText(loaded.Size.ToString());
var type = loaded.ColorFormat.ToString();
if (loaded.DepthStencilHandle != default)
type += "+DS";
AddColumnText(type);
AddColumnText(ByteHelpers.FormatBytes(loaded.MemoryPressure));
// Disable texture thumbnails outside TOOLS.
// Avoid people cheating by using devwindow to see through walls. Barely.
#if TOOLS
if (instance is IRenderTexture renderTexture)
{
var clone = CloneTexture(renderTexture.Texture, loaded.ColorFormat);
_copyTextures.Add(clone);
AddColumn(new TextureRect
{
Texture = clone.Texture,
Stretch = TextureRect.StretchMode.KeepAspect
});
}
else
#endif // TOOLS
{
AddColumnText(_loc.GetString("dev-window-tab-render-targets-value-not-available"));
}
totalVram += loaded.MemoryPressure;
even = !even;
}
TotalLabel.Text = Loc.GetString(
"dev-window-tab-render-targets-summary",
("vram", ByteHelpers.FormatBytes(totalVram)));
return;
void AddColumnText(string text)
{
var richTextLabel = new RichTextLabel { Margin = new Thickness(4) };
richTextLabel.SetMessage(text, defaultColor: Color.White);
AddColumn(richTextLabel);
}
void AddColumn(Control control)
{
control.VerticalAlignment = VAlignment.Center;
if (even)
{
control = new PanelContainer
{
PanelOverride = _styleAltRow,
Children = { control },
};
}
else
{
// Wrapping control so we can use SetHeight.
control = new Control
{
Children = { control },
};
}
control.SetHeight = 50;
Table.AddChild(control);
}
}
#if TOOLS
private IRenderTexture CloneTexture(Texture texture, RTCF colorFormat)
{
var thumbnailSize = GetThumbnailSize(texture.Size);
var rt = _clyde.CreateRenderTarget(
thumbnailSize,
new RenderTargetFormatParameters
{
ColorFormat = colorFormat,
HasDepthStencil = false,
},
new TextureSampleParameters
{
Filter = true,
},
name: $"{nameof(DevWindowTabRenderTargets)}-clone");
_clyde.RenderNow(rt,
handle =>
{
handle.DrawingHandleScreen.DrawTextureRect(texture, UIBox2.FromDimensions(Vector2.Zero, thumbnailSize));
});
return rt;
}
private static Vector2i GetThumbnailSize(Vector2i textureSize)
{
const int maxHeight = 50;
const int maxWidth = 100;
var (w, h) = (Vector2)textureSize;
if (h > maxHeight)
{
w /= h / maxHeight;
h = maxHeight;
}
if (w > maxWidth)
{
h /= w / maxWidth;
w = maxWidth;
}
return new Vector2i((int)w, (int)h);
}
#endif // TOOLS
}

View File

@@ -36,6 +36,5 @@ public enum DebugMonitor
Input,
Bandwidth,
Prof,
System,
Version
System
}

View File

@@ -21,7 +21,7 @@ namespace Robust.Client.UserInterface
/// <param name="tooltip">control to position (current size will be used to determine bounds)</param>
public static void PositionTooltip(Control tooltip)
{
PositionTooltip(tooltip.Root!.Size,
PositionTooltip(tooltip.UserInterfaceManager.RootControl.Size,
tooltip.UserInterfaceManager.MousePositionScaled.Position,
tooltip);
}

View File

@@ -1,13 +0,0 @@
using System;
using Robust.Shared.Maths;
namespace Robust.Client.UserInterface;
internal static class UIAnimations
{
// From https://blog.pkh.me/p/41-fixing-the-iterative-damping-interpolation-in-video-games.html
public static float LerpAnimate(float a, float b, float dt, float rate)
{
return MathHelper.Lerp(a, b, 1f - MathF.Exp(-dt * rate));
}
}

View File

@@ -333,7 +333,7 @@ internal partial class UserInterfaceManager
if (_suppliedTooltip != null)
{
_suppliedTooltip.Orphan();
PopupRoot.RemoveChild(_suppliedTooltip);
_suppliedTooltip = null;
}
@@ -538,7 +538,7 @@ internal partial class UserInterfaceManager
if (_showingTooltip) return;
_showingTooltip = true;
var hovered = CurrentlyHovered;
if (hovered == null || hovered.Root == null)
if (hovered == null)
{
return;
}
@@ -563,7 +563,7 @@ internal partial class UserInterfaceManager
if (_suppliedTooltip == null)
return;
hovered.Root.PopupRoot.AddChild(_suppliedTooltip);
PopupRoot.AddChild(_suppliedTooltip);
Tooltips.PositionTooltip(_suppliedTooltip);
hovered.PerformShowTooltip();
}

View File

@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Client.Timing;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.CustomControls.DebugMonitorControls;
@@ -18,7 +20,9 @@ using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
@@ -33,9 +37,13 @@ namespace Robust.Client.UserInterface
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IFontManager _fontManager = default!;
[Dependency] private readonly IClydeInternal _clyde = default!;
[Dependency] private readonly IClientGameTiming _gameTiming = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManager = default!;
[Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!;
@@ -78,10 +86,10 @@ namespace Robust.Client.UserInterface
[ViewVariables] public ViewportContainer MainViewport { get; private set; } = default!;
[ViewVariables] public LayoutContainer StateRoot { get; private set; } = default!;
[ViewVariables] public PopupContainer ModalRoot => RootControl.ModalRoot;
[ViewVariables] public PopupContainer ModalRoot { get; private set; } = default!;
[ViewVariables] public WindowRoot RootControl { get; private set; } = default!;
[ViewVariables] public LayoutContainer WindowRoot { get; private set; } = default!;
[ViewVariables] public LayoutContainer PopupRoot => RootControl.PopupRoot;
[ViewVariables] public LayoutContainer PopupRoot { get; private set; } = default!;
[ViewVariables] public DropDownDebugConsole DebugConsole { get; private set; } = default!;
[ViewVariables] public IDebugMonitors DebugMonitors => _debugMonitors;
private DebugMonitors _debugMonitors = default!;
@@ -109,11 +117,10 @@ namespace Robust.Client.UserInterface
DebugConsole = new DropDownDebugConsole();
RootControl.AddChild(DebugConsole);
DebugConsole.SetPositionInParent(RootControl.ModalRoot.GetPositionInParent());
DebugConsole.SetPositionInParent(ModalRoot.GetPositionInParent());
_debugMonitors = new DebugMonitors();
_rootDependencies.InjectDependencies(_debugMonitors);
_debugMonitors.Init();
_debugMonitors = new DebugMonitors(_gameTiming, _playerManager, _eyeManager, _inputManager, _stateManager,
_clyde, _netManager, _mapManager);
DebugConsole.BelowConsole.AddChild(_debugMonitors);
_inputManager.SetInputCommand(EngineKeyFunctions.ShowDebugConsole,
@@ -170,7 +177,19 @@ namespace Robust.Client.UserInterface
};
RootControl.AddChild(WindowRoot);
RootControl.CreateRootControls();
ModalRoot = new PopupContainer
{
Name = "ModalRoot",
MouseFilter = Control.MouseFilterMode.Ignore,
};
RootControl.AddChild(ModalRoot);
PopupRoot = new LayoutContainer
{
Name = "PopupRoot",
MouseFilter = Control.MouseFilterMode.Ignore
};
RootControl.AddChild(PopupRoot);
}
public void InitializeTesting()

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics.Contracts;
using System.Text;
using Robust.Client.Graphics;
@@ -36,18 +36,10 @@ internal struct WordWrap
LastRune = new Rune('A');
}
/// <summary>
/// Add <paramref name="rune" /> as the next glyph in the sequence and update internal line break state.
/// </summary>
/// <param name="breakLine">If non-null, indicates that a line break needs to be added at this rune
/// index within the string. This index will refer to the offset of a previously-entered rune</param>
/// <param name="breakNewLine">If non-null, indicates the rune index of an entered newline</param>
/// <param name="skip">If true, indicates that the rune should occupy zero space. Currently only used
/// for newlines.</param>
public void NextRune(Rune rune, out int? breakLine, out int? breakNewLine, out bool skip)
{
BreakIndexCounter = NextBreakIndexCounter;
NextBreakIndexCounter += 1;
NextBreakIndexCounter += rune.Utf16SequenceLength;
breakLine = null;
breakNewLine = null;

View File

@@ -3,9 +3,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Robust.Shared;
using Robust.Shared.Asynchronous;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -20,25 +18,24 @@ namespace Robust.Client.UserInterface.XAML.Proxy;
/// </remarks>
internal sealed class XamlHotReloadManager : IXamlHotReloadManager
{
[Dependency] private readonly IConfigurationManager _cfg = null!;
[Dependency] private readonly ILogManager _logManager = null!;
[Dependency] private readonly IResourceManagerInternal _resources = null!;
private const string MarkerFileName = "SpaceStation14.sln";
[Dependency] ILogManager _logManager = null!;
[Dependency] private readonly IResourceManager _resources = null!;
[Dependency] private readonly ITaskManager _taskManager = null!;
[Dependency] private readonly IXamlProxyManager _xamlProxyManager = null!;
private ISawmill _sawmill = null!;
private FileSystemWatcher? _watcher;
private string _markerFileName = null!;
public void Initialize()
{
_markerFileName = _cfg.GetCVar(CVars.XamlHotReloadMarkerName);
_sawmill = _logManager.GetSawmill("xamlhotreload");
var codeLocation = InferCodeLocation();
if (codeLocation == null)
{
_sawmill.Warning($"could not find code -- where is {_markerFileName}?");
_sawmill.Warning($"could not find code -- where is {MarkerFileName}?");
return;
}
@@ -120,9 +117,9 @@ internal sealed class XamlHotReloadManager : IXamlHotReloadManager
private string? InferCodeLocation()
{
// ascend upwards from each content root until the solution file is found
foreach (var baseSystemPath in _resources.GetContentRoots())
foreach (var contentRoot in _resources.GetContentRoots())
{
var systemPath = baseSystemPath;
var systemPath = contentRoot.ToRelativeSystemPath();
while (true)
{
var files = Array.Empty<string>();
@@ -132,7 +129,7 @@ internal sealed class XamlHotReloadManager : IXamlHotReloadManager
}
catch (IOException) { } // this is allowed to fail, and if so we just keep going up
if (files.Any(f => Path.GetFileName(f).Equals(_markerFileName, StringComparison.InvariantCultureIgnoreCase)))
if (files.Any(f => Path.GetFileName(f).Equals(MarkerFileName, StringComparison.InvariantCultureIgnoreCase)))
{
return systemPath;
}

View File

@@ -1,9 +1,6 @@
#if !WINDOWS
using System;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using OpenTK.Audio.OpenAL;
using SDL3;
namespace Robust.Client.Utility
{
@@ -12,31 +9,32 @@ namespace Robust.Client.Utility
[ModuleInitializer]
internal static void Initialize()
{
if (OperatingSystem.IsWindows())
return;
NativeLibrary.SetDllImportResolver(typeof(ClientDllMap).Assembly, (name, assembly, path) =>
{
if (name == "swnfd.dll")
{
#if LINUX || FREEBSD
return NativeLibrary.Load("libswnfd.so", assembly, path);
#elif MACOS
return NativeLibrary.Load("libswnfd.dylib", assembly, path);
#endif
if (OperatingSystem.IsLinux())
return NativeLibrary.Load("libswnfd.so", assembly, path);
if (OperatingSystem.IsMacOS())
return NativeLibrary.Load("libswnfd.dylib", assembly, path);
return IntPtr.Zero;
}
if (name == "libEGL.dll")
{
#if LINUX || FREEBSD
return NativeLibrary.Load("libEGL.so", assembly, path);
#endif
if (OperatingSystem.IsLinux())
return NativeLibrary.Load("libEGL.so", assembly, path);
return IntPtr.Zero;
}
return IntPtr.Zero;
});
#if MACOS
OpenALLibraryNameContainer.OverridePath = "libopenal.1.dylib";
#endif
}
}
}
#endif

View File

@@ -19,7 +19,7 @@ internal sealed class ReloadManager : IReloadManager
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly IResourceManagerInternal _res = default!;
[Dependency] private readonly IResourceManager _res = default!;
#pragma warning disable CS0414
[Dependency] private readonly ITaskManager _tasks = default!;
#pragma warning restore CS0414
@@ -72,7 +72,7 @@ internal sealed class ReloadManager : IReloadManager
public void Register(ResPath directory, string filter)
{
Register(directory.ToRelativeSystemPath(), filter);
Register(directory.ToString(), filter);
}
public void Register(string directory, string filter)
@@ -83,7 +83,7 @@ internal sealed class ReloadManager : IReloadManager
#if TOOLS
foreach (var root in _res.GetContentRoots())
{
var path = Path.Join(root, directory);
var path = root + directory;
if (!Directory.Exists(path))
{
@@ -128,17 +128,17 @@ internal sealed class ReloadManager : IReloadManager
_tasks.RunOnMainThread(() =>
{
var fullPath = args.FullPath.Replace(Path.DirectorySeparatorChar, '/');
var file = new ResPath(fullPath);
foreach (var rootIter in _res.GetContentRoots())
{
var relPath = Path.GetRelativePath(rootIter, args.FullPath);
if (relPath == args.FullPath)
if (!file.TryRelativeTo(rootIter, out var relative))
{
// Not relative.
continue;
}
var file = ResPath.FromRelativeSystemPath(relPath);
_reloadQueue.Add(file);
_reloadQueue.Add(relative.Value);
}
});
}

View File

@@ -24,9 +24,8 @@ namespace Robust.Packaging;
/// PresetPassesCore --2--> Output
/// InputResources --> PresetPassesResources
/// PresetPassesResources --1--> AudioMetadata
/// PresetPassesResources --2--> DropAudioFiles
/// PresetPassesResources --3--> NormalizeTextResources
/// PresetPassesResources --4--> PrefixResources
/// PresetPassesResources --2--> NormalizeTextResources
/// PresetPassesResources --3--> PrefixResources
/// AudioMetadata --> PrefixResources
/// NormalizeTextResources --> PrefixResources
/// PrefixResources --> Output
@@ -60,17 +59,6 @@ public sealed class RobustServerAssetGraph
public AssetPassPipe PresetPassesResources { get; }
public AssetPassAudioMetadata AudioMetadata { get; }
/// <summary>
/// Used to drop all files in the <c>Audio/</c> directory.
/// </summary>
/// <remarks>
/// <para>
/// Most audio files are actually removed by <see cref="AudioMetadata"/>.
/// This pass cleans up stuff like attribution files and soundfonts in <c>Audio/MidiCustom</c>.
/// </para>
/// </remarks>
public AssetPassFilterDrop DropAudioFiles { get; }
/// <summary>
/// Normalizes text files in resources.
/// </summary>
@@ -115,12 +103,10 @@ public sealed class RobustServerAssetGraph
PresetPassesResources = new AssetPassPipe { Name = "RobustServerAssetGraphPresetPassesResources" };
NormalizeTextResources = new AssetPassNormalizeText { Name = "RobustServerAssetGraphNormalizeTextResources" };
AudioMetadata = new AssetPassAudioMetadata { Name = "RobustServerAssetGraphAudioMetadata" };
DropAudioFiles = new AssetPassFilterDrop(p => p.Path.StartsWith("Audio/")) { Name = "RobustServerAssetGraphDropAudioFiles" };
PrefixResources = new AssetPassPrefix("Resources/") { Name = "RobustServerAssetGraphPrefixResources" };
PresetPassesResources.AddDependency(InputResources);
AudioMetadata.AddDependency(PresetPassesResources).AddBefore(DropAudioFiles);
DropAudioFiles.AddDependency(PresetPassesResources).AddBefore(NormalizeTextResources);
AudioMetadata.AddDependency(PresetPassesResources).AddBefore(NormalizeTextResources);
NormalizeTextResources.AddDependency(PresetPassesResources).AddBefore(PrefixResources);
PrefixResources.AddDependency(PresetPassesResources);
PrefixResources.AddDependency(AudioMetadata);
@@ -138,7 +124,6 @@ public sealed class RobustServerAssetGraph
NormalizeTextResources,
AudioMetadata,
PrefixResources,
DropAudioFiles
};
}
}

View File

@@ -8,9 +8,7 @@ public sealed class RobustServerPackaging
{
"Textures",
"Fonts",
"EngineFonts",
"Shaders",
"Midi"
};
public static async Task WriteServerResources(

View File

@@ -102,8 +102,8 @@ namespace Robust.Server.Console.Commands
var sys = _system.GetEntitySystem<SharedMapSystem>();
if (!sys.MapExists(mapId))
{
shell.WriteError($"MapID {intMapId} did not exist, creating without map init");
sys.CreateMap(mapId, false); // doesnt runmapinit to be conservative.
shell.WriteError("Target map does not exist.");
return;
}
Vector2 offset = default;

View File

@@ -1,4 +1,3 @@
using System.Diagnostics.Contracts;
using System.Linq;
using Robust.Server.GameStates;
using Robust.Shared;
@@ -19,15 +18,13 @@ namespace Robust.Server.GameObjects
private bool _deleteEmptyGrids;
[Pure]
internal override MapId GetNextMapId()
protected override MapId GetNextMapId()
{
var id = new MapId(LastMapId + 1);
var id = new MapId(++LastMapId);
while (MapExists(id) || UsedIds.Contains(id))
{
id = new MapId(id.Value + 1);
id = new MapId(++LastMapId);
}
return id;
}

View File

@@ -28,8 +28,7 @@
<PackageReference Include="TerraFX.Interop.Windows" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" PrivateAssets="compile" />
<PackageReference Include="SharpZstd.Interop" PrivateAssets="compile" ExcludeAssets="native" />
<PackageReference Include="Robust.Natives.Zstd" />
<PackageReference Include="SharpZstd.Interop" PrivateAssets="compile" />
<PackageReference Condition="'$(RobustToolsBuild)' == 'True'" Include="JetBrains.Profiler.Api" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" />

View File

@@ -92,7 +92,7 @@ namespace Robust.Server.ServerStatus
{
context.ResponseHeaders.Add("Content-Encoding", "zstd");
await context.RespondAsync(result.ManifestData, HttpStatusCode.OK, "text/plain; charset=utf-8");
await context.RespondAsync(result.ManifestData, HttpStatusCode.OK);
}
else
{
@@ -110,7 +110,7 @@ namespace Robust.Server.ServerStatus
}
else
{
await context.RespondAsync(result.ManifestData, HttpStatusCode.OK, "text/plain; charset=utf-8");
await context.RespondAsync(result.ManifestData, HttpStatusCode.OK);
}
}

View File

@@ -25,11 +25,11 @@ public static class Matrix3Helpers
return a.EqualsApprox(b, (float) tolerance);
}
[Obsolete("Use TransformBox")]
// This method was previously broken, and now just returns an bounding box pretending to be a Box2Rotated
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Box2Rotated TransformBounds(this Matrix3x2 refFromBox, Box2Rotated box)
{
return new Box2Rotated(TransformBox(refFromBox, box));
var matty = Matrix3x2.Multiply(refFromBox, box.Transform);
return new Box2Rotated(Vector2.Transform(box.BottomLeft, matty), Vector2.Transform(box.TopRight, matty));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -6,3 +6,4 @@
[assembly: InternalsVisibleTo("Robust.Client")]
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
[assembly: InternalsVisibleTo("Content.Benchmarks")]

View File

@@ -437,7 +437,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
protected TimeSpan GetAudioLength(string filename)
{
if (!filename.StartsWith('/'))
throw new ArgumentException($"Path must be rooted. Path: {filename}");
throw new ArgumentException("Path must be rooted");
return GetAudioLengthImpl(filename);
}

View File

@@ -4,7 +4,6 @@ using Lidgren.Network;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Maths;
@@ -132,13 +131,13 @@ namespace Robust.Shared
/// Whether to interpolate between server game states for render frames on the client.
/// </summary>
public static readonly CVarDef<bool> NetInterp =
CVarDef.Create("net.interp", true, CVar.ARCHIVE | CVar.CLIENT | CVar.REPLICATED);
CVarDef.Create("net.interp", true, CVar.ARCHIVE | CVar.CLIENTONLY);
/// <summary>
/// The target number of game states to keep buffered up to smooth out network inconsistency.
/// </summary>
public static readonly CVarDef<int> NetBufferSize =
CVarDef.Create("net.buffer_size", 2, CVar.ARCHIVE | CVar.CLIENT | CVar.REPLICATED);
CVarDef.Create("net.buffer_size", 2, CVar.ARCHIVE | CVar.CLIENTONLY);
/// <summary>
/// The maximum size of the game state buffer. If this is exceeded the client will request a full game state.
@@ -1002,15 +1001,6 @@ namespace Robust.Shared
public static readonly CVarDef<bool> DisplayVSync =
CVarDef.Create("display.vsync", true, CVar.ARCHIVE | CVar.CLIENTONLY);
/// <summary>
/// Maximum framerate the client should run at. Set to 0 to have no limit.
/// </summary>
/// <remarks>
/// This is ignored if <see cref="DisplayVSync"/> is enabled.
/// </remarks>
public static readonly CVarDef<int> DisplayMaxFPS =
CVarDef.Create("display.max_fps", 0, CVar.ARCHIVE | CVar.CLIENTONLY);
/// <summary>
/// Window mode for the main game window. 0 = windowed, 1 = fullscreen.
/// </summary>
@@ -1496,7 +1486,7 @@ namespace Robust.Shared
/// Non-default seek modes WILL result in worse performance.
/// </remarks>
public static readonly CVarDef<int> ResStreamSeekMode =
CVarDef.Create("res.stream_seek_mode", (int)StreamSeekMode.None);
CVarDef.Create("res.stream_seek_mode", (int)ContentPack.StreamSeekMode.None);
/// <summary>
/// Whether to watch prototype files for prototype reload on the client. Only applies to development builds.
@@ -1892,28 +1882,11 @@ namespace Robust.Shared
public static readonly CVarDef<int> ToolshedNearbyEntitiesLimit =
CVarDef.Create("toolshed.nearby_entities_limit", 5, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// The max amount of prototype ids that can be sent to the client when autocompleting prototype ids.
/// </summary>
public static readonly CVarDef<int> ToolshedPrototypesAutocompleteLimit =
CVarDef.Create("toolshed.prototype_autocomplete_limit", 256, CVar.SERVER | CVar.REPLICATED);
/*
* Localization
*/
public static readonly CVarDef<string> LocCultureName =
CVarDef.Create("loc.culture_name", "en-US", CVar.ARCHIVE);
/*
* UI
*/
/// <summary>
/// The file XamlHotReloadManager looks for when locating the root of the project.
/// By default, this is Space Station 14's sln, but it can be any file at the same root level.
/// </summary>
public static readonly CVarDef<string> XamlHotReloadMarkerName =
CVarDef.Create("ui.xaml_hot_reload_marker_name", "SpaceStation14.sln", CVar.CLIENTONLY);
}
}

Some files were not shown because too many files have changed in this diff Show More