mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
281dd0626f | ||
|
|
42eb441a8d | ||
|
|
4d7022e101 | ||
|
|
a9305107d2 |
2
.github/workflows/build-test.yml
vendored
2
.github/workflows/build-test.yml
vendored
@@ -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 }}
|
||||
|
||||
|
||||
2
.github/workflows/publish-client.yml
vendored
2
.github/workflows/publish-client.yml
vendored
@@ -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: |
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
159
RELEASE-NOTES.md
159
RELEASE-NOTES.md
@@ -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
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
generic-map = map
|
||||
generic-grid = grid
|
||||
generic-mapid = map Id
|
||||
@@ -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]
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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!");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
25
Robust.Client/Graphics/Clyde/Windowing/SDL3-CS/LICENSE
Normal file
25
Robust.Client/Graphics/Clyde/Windowing/SDL3-CS/LICENSE
Normal 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>
|
||||
*
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
8043
Robust.Client/Graphics/Clyde/Windowing/SDL3-CS/SDL3.Core.cs
Normal file
8043
Robust.Client/Graphics/Clyde/Windowing/SDL3-CS/SDL3.Core.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -562,7 +562,7 @@ namespace Robust.Client.Placement
|
||||
}
|
||||
|
||||
coordinates = InputManager.MouseScreenPosition;
|
||||
return coordinates.IsValid;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CurrentEraserMouseCoordinates(out EntityCoordinates coordinates)
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -6,6 +6,5 @@
|
||||
<DevWindowTabUI Name="UI" />
|
||||
<DevWindowTabPerf Name="Perf" />
|
||||
<DevWindowTabTextures Name="Textures" />
|
||||
<DevWindowTabRenderTargets Name="RenderTargets" />
|
||||
</TabContainer>
|
||||
</Control>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
@@ -36,6 +36,5 @@ public enum DebugMonitor
|
||||
Input,
|
||||
Bandwidth,
|
||||
Prof,
|
||||
System,
|
||||
Version
|
||||
System
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ public sealed class RobustServerPackaging
|
||||
{
|
||||
"Textures",
|
||||
"Fonts",
|
||||
"EngineFonts",
|
||||
"Shaders",
|
||||
"Midi"
|
||||
};
|
||||
|
||||
public static async Task WriteServerResources(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user