mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e4e8aad34 | ||
|
|
257127afcd | ||
|
|
810e4e454d | ||
|
|
e0ff1e83e2 | ||
|
|
856cdb8a3d | ||
|
|
b783cd79be | ||
|
|
b5ba964f61 | ||
|
|
09676a1d9f | ||
|
|
6959f21927 | ||
|
|
26a1fb35b5 | ||
|
|
7fb3ce0e70 | ||
|
|
5497b52100 | ||
|
|
18b5f33080 | ||
|
|
30d3367c50 | ||
|
|
f6aabd1a22 | ||
|
|
6d229a3eb2 | ||
|
|
da28bdbce5 | ||
|
|
0181988225 | ||
|
|
fb2ba7460a | ||
|
|
b70d20a217 | ||
|
|
ebc33df457 | ||
|
|
697af6771c | ||
|
|
20706870da | ||
|
|
372fa39228 | ||
|
|
d6bfbe4f6f | ||
|
|
54645b4adf | ||
|
|
7c16573f3e | ||
|
|
8935b39987 | ||
|
|
388f8369a8 | ||
|
|
217d889e36 | ||
|
|
f243baccf2 | ||
|
|
790f42ea70 | ||
|
|
a5fcf122b8 | ||
|
|
df2d6ab8c2 | ||
|
|
23c90c0c45 | ||
|
|
c69756e7f1 | ||
|
|
07fbd5263c | ||
|
|
a1cdd60602 | ||
|
|
6fcaee91b6 | ||
|
|
4d4f353680 | ||
|
|
9f0dad80e4 | ||
|
|
c3f4b9bd67 | ||
|
|
641411288f | ||
|
|
1fb7d3e723 | ||
|
|
8cbc5d4cd8 | ||
|
|
b4863dcc38 | ||
|
|
e771530de2 | ||
|
|
ce3a5f6bfa | ||
|
|
1cd802640a | ||
|
|
1983734e2d | ||
|
|
ea380056b4 | ||
|
|
9c26fba308 | ||
|
|
3de48d7595 | ||
|
|
7a510298e1 | ||
|
|
046db645e9 | ||
|
|
63e383bb17 | ||
|
|
e316649fd1 | ||
|
|
121b58ee9a | ||
|
|
d11f4bcc14 | ||
|
|
735ef09d42 | ||
|
|
772173cbaf | ||
|
|
4bd7aa16c1 | ||
|
|
bc4b4d3e6f | ||
|
|
7d9a039252 | ||
|
|
857f9a540b | ||
|
|
6ae332d543 | ||
|
|
f4786f2d90 | ||
|
|
dcbe0505dc | ||
|
|
c3489d4ded | ||
|
|
8498634993 | ||
|
|
3d289fbd83 | ||
|
|
ebce0daa1b | ||
|
|
bbbfcca303 | ||
|
|
e195ac4ce6 | ||
|
|
dc5cbd085b | ||
|
|
c4dff678a9 | ||
|
|
cd9616c87c | ||
|
|
d1c6c11755 | ||
|
|
1ebac7c894 | ||
|
|
6b41be8901 | ||
|
|
51c929c8ec | ||
|
|
4863b09f0a | ||
|
|
cdd3afaa4c | ||
|
|
fee67b648c | ||
|
|
0bf4123b8d | ||
|
|
9ea51432d1 | ||
|
|
c8da6f30a3 | ||
|
|
974c1e827d | ||
|
|
3c48b24539 | ||
|
|
d8aefe5118 | ||
|
|
6d9a4719a9 | ||
|
|
893173ab17 | ||
|
|
7c0f1b8031 | ||
|
|
bb57f82811 | ||
|
|
0fc9b0acd0 | ||
|
|
f2b7f0d8d2 | ||
|
|
0ec189dece | ||
|
|
c876eb1f4c | ||
|
|
1037fc735e | ||
|
|
d5df765467 | ||
|
|
93cf9f4227 | ||
|
|
d2977e2a63 | ||
|
|
a3f0ea19c4 | ||
|
|
d9032b8757 | ||
|
|
cba6e37f9f | ||
|
|
90ec9a80c9 | ||
|
|
7eaf2f590b | ||
|
|
0439ea9893 | ||
|
|
74aa8fa9ed | ||
|
|
ceeb002692 | ||
|
|
78d807b13c | ||
|
|
5cd4c187bf | ||
|
|
fec477bf41 | ||
|
|
9d00b1f093 | ||
|
|
de0871d17b | ||
|
|
053c469cac | ||
|
|
efa8975bc6 | ||
|
|
4851e913b0 | ||
|
|
74a318c521 | ||
|
|
e52a6bbbf2 | ||
|
|
e169d6a5a2 | ||
|
|
3634ee636b | ||
|
|
2349728eab | ||
|
|
777f02cadd | ||
|
|
0fc6f2bce6 | ||
|
|
dc3705e520 | ||
|
|
01f71ca55a | ||
|
|
c5e812836b | ||
|
|
56eda3ea92 | ||
|
|
9dffd36319 | ||
|
|
a45b72a1c5 | ||
|
|
bd0579ed6d | ||
|
|
c73b54862e | ||
|
|
6436ff8040 | ||
|
|
98313ae369 | ||
|
|
0e63391203 | ||
|
|
261bfaeeb8 | ||
|
|
4017e1f57e | ||
|
|
e170bf1ad2 | ||
|
|
da0abd2535 |
34
.github/workflows/build-all-configurations.yml
vendored
Normal file
34
.github/workflows/build-all-configurations.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Build All Configurations
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
targetOS: [Windows, Linux, MacOS]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build Debug
|
||||
run: dotnet build --no-restore --configuration Debug /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
|
||||
- name: Build Tools
|
||||
run: dotnet build --no-restore --configuration Tools /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
|
||||
- name: Build Release
|
||||
run: dotnet build --no-restore --configuration Release /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
|
||||
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] - temporarily disabled due to libfreetype.dll errors.
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
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 -p windows mac linux
|
||||
run: Tools/package_client_build.py
|
||||
|
||||
- name: Shuffle files around
|
||||
run: |
|
||||
|
||||
@@ -44,10 +44,11 @@
|
||||
<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.OpenAL" Version="4.7.7" />
|
||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
|
||||
<PackageVersion Include="Pidgin" Version="3.3.0" />
|
||||
<PackageVersion Include="Robust.Natives" Version="0.1.1" />
|
||||
<PackageVersion Include="Robust.Natives" Version="0.2.1" />
|
||||
<PackageVersion Include="Robust.Natives.Zstd" Version="0.1.0-zstd1.5.7" />
|
||||
<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" />
|
||||
@@ -55,11 +56,13 @@
|
||||
<PackageVersion Include="Serilog" Version="4.2.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.2.2" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.1.0" />
|
||||
<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 -->
|
||||
<PropertyGroup><Version>263.0.0</Version></PropertyGroup>
|
||||
</Project>
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false"/>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false">
|
||||
<SetConfiguration Condition="'$(Configuration)' == 'DebugOpt'">Configuration=Debug</SetConfiguration>
|
||||
<SetConfiguration Condition="'$(Configuration)' == 'Tools'">Configuration=Release</SetConfiguration>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- XamlIL does not make use of special Robust configurations like DebugOpt. Convert these down. -->
|
||||
|
||||
Submodule NetSerializer updated: 4882400f2c...84ab8fec64
181
RELEASE-NOTES.md
181
RELEASE-NOTES.md
@@ -54,6 +54,187 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 267.0.2
|
||||
|
||||
|
||||
## 267.0.1
|
||||
|
||||
|
||||
## 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.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* A new analyzer has been added that will error if you attempt to subscribe to `AfterAutoHandleStateEvent` on a
|
||||
component that doesn't have the `AutoGenerateComponentState` attribute, or doesn't have the first argument of that
|
||||
attribute set to `true`. In most cases you will want to set said argument to `true`.
|
||||
* The fields on `AutoGenerateComponentStateAttribute` are now `readonly`. Setting these directly (instead of using the constructor arguments) never worked in the first place, so this change only catches existing programming errors.
|
||||
* When a player disconnects, `ISharedPlayerManager.PlayerStatusChanged` is now fired *after* removing the session from the `Sessions` list.
|
||||
* `.rsi` files are now compacted into individual `.rsic` files on packaging. This should significantly reduce file count & improve performance all over release builds, but breaks the ability to access `.png` files into RSIs directly. To avoid this, `"rsic": false` can be specified in the RSI's JSON metadata.
|
||||
* The `scale` command has been removed, with the intent of it being moved to content instead.
|
||||
|
||||
### New features
|
||||
|
||||
* ViewVariables editors for `ProtoId` fields now have a Select button which opens a window listing all available prototypes of the appropriate type.
|
||||
* added **IConfigurationManager**.*SubscribeMultiple* ext. method to provide simpler way to unsubscribe from multiple cvar at once
|
||||
* Added `SharedMapSystem.QueueDeleteMap`, which deletes a map with the specified MapId in the next tick.
|
||||
* Added generic version of `ComponentRegistry.TryGetComponent`.
|
||||
* `AttributeHelper.HasAttribute` has had an overload's type signature loosened from `INamedTypeSymbol` to `ITypeSymbol`.
|
||||
* Errors are now logged when sending messages to disconnected `INetChannel`s.
|
||||
* Warnings are now logged if sending a message via Lidgren failed for some reason.
|
||||
* `.yml` and `.ftl` files in the same directory are now concatenated onto each other, to reduce file count in packaged builds. This is done through the new `AssetPassMergeTextDirectories` pass.
|
||||
* Added `System.Linq.ImmutableArrayExtensions` to sandbox.
|
||||
* `ImmutableDictionary<TKey, TValue>` and `ImmutableHashSet<T>` can now be network serialized.
|
||||
* `[AutoPausedField]` now works on fields of type `Dictionary<TKey, TimeSpan>`.
|
||||
* `[NotYamlSerializable]` analyzer now detects nullable fields of the not-serializable type.
|
||||
* `ItemList` items can now have a scale applied for the icon.
|
||||
* Added new OS mouse cursor shapes for the SDL3 backend. These are not available on the GLFW backend.
|
||||
* Added `IMidiRenderer.MinVolume` to scale the volume of MIDI notes.
|
||||
* Added `SharedPhysicsSystem.ScaleFixtures`, to apply the physics-only changes of the prior `scale` command.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* `LayoutContainer.SetMarginsPreset` and `SetAnchorAndMarginPreset` now correctly use the provided control's top anchor when calculating the margins for its presets; it previously used the bottom anchor instead. This may result in a few UI differences, by a few pixels at most.
|
||||
* `IConfigurationManager` no longer logs a warning when saving configuration in an integration test.
|
||||
* Fixed impossible-to-source `ChannelClosedException`s when sending some net messages to disconnected `INetChannel`s.
|
||||
* Fixed an edge case causing some color values to throw an error in `ColorNaming`.
|
||||
* Fresh builds from specific projects should no longer cause errors related to `Robust.Client.Injectors` not being found.
|
||||
* Stopped errors getting logged about `NoteOff` and `NoteOn` operations failing in MIDI.
|
||||
* Fixed MIDI players not resuming properly when re-entering PVS range.
|
||||
|
||||
### Other
|
||||
|
||||
* Updated ImageSharp to 3.1.11 to stop the warning about a DoS vulnerability.
|
||||
* Prototype YAML documents that are completely empty are now skipped by the prototype loader. Previously they would cause a load error for the whole file.
|
||||
* `TileSpawnWindow` can now be localized.
|
||||
* `BaseWindow` uses the new mouse cursor shapes for diagonal resizing.
|
||||
* `NFluidsynth` has been updated to 0.2.0
|
||||
|
||||
### Internal
|
||||
|
||||
* Added `uitest` tab for standard mouse cursor shapes.
|
||||
|
||||
|
||||
## 265.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* More members in `IntegrationInstance` now enforce that the instance is idle before accessing it.
|
||||
* `Prototype.ValidateDirectory` now requires that prototype IDs have no spaces or periods in them.
|
||||
* `IPrototypeManager.TryIndex` no longer logs errors unless using the overload with an optional parameter. Use `Resolve()` instead if error logging is desired.
|
||||
* `LocalizedCommands` now has a `Loc` property that refers to `LocalizationManager`. This can cause compile failures if you have static methods in child types that referenced static `Loc`.
|
||||
* `[AutoGenerateComponentState]` now works on parent members for inherited classes. This can cause compile failures in certain formerly silently broken cases with overriden properties.
|
||||
* `Vector3`, `Vector4`, `Quaternion`, and `Matrix4` have been removed from `Robust.Shared.Maths`. Use the `System.Numerics` types instead.
|
||||
|
||||
### New features
|
||||
|
||||
* `RobustClientPackaging.WriteClientResources()` and `RobustServerPackaging.WriteServerResources()` now have an overload taking in a set of things to ignore in the content resources directory.
|
||||
* Added `IPrototypeManager.Resolve()`, which logs an error if the resolved prototype does not exist. This is effectively the previous (but not original) default behavior of `IPrototypeManager.TryIndex`.
|
||||
* There's now a ViewVariables property editor for tuples.
|
||||
* Added `ColorNaming` helper functions for getting textual descriptions of color values.
|
||||
* Added Oklab/Oklch conversion functions for `Color`.
|
||||
* `ColorSelectorSliders` now displays textual descriptions of color values.
|
||||
* Added `TimeSpanExt.TryTimeSpan` to parse `TimeSpan`s with the `1.5h` format available in YAML.
|
||||
* Added `ITestContextLike` and related classes to allow controlling pooled integration instances better.
|
||||
* `EntProtoId` VV prop editors now don't allow setting invalid prototype IDs, inline with `ProtoId<T>`.
|
||||
* Custom VV controls can now be registered using `IViewVariableControlFactory`.
|
||||
* The entity spawn window now shows all placement modes registered with `IPlacementManager`.
|
||||
* Added `VectorHelpers.InterpolateCubic` for `System.Numerics` `Vector3` and `Vector4`.
|
||||
* Added deconstruct helpers for `System.Numerics` `Vector3` and `Vector4`.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Pooled integration instances returned by `RobustIntegrationTest` are now treated as non-idle, for consistency with non-pooled startups.
|
||||
* `SharedAudioSystem.SetState` no longer calls `DirtyField` on `PlaybackPosition`, an unnetworked field.
|
||||
* Fix loading texture files from the root directory.
|
||||
* Fix integration test pooling leaking non-reusable instances.
|
||||
* Fix multiple bugs where VV displayed the wrong property editor for remote values.
|
||||
* VV displays group headings again in member list.
|
||||
* Fix a stack overflow that could occur with `ColorSelectorSliders`.
|
||||
* `MidiRenderer` now properly handles `NoteOn` events with 0 velocity (which should actually be treated as `NoteOff` events).
|
||||
|
||||
### Other
|
||||
|
||||
* The debug assert for `RobustRandom.Next(TimeSpan, TimeSpan)` now allows for the two arguments to be equal.
|
||||
* The configuration system will now report an error instead of warning if it fails to load the config file.
|
||||
* Members in `IntegrationInstance` that enforce the instance is idle now always allow access from the instance's thread (e.g. from a callback).
|
||||
* `IPrototypeManager` methods now have `[ForbidLiteral]` where appropriate.
|
||||
* Performance improvements to physics system.
|
||||
* `[ValidatePrototypeIdAttribute]` has been marked as obsolete.
|
||||
* `ParallelManager` no longer cuts out exception information for caught job exceptions.
|
||||
* Improved logging for PVS uninitialized/deleted entity errors.
|
||||
|
||||
### Internal
|
||||
|
||||
* General code & warning cleanup.
|
||||
* Fix `VisibilityTest` being unreliable.
|
||||
* `ColorSelectorSliders` has been internally refactored.
|
||||
* Added CI workflows that test all RT build configurations.
|
||||
|
||||
## 264.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `IPrototypeManager.Index(Type kind, string id)` now throws `UnknownPrototypeException` instead of `KeyNotFoundException`, for consistency with `IPrototypeManager.Index<T>`.
|
||||
|
||||
### New features
|
||||
|
||||
* Types can now implement the new interface `IRobustCloneable<T>` to be cloned by the component state source generator.
|
||||
* Added extra Roslyn Analyzers to detect some misuse of prototypes:
|
||||
* Network serializing prototypes (tagging them with `[Serializable, NetSerializable]`).
|
||||
* Constructing new instances of prototypes directly.
|
||||
* Add `PrototypeManagerExt.Index` helper function that takes a nullable `ProtoId<T>`, returning null if the ID is null.
|
||||
* Added an `AlwaysActive` field to `WebViewControl` to make a browser window active even when not in the UI tree.
|
||||
* Made some common dependencies accessible through `IPlacementManager`.
|
||||
* Added a new `GENITIVE()` localization helper function, which is useful for certain languages.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Sprite scale is now correctly applied to sprite boundaries in `SpriteSystem.GetLocalBounds`.
|
||||
* Fixed documentation for `IPrototypeManager.Index<T>` stating that `KeyNotFoundException` gets thrown, when in actuality `UnknownPrototypeException` gets thrown.
|
||||
|
||||
### Other
|
||||
|
||||
* More tiny optimizations to `DataDefinitionAnalyzer`.
|
||||
* NetSerializer has been updated. On debug, it will now report *where* a type that can't be serialized is referenced from.
|
||||
|
||||
### Internal
|
||||
|
||||
* Minor internal code cleanup.
|
||||
|
||||
|
||||
## 263.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
@@ -21,7 +21,8 @@ zzzz-object-pronoun = { GENDER($ent) ->
|
||||
}
|
||||
|
||||
# Used internally by the DAT-OBJ() function.
|
||||
# Not used in en-US. Created for supporting other languages.
|
||||
# Not used in en-US. Created to support other languages.
|
||||
# (e.g., "to him," "for her")
|
||||
zzzz-dat-object = { GENDER($ent) ->
|
||||
[male] him
|
||||
[female] her
|
||||
@@ -29,6 +30,16 @@ zzzz-dat-object = { GENDER($ent) ->
|
||||
*[neuter] it
|
||||
}
|
||||
|
||||
# Used internally by the GENITIVE() function.
|
||||
# Not used in en-US. Created to support other languages.
|
||||
# e.g., "у него" (Russian), "seines Vaters" (German).
|
||||
zzzz-genitive = { GENDER($ent) ->
|
||||
[male] his
|
||||
[female] her
|
||||
[epicene] their
|
||||
*[neuter] its
|
||||
}
|
||||
|
||||
# Used internally by the POSS-PRONOUN() function.
|
||||
zzzz-possessive-pronoun = { GENDER($ent) ->
|
||||
[male] his
|
||||
|
||||
33
Resources/Locale/en-US/color-naming.ftl
Normal file
33
Resources/Locale/en-US/color-naming.ftl
Normal file
@@ -0,0 +1,33 @@
|
||||
color-hue-chroma-lightness = {$lightness} {$chroma} {$hue}
|
||||
color-hue-chroma = {$chroma} {$hue}
|
||||
color-hue-lightness = {$lightness} {$hue}
|
||||
color-very-dark = very dark
|
||||
color-dark = dark
|
||||
color-light = light
|
||||
color-very-light = very light
|
||||
color-mixed-hue = {$a} {$b}
|
||||
color-pale = pale
|
||||
color-gray-adjective = gray
|
||||
color-strong = strong
|
||||
color-pink = pink
|
||||
color-red = red
|
||||
color-orange = orange
|
||||
color-yellow = yellow
|
||||
color-green = green
|
||||
color-cyan = cyan
|
||||
color-blue = blue
|
||||
color-purple = purple
|
||||
color-brown = brown
|
||||
color-white = white
|
||||
color-gray = gray
|
||||
color-black = black
|
||||
color-unknown = unknown color, you should not see this
|
||||
|
||||
color-pink-color-red = pinkish red
|
||||
color-red-color-orange = reddish orange
|
||||
color-orange-color-yellow = orangeish yellow
|
||||
color-yellow-color-green = yellowish green
|
||||
color-green-color-cyan = greenish cyan
|
||||
color-cyan-color-blue = cyanish blue
|
||||
color-blue-color-purple = blueish purple
|
||||
color-purple-color-pink = purpleish pink
|
||||
@@ -411,9 +411,6 @@ cmd-spawn-help = spawn <prototype> OR spawn <prototype> <relative entity ID> OR
|
||||
cmd-cspawn-desc = Spawns a client-side entity with specific type at your feet.
|
||||
cmd-cspawn-help = cspawn <entity type>
|
||||
|
||||
cmd-scale-desc = Increases or decreases an entity's size naively.
|
||||
cmd-scale-help = scale <entityUid> <float>
|
||||
|
||||
cmd-dumpentities-desc = Dump entity list.
|
||||
cmd-dumpentities-help = Dumps entity list of UIDs and prototype.
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
## EntitySpawnWindow
|
||||
|
||||
entity-spawn-window-title = Entity Spawn Panel
|
||||
entity-spawn-window-search-bar-placeholder = search
|
||||
entity-spawn-window-clear-button = Clear
|
||||
entity-spawn-window-replace-button-text = Replace
|
||||
entity-spawn-window-override-menu-tooltip = Override placement
|
||||
|
||||
@@ -22,3 +20,5 @@ output-panel-scroll-down-button-text = Scroll Down
|
||||
## Common Used
|
||||
|
||||
window-erase-button-text = Erase Mode
|
||||
window-search-bar-placeholder = Search
|
||||
window-clear-button = Clear
|
||||
|
||||
@@ -2,6 +2,7 @@ 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
|
||||
@@ -70,8 +71,8 @@ input-key-MouseButton9 = Mouse 9
|
||||
|
||||
input-key-LSystem-win = Left Win
|
||||
input-key-RSystem-win = Right Win
|
||||
input-key-LSystem-mac = Left Cmd
|
||||
input-key-RSystem-mac = Right Cmd
|
||||
input-key-LSystem-mac = Left ⌘
|
||||
input-key-RSystem-mac = Right ⌘
|
||||
input-key-LSystem-linux = Left Meta
|
||||
input-key-RSystem-linux = Right Meta
|
||||
|
||||
|
||||
@@ -25,3 +25,9 @@ vv-sound-reference-distance = Reference Distance
|
||||
vv-sound-loop = Loop
|
||||
vv-sound-play-offset = Play Offset (s)
|
||||
vv-sound-variation = Pitch variation
|
||||
|
||||
|
||||
## ProtoId
|
||||
vv-protoid-id-placeholder = Prototype ID
|
||||
vv-protoid-select-button-label = Select
|
||||
vv-protoid-addwindow-title = Set Prototype
|
||||
|
||||
110
Robust.Analyzers.Tests/AfterAutoHandleStateAnalyzerTest.cs
Normal file
110
Robust.Analyzers.Tests/AfterAutoHandleStateAnalyzerTest.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.AfterAutoHandleStateAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture, TestOf(typeof(AfterAutoHandleStateAnalyzer))]
|
||||
public sealed class AfterAutoHandleStateAnalyzerTest
|
||||
{
|
||||
private const string SubscribeEventDef = """
|
||||
using System;
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public readonly struct EntityUid;
|
||||
|
||||
public abstract class EntitySystem
|
||||
{
|
||||
public void SubscribeLocalEvent<T, TEvent>() where TEvent : notnull { }
|
||||
}
|
||||
|
||||
public interface IComponent;
|
||||
public interface IComponentState;
|
||||
""";
|
||||
|
||||
// A rare case for block-scoped namespace, I thought. Then I realized this
|
||||
// only needed the one type definition.
|
||||
private const string OtherTypeDefs = """
|
||||
using System;
|
||||
|
||||
namespace JetBrains.Annotations
|
||||
{
|
||||
public sealed class BaseTypeRequiredAttribute(Type baseType) : Attribute;
|
||||
}
|
||||
""";
|
||||
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpAnalyzerTest<AfterAutoHandleStateAnalyzer, DefaultVerifier>
|
||||
{
|
||||
TestState = { Sources = { code } }
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(test.TestState,
|
||||
"Robust.Shared.Analyzers.ComponentNetworkGeneratorAuxiliary.cs",
|
||||
"Robust.Shared.GameObjects.EventBusAttributes.cs");
|
||||
|
||||
test.TestState.Sources.Add(("EntitySystem.Subscriptions.cs", SubscribeEventDef));
|
||||
test.TestState.Sources.Add(("Types.cs", OtherTypeDefs));
|
||||
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
[AutoGenerateComponentState(true)]
|
||||
public sealed class AutoGenTrue;
|
||||
[AutoGenerateComponentState(true, true)]
|
||||
public sealed class AutoGenTrueTrue;
|
||||
|
||||
public sealed class NotAutoGen;
|
||||
[AutoGenerateComponentState]
|
||||
public sealed class AutoGenNoArgs;
|
||||
[AutoGenerateComponentState(false)]
|
||||
public sealed class AutoGenFalse;
|
||||
|
||||
public sealed class Foo : EntitySystem
|
||||
{
|
||||
public void Good()
|
||||
{
|
||||
// Subscribing to other events works
|
||||
SubscribeLocalEvent<AutoGenNoArgs, object>();
|
||||
// First arg true allows subscribing
|
||||
SubscribeLocalEvent<AutoGenTrue, AfterAutoHandleStateEvent>();
|
||||
SubscribeLocalEvent<AutoGenTrueTrue, AfterAutoHandleStateEvent>();
|
||||
}
|
||||
|
||||
public void Bad()
|
||||
{
|
||||
// Can't subscribe if AutoGenerateComponentState isn't even present
|
||||
SubscribeLocalEvent<NotAutoGen, AfterAutoHandleStateEvent>();
|
||||
|
||||
// Can't subscribe if first arg is not specified/false
|
||||
SubscribeLocalEvent<AutoGenNoArgs, AfterAutoHandleStateEvent>();
|
||||
SubscribeLocalEvent<AutoGenFalse, AfterAutoHandleStateEvent>();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(29,9): error RA0040: Tried to subscribe to AfterAutoHandleStateEvent for 'NotAutoGen' which doesn't have an AutoGenerateComponentState attribute
|
||||
VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttribute).WithSpan(29, 9, 29, 69).WithArguments("NotAutoGen"),
|
||||
// /0/Test0.cs(32,9): error RA0041: Tried to subscribe to AfterAutoHandleStateEvent for 'AutoGenNoArgs' which doesn't have raiseAfterAutoHandleState set
|
||||
VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttributeParam).WithSpan(32, 9, 32, 72).WithArguments("AutoGenNoArgs"),
|
||||
// /0/Test0.cs(33,9): error RA0041: Tried to subscribe to AfterAutoHandleStateEvent for 'AutoGenFalse' which doesn't have raiseAfterAutoHandleState set
|
||||
VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttributeParam).WithSpan(33, 9, 33, 71).WithArguments("AutoGenFalse")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
extern alias SerializationGenerator;
|
||||
extern alias SerializationGenerator;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis;
|
||||
@@ -126,6 +126,48 @@ public sealed class ComponentPauseGeneratorTest
|
||||
""");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDictionary()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
[AutoPausedField]
|
||||
public Dictionary<string, TimeSpan> Foo;
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoDiagnostics(result);
|
||||
ExpectSource(
|
||||
result,
|
||||
"""
|
||||
// <auto-generated />
|
||||
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
public partial class FooComponent
|
||||
{
|
||||
[RobustAutoGenerated]
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public sealed class FooComponent_AutoPauseSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
||||
}
|
||||
|
||||
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
foreach (var key in component.Foo.Keys)
|
||||
component.Foo[key] += args.PausedTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoState()
|
||||
{
|
||||
|
||||
@@ -203,6 +203,8 @@ public sealed class DataDefinitionAnalyzerTest
|
||||
|
||||
[NotYamlSerializable]
|
||||
public sealed class NotSerializableClass { }
|
||||
[NotYamlSerializable]
|
||||
public readonly struct NotSerializableStruct { }
|
||||
|
||||
[DataDefinition]
|
||||
public sealed partial class Foo
|
||||
@@ -213,6 +215,21 @@ public sealed class DataDefinitionAnalyzerTest
|
||||
[DataField]
|
||||
public NotSerializableClass BadProperty { get; set; }
|
||||
|
||||
[DataField]
|
||||
public NotSerializableClass? BadNullableField;
|
||||
|
||||
[DataField]
|
||||
public NotSerializableStruct BadStructField;
|
||||
|
||||
[DataField]
|
||||
public NotSerializableStruct BadStructProperty { get; set; }
|
||||
|
||||
[DataField]
|
||||
public NotSerializableStruct? BadNullableStructField;
|
||||
|
||||
[DataField]
|
||||
public NotSerializableStruct? BadNullableStructProperty { get; set; }
|
||||
|
||||
public NotSerializableClass GoodField; // Not a DataField, not a problem
|
||||
|
||||
public NotSerializableClass GoodProperty { get; set; } // Not a DataField, not a problem
|
||||
@@ -220,10 +237,20 @@ public sealed class DataDefinitionAnalyzerTest
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(10,12): error RA0033: Data field BadField in data definition Foo is type NotSerializableClass, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(10, 12, 10, 32).WithArguments("BadField", "Foo", "NotSerializableClass"),
|
||||
// /0/Test0.cs(13,12): error RA0033: Data field BadProperty in data definition Foo is type NotSerializableClass, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(13, 12, 13, 32).WithArguments("BadProperty", "Foo", "NotSerializableClass")
|
||||
// /0/Test0.cs(12,12): error RA0033: Data field BadField in data definition Foo is type NotSerializableClass, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(12, 12, 12, 32).WithArguments("BadField", "Foo", "NotSerializableClass"),
|
||||
// /0/Test0.cs(15,12): error RA0033: Data field BadProperty in data definition Foo is type NotSerializableClass, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(15, 12, 15, 32).WithArguments("BadProperty", "Foo", "NotSerializableClass"),
|
||||
// /0/Test0.cs(18,12): error RA0036: Data field BadNullableField in data definition Foo is type NotSerializableClass, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(18, 12, 18, 33).WithArguments("BadNullableField", "Foo", "NotSerializableClass"),
|
||||
// /0/Test0.cs(21,12): error RA0036: Data field BadStructField in data definition Foo is type NotSerializableStruct, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(21, 12, 21, 33).WithArguments("BadStructField", "Foo", "NotSerializableStruct"),
|
||||
// /0/Test0.cs(24,12): error RA0036: Data field BadStructProperty in data definition Foo is type NotSerializableStruct, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(24, 12, 24, 33).WithArguments("BadStructProperty", "Foo", "NotSerializableStruct"),
|
||||
// /0/Test0.cs(27,12): error RA0036: Data field BadNullableStructField in data definition Foo is type NotSerializableStruct, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(27, 12, 27, 34).WithArguments("BadNullableStructField", "Foo", "NotSerializableStruct"),
|
||||
// /0/Test0.cs(30,12): error RA0036: Data field BadNullableStructProperty in data definition Foo is type NotSerializableStruct, which is not YAML serializable
|
||||
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(30, 12, 30, 34).WithArguments("BadNullableStructProperty", "Foo", "NotSerializableStruct")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
64
Robust.Analyzers.Tests/PrototypeInstantiationAnalyzerTest.cs
Normal file
64
Robust.Analyzers.Tests/PrototypeInstantiationAnalyzerTest.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeInstantiationAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
[TestOf(typeof(PrototypeInstantiationAnalyzer))]
|
||||
public sealed class PrototypeInstantiationAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new RTAnalyzerTest<PrototypeInstantiationAnalyzer>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Prototypes.Attributes.cs",
|
||||
"Robust.Shared.Prototypes.IPrototype.cs",
|
||||
"Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
public sealed class FooPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
public static class Bad
|
||||
{
|
||||
public static FooPrototype Real()
|
||||
{
|
||||
return new FooPrototype();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(15,16): warning RA0039: Do not instantiate prototypes directly. Prototypes should always be instantiated by the prototype manager.
|
||||
VerifyCS.Diagnostic().WithSpan(15, 16, 15, 34));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeNetSerializableAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
[TestOf(typeof(PrototypeNetSerializableAnalyzer))]
|
||||
public sealed class PrototypeNetSerializableAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new RTAnalyzerTest<PrototypeNetSerializableAnalyzer>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Serialization.NetSerializableAttribute.cs",
|
||||
"Robust.Shared.Prototypes.Attributes.cs",
|
||||
"Robust.Shared.Prototypes.IPrototype.cs",
|
||||
"Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class FooPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(7,21): warning RA0037: Type FooPrototype is a prototype and marked as [NetSerializable]. Prototypes should not be directly sent over the network, send their IDs instead.
|
||||
VerifyCS.Diagnostic(PrototypeNetSerializableAnalyzer.RuleNetSerializable).WithSpan(7, 21, 7, 33).WithArguments("FooPrototype"),
|
||||
// /0/Test0.cs(7,21): warning RA0038: Type FooPrototype is a prototype and marked as [Serializable]. Prototypes should not be directly sent over the network, send their IDs instead.
|
||||
VerifyCS.Diagnostic(PrototypeNetSerializableAnalyzer.RuleSerializable).WithSpan(7, 21, 7, 33).WithArguments("FooPrototype"));
|
||||
}
|
||||
}
|
||||
17
Robust.Analyzers.Tests/RTAnalyzerTest.cs
Normal file
17
Robust.Analyzers.Tests/RTAnalyzerTest.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
public sealed class RTAnalyzerTest<TAnalyzer> : CSharpAnalyzerTest<TAnalyzer, DefaultVerifier>
|
||||
where TAnalyzer : DiagnosticAnalyzer, new()
|
||||
{
|
||||
protected override ParseOptions CreateParseOptions()
|
||||
{
|
||||
var baseOptions = (CSharpParseOptions) base.CreateParseOptions();
|
||||
return baseOptions.WithPreprocessorSymbols("ROBUST_ANALYZERS_TEST");
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LogicalName="Robust.Shared.Analyzers.AccessAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LogicalName="Robust.Shared.Analyzers.AccessPermissions.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ComponentNetworkGeneratorAuxiliary.cs" LogicalName="Robust.Shared.Analyzers.ComponentNetworkGeneratorAuxiliary.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\MustCallBaseAttribute.cs" LogicalName="Robust.Shared.IoC.MustCallBaseAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferNonGenericVariantForAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferOtherTypeAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs" LinkBase="Implementations" />
|
||||
@@ -17,6 +18,10 @@
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\GameObjects\EventBusAttributes.cs" LogicalName="Robust.Shared.GameObjects.EventBusAttributes.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Serialization\NetSerializableAttribute.cs" LogicalName="Robust.Shared.Serialization.NetSerializableAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Prototypes\Attributes.cs" LogicalName="Robust.Shared.Prototypes.Attributes.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Prototypes\IPrototype.cs" LogicalName="Robust.Shared.Prototypes.IPrototype.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Serialization\Manager\Attributes\DataFieldAttribute.cs" LogicalName="Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
85
Robust.Analyzers/AfterAutoHandleStateAnalyzer.cs
Normal file
85
Robust.Analyzers/AfterAutoHandleStateAnalyzer.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class AfterAutoHandleStateAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string AfterAutoHandleStateEventName = "AfterAutoHandleStateEvent";
|
||||
private const string AutoGenStateAttribute = "Robust.Shared.Analyzers.AutoGenerateComponentStateAttribute";
|
||||
private const string SubscribeLocalEventName = "SubscribeLocalEvent";
|
||||
|
||||
public static readonly DiagnosticDescriptor MissingAttribute = new(
|
||||
Diagnostics.IdAutoGenStateAttributeMissing,
|
||||
"Unreachable AfterAutoHandleState subscription",
|
||||
"Tried to subscribe to AfterAutoHandleStateEvent for '{0}' which doesn't have an "
|
||||
+ "AutoGenerateComponentState attribute",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
// Does this even show up anywhere in Rider? >:(
|
||||
"You must mark your component with '[AutoGenerateComponentState(true)]' to subscribe to this event."
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor MissingAttributeParam = new(
|
||||
Diagnostics.IdAutoGenStateParamMissing,
|
||||
"Unreachable AfterAutoHandleState subscription",
|
||||
"Tried to subscribe to AfterAutoHandleStateEvent for '{0}' which doesn't have "
|
||||
+ "raiseAfterAutoHandleState set",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"The AutoGenerateComponentState attribute must be passed 'true' in order to subscribe to this event."
|
||||
);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||
[MissingAttribute, MissingAttributeParam];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
// This is more to stop user error rather than code generation error
|
||||
// (Plus this shouldn't affect code gen anyway)
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterCompilationStartAction(compilationContext =>
|
||||
{
|
||||
var autoGenStateAttribute = compilationContext.Compilation.GetTypeByMetadataName(AutoGenStateAttribute);
|
||||
// No attribute, no analyzer.
|
||||
if (autoGenStateAttribute is null)
|
||||
return;
|
||||
|
||||
compilationContext.RegisterOperationAction(
|
||||
analysisContext => CheckEventSubscription(analysisContext, autoGenStateAttribute),
|
||||
OperationKind.Invocation);
|
||||
});
|
||||
}
|
||||
|
||||
private static void CheckEventSubscription(OperationAnalysisContext context, ITypeSymbol autoGenStateAttribute)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation operation)
|
||||
return;
|
||||
|
||||
// Check the method has the right name and has the right type args
|
||||
if (operation.TargetMethod is not
|
||||
{ Name: SubscribeLocalEventName, TypeArguments: [var component, { Name: AfterAutoHandleStateEventName }] })
|
||||
return;
|
||||
|
||||
// Search the component's attributes for something matching autoGenStateAttribute
|
||||
AttributeHelper.HasAttribute(component, autoGenStateAttribute, out var autoGenAttribute);
|
||||
|
||||
// First argument is raiseAfterAutoHandleState—note it shouldn't ever
|
||||
// be null, since it has a default, but eh.
|
||||
if (autoGenAttribute?.ConstructorArguments[0].Value is true)
|
||||
return;
|
||||
|
||||
context.ReportDiagnostic(Diagnostic.Create(autoGenAttribute is null ? MissingAttribute : MissingAttributeParam,
|
||||
operation.Syntax.GetLocation(),
|
||||
component.Name));
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
}, SymbolKind.NamedType);
|
||||
}
|
||||
|
||||
private void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
private static void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not TypeDeclarationSyntax declaration)
|
||||
return;
|
||||
@@ -147,7 +147,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
private static void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not FieldDeclarationSyntax field)
|
||||
return;
|
||||
@@ -186,6 +186,8 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
if (context.SemanticModel.GetSymbolInfo(field.Declaration.Type).Symbol is not ITypeSymbol fieldTypeSymbol)
|
||||
continue;
|
||||
|
||||
fieldTypeSymbol = TypeSymbolHelper.GetNullableUnderlyingTypeOrSelf(fieldTypeSymbol);
|
||||
|
||||
if (IsNotYamlSerializable(fieldSymbol, fieldTypeSymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldYamlSerializableRule,
|
||||
@@ -198,7 +200,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
private static void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not PropertyDeclarationSyntax property)
|
||||
return;
|
||||
@@ -239,6 +241,8 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
if (context.SemanticModel.GetSymbolInfo(property.Type).Symbol is not ITypeSymbol propertyTypeSymbol)
|
||||
return;
|
||||
|
||||
propertyTypeSymbol = TypeSymbolHelper.GetNullableUnderlyingTypeOrSelf(propertyTypeSymbol);
|
||||
|
||||
if (IsNotYamlSerializable(propertySymbol, propertyTypeSymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldYamlSerializableRule,
|
||||
|
||||
48
Robust.Analyzers/PrototypeInstantiationAnalyzer.cs
Normal file
48
Robust.Analyzers/PrototypeInstantiationAnalyzer.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class PrototypeInstantiationAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string PrototypeInterfaceType = "Robust.Shared.Prototypes.IPrototype";
|
||||
|
||||
public static readonly DiagnosticDescriptor Rule = new(
|
||||
Diagnostics.IdPrototypeInstantiation,
|
||||
"Do not instantiate prototypes directly",
|
||||
"Do not instantiate prototypes directly. Prototypes should always be instantiated by the prototype manager.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.EnableConcurrentExecution();
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.RegisterCompilationStartAction(static ctx =>
|
||||
{
|
||||
var prototypeInterface = ctx.Compilation.GetTypeByMetadataName(PrototypeInterfaceType);
|
||||
if (prototypeInterface == null)
|
||||
return;
|
||||
|
||||
ctx.RegisterOperationAction(symContext => Check(prototypeInterface, symContext), OperationKind.ObjectCreation);
|
||||
});
|
||||
}
|
||||
|
||||
private static void Check(INamedTypeSymbol prototypeInterface, OperationAnalysisContext ctx)
|
||||
{
|
||||
if (ctx.Operation is not IObjectCreationOperation { Type: { } resultType } creationOp)
|
||||
return;
|
||||
|
||||
if (!TypeSymbolHelper.ImplementsInterface(resultType, prototypeInterface))
|
||||
return;
|
||||
|
||||
ctx.ReportDiagnostic(Diagnostic.Create(Rule, creationOp.Syntax.GetLocation()));
|
||||
}
|
||||
}
|
||||
76
Robust.Analyzers/PrototypeNetSerializableAnalyzer.cs
Normal file
76
Robust.Analyzers/PrototypeNetSerializableAnalyzer.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class PrototypeNetSerializableAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string PrototypeInterfaceType = "Robust.Shared.Prototypes.IPrototype";
|
||||
private const string NetSerializableAttributeType = "Robust.Shared.Serialization.NetSerializableAttribute";
|
||||
|
||||
public static readonly DiagnosticDescriptor RuleNetSerializable = new(
|
||||
Diagnostics.IdPrototypeNetSerializable,
|
||||
"Prototypes should not be [NetSerializable]",
|
||||
"Type {0} is a prototype and marked as [NetSerializable]. Prototypes should not be directly sent over the network, send their IDs instead.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
|
||||
public static readonly DiagnosticDescriptor RuleSerializable = new(
|
||||
Diagnostics.IdPrototypeSerializable,
|
||||
"Prototypes should not be [Serializable]",
|
||||
"Type {0} is a prototype and marked as [Serializable]. Prototypes should not be directly sent over the network, send their IDs instead.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [
|
||||
RuleNetSerializable,
|
||||
RuleSerializable
|
||||
];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.EnableConcurrentExecution();
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
|
||||
context.RegisterCompilationStartAction(static ctx =>
|
||||
{
|
||||
var prototypeInterface = ctx.Compilation.GetTypeByMetadataName(PrototypeInterfaceType);
|
||||
var netSerializableAttribute = ctx.Compilation.GetTypeByMetadataName(NetSerializableAttributeType);
|
||||
|
||||
if (prototypeInterface == null || netSerializableAttribute == null)
|
||||
return;
|
||||
|
||||
ctx.RegisterSymbolAction(symbolContext => CheckClass(prototypeInterface, netSerializableAttribute, symbolContext), SymbolKind.NamedType);
|
||||
});
|
||||
}
|
||||
|
||||
private static void CheckClass(
|
||||
INamedTypeSymbol prototypeInterface,
|
||||
INamedTypeSymbol netSerializableAttribute,
|
||||
SymbolAnalysisContext symbolContext)
|
||||
{
|
||||
if (symbolContext.Symbol is not INamedTypeSymbol symbol)
|
||||
return;
|
||||
|
||||
if (!TypeSymbolHelper.ImplementsInterface(symbol, prototypeInterface))
|
||||
return;
|
||||
|
||||
if (AttributeHelper.HasAttribute(symbol, netSerializableAttribute, out _))
|
||||
{
|
||||
symbolContext.ReportDiagnostic(
|
||||
Diagnostic.Create(RuleNetSerializable, symbol.Locations[0], symbol.ToDisplayString()));
|
||||
}
|
||||
|
||||
if (symbol.IsSerializable)
|
||||
{
|
||||
symbolContext.ReportDiagnostic(
|
||||
Diagnostic.Create(RuleSerializable, symbol.Locations[0], symbol.ToDisplayString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
public static class Program
|
||||
internal static class Program
|
||||
{
|
||||
// This was supposed to be the main entry for the subprocess program... It doesn't work.
|
||||
public static int Main(string[] args)
|
||||
|
||||
@@ -162,9 +162,10 @@ namespace Robust.Client.WebView.Cef
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsOpen => _data != null;
|
||||
public bool IsLoading => _data?.Browser.IsLoading ?? false;
|
||||
|
||||
public void EnteredTree()
|
||||
public void StartBrowser()
|
||||
{
|
||||
DebugTools.AssertNull(_data);
|
||||
|
||||
@@ -195,7 +196,7 @@ namespace Robust.Client.WebView.Cef
|
||||
_data = new LiveData(texture, client, browser, renderer);
|
||||
}
|
||||
|
||||
public void ExitedTree()
|
||||
public void CloseBrowser()
|
||||
{
|
||||
DebugTools.AssertNotNull(_data);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -24,6 +25,7 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameControllerInternal _gameController = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
@@ -61,7 +63,10 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
var cachePath = "";
|
||||
if (_resourceManager.UserData is WritableDirProvider userData)
|
||||
cachePath = userData.GetFullPath(new ResPath("/cef_cache"));
|
||||
{
|
||||
var rootDir = UserDataDir.GetRootUserDataDir(_gameController);
|
||||
cachePath = Path.Combine(rootDir, "cef_cache", "0");
|
||||
}
|
||||
|
||||
var settings = new CefSettings()
|
||||
{
|
||||
|
||||
@@ -81,11 +81,13 @@ namespace Robust.Client.WebView.Headless
|
||||
|
||||
private sealed class WebViewControlImplDummy : DummyBase, IWebViewControlImpl
|
||||
{
|
||||
public void EnteredTree()
|
||||
public bool IsOpen => false;
|
||||
|
||||
public void StartBrowser()
|
||||
{
|
||||
}
|
||||
|
||||
public void ExitedTree()
|
||||
public void CloseBrowser()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@ namespace Robust.Client.WebView
|
||||
/// </summary>
|
||||
internal interface IWebViewControlImpl : IWebViewControl
|
||||
{
|
||||
void EnteredTree();
|
||||
void ExitedTree();
|
||||
public bool IsOpen { get; }
|
||||
|
||||
void StartBrowser();
|
||||
void CloseBrowser();
|
||||
void MouseMove(GUIMouseMoveEventArgs args);
|
||||
void MouseExited();
|
||||
void MouseWheel(GUIMouseWheelEventArgs args);
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Robust.Client.WebView
|
||||
[Dependency] private readonly IWebViewManagerInternal _webViewManager = default!;
|
||||
|
||||
private readonly IWebViewControlImpl _controlImpl;
|
||||
private bool _alwaysActive;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Url
|
||||
@@ -22,6 +23,21 @@ namespace Robust.Client.WebView
|
||||
set => _controlImpl.Url = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool AlwaysActive
|
||||
{
|
||||
get => _alwaysActive;
|
||||
set
|
||||
{
|
||||
_alwaysActive = value;
|
||||
|
||||
if (_alwaysActive && !_controlImpl.IsOpen)
|
||||
_controlImpl.StartBrowser();
|
||||
else if (!_alwaysActive && _controlImpl.IsOpen && !IsInsideTree)
|
||||
_controlImpl.CloseBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables] public bool IsLoading => _controlImpl.IsLoading;
|
||||
|
||||
public WebViewControl()
|
||||
@@ -39,14 +55,16 @@ namespace Robust.Client.WebView
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
_controlImpl.EnteredTree();
|
||||
if (!_controlImpl.IsOpen)
|
||||
_controlImpl.StartBrowser();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
_controlImpl.ExitedTree();
|
||||
if (!_alwaysActive)
|
||||
_controlImpl.CloseBrowser();
|
||||
}
|
||||
|
||||
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
|
||||
|
||||
@@ -3,8 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Maths;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -122,9 +120,9 @@ namespace Robust.Client.Animations
|
||||
case Vector2 vector2:
|
||||
return Vector2Helpers.InterpolateCubic((Vector2) preA, vector2, (Vector2) b, (Vector2) postB, t);
|
||||
case Vector3 vector3:
|
||||
return Vector3.InterpolateCubic((Vector3) preA, vector3, (Vector3) b, (Vector3) postB, t);
|
||||
return VectorHelpers.InterpolateCubic((Vector3) preA, vector3, (Vector3) b, (Vector3) postB, t);
|
||||
case Vector4 vector4:
|
||||
return Vector4.InterpolateCubic((Vector4) preA, vector4, (Vector4) b, (Vector4) postB, t);
|
||||
return VectorHelpers.InterpolateCubic((Vector4) preA, vector4, (Vector4) b, (Vector4) postB, t);
|
||||
case float f:
|
||||
return MathHelper.InterpolateCubic((float) preA, f, (float) b, (float) postB, t);
|
||||
case double d:
|
||||
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
@@ -145,7 +144,7 @@ internal sealed partial class AudioManager : IAudioInternal
|
||||
private static void RemoveEfx((int sourceHandle, int filterHandle) handles)
|
||||
{
|
||||
if (handles.filterHandle != 0)
|
||||
EFX.DeleteFilter(handles.filterHandle);
|
||||
ALC.EFX.DeleteFilter(handles.filterHandle);
|
||||
}
|
||||
|
||||
private void _checkAlcError(ALDevice device,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using Robust.Client.Audio.Effects;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using System.Numerics;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Effects;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -15,16 +16,16 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
|
||||
public AudioEffect(IAudioInternal manager)
|
||||
{
|
||||
Handle = EFX.GenEffect();
|
||||
Handle = ALC.EFX.GenEffect();
|
||||
_master = manager;
|
||||
EFX.Effect(Handle, EffectInteger.EffectType, (int) EffectType.EaxReverb);
|
||||
ALC.EFX.Effect(Handle, EffectInteger.EffectType, (int) EffectType.EaxReverb);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Handle != 0)
|
||||
{
|
||||
EFX.DeleteEffect(Handle);
|
||||
ALC.EFX.DeleteEffect(Handle);
|
||||
Handle = 0;
|
||||
}
|
||||
}
|
||||
@@ -43,14 +44,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbDensity, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDensity, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbDensity, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDensity, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -61,14 +62,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbDiffusion, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDiffusion, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbDiffusion, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDiffusion, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -79,14 +80,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbGain, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbGain, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbGain, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbGain, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -97,14 +98,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbGainHF, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbGainHF, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbGainHF, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbGainHF, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -115,14 +116,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbGainLF, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbGainLF, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbGainLF, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbGainLF, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -133,14 +134,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayTime, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayTime, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbDecayTime, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDecayTime, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -151,14 +152,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayHFRatio, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayHFRatio, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbDecayHFRatio, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDecayHFRatio, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -169,14 +170,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayLFRatio, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayLFRatio, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbDecayLFRatio, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbDecayLFRatio, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -187,14 +188,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsGain, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsGain, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsGain, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsGain, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -205,14 +206,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsDelay, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsDelay, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsDelay, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsDelay, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -223,7 +224,7 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
var value = EFX.GetEffect(Handle, EffectVector3.EaxReverbReflectionsPan);
|
||||
var value = ALC.EFX.GetEffect(Handle, EffectVector3.EaxReverbReflectionsPan);
|
||||
_master._checkAlError();
|
||||
return new Vector3(value.X, value.Z, value.Y);
|
||||
}
|
||||
@@ -231,7 +232,7 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
{
|
||||
_checkDisposed();
|
||||
var openVec = new OpenTK.Mathematics.Vector3(value.X, value.Y, value.Z);
|
||||
EFX.Effect(Handle, EffectVector3.EaxReverbReflectionsPan, ref openVec);
|
||||
ALC.EFX.Effect(Handle, EffectVector3.EaxReverbReflectionsPan, ref openVec);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -242,14 +243,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbGain, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbGain, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbGain, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbGain, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -260,14 +261,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbDelay, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbDelay, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbDelay, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbDelay, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -278,7 +279,7 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
var value = EFX.GetEffect(Handle, EffectVector3.EaxReverbLateReverbPan);
|
||||
var value = ALC.EFX.GetEffect(Handle, EffectVector3.EaxReverbLateReverbPan);
|
||||
_master._checkAlError();
|
||||
return new Vector3(value.X, value.Z, value.Y);
|
||||
}
|
||||
@@ -286,7 +287,7 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
{
|
||||
_checkDisposed();
|
||||
var openVec = new OpenTK.Mathematics.Vector3(value.X, value.Y, value.Z);
|
||||
EFX.Effect(Handle, EffectVector3.EaxReverbLateReverbPan, ref openVec);
|
||||
ALC.EFX.Effect(Handle, EffectVector3.EaxReverbLateReverbPan, ref openVec);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -297,14 +298,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoTime, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoTime, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbEchoTime, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbEchoTime, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -315,14 +316,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoDepth, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoDepth, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbEchoDepth, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbEchoDepth, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -333,14 +334,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationTime, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationTime, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbModulationTime, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbModulationTime, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -351,14 +352,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationDepth, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationDepth, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbModulationDepth, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbModulationDepth, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -369,14 +370,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -387,14 +388,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbHFReference, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbHFReference, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbHFReference, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbHFReference, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -405,14 +406,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbLFReference, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbLFReference, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbLFReference, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbLFReference, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -423,14 +424,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, value);
|
||||
ALC.EFX.Effect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
@@ -441,14 +442,14 @@ internal sealed class AudioEffect : IAudioEffect
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.GetEffect(Handle, EffectInteger.EaxReverbDecayHFLimit, out var value);
|
||||
ALC.EFX.GetEffect(Handle, EffectInteger.EaxReverbDecayHFLimit, out var value);
|
||||
_master._checkAlError();
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
EFX.Effect(Handle, EffectInteger.EaxReverbDecayHFLimit, value);
|
||||
ALC.EFX.Effect(Handle, EffectInteger.EaxReverbDecayHFLimit, value);
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
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 = EFX.GenAuxiliaryEffectSlot();
|
||||
internal int Handle = ALC.EFX.GenAuxiliaryEffectSlot();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Handle != -1)
|
||||
{
|
||||
EFX.DeleteAuxiliaryEffectSlot(Handle);
|
||||
ALC.EFX.DeleteAuxiliaryEffectSlot(Handle);
|
||||
Handle = -1;
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,11 @@ internal sealed class AuxiliaryAudio : IAuxiliaryAudio
|
||||
{
|
||||
if (effect is AudioEffect audEffect)
|
||||
{
|
||||
EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, audEffect.Handle);
|
||||
ALC.EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, audEffect.Handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, 0);
|
||||
ALC.EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,4 +213,6 @@ public interface IMidiRenderer : IDisposable
|
||||
/// Actually disposes of this renderer. Do NOT use outside the MIDI thread.
|
||||
/// </summary>
|
||||
internal void InternalDispose();
|
||||
|
||||
byte MinVolume { get; set; }
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
[Dependency] private readonly IRuntimeLog _runtime = default!;
|
||||
|
||||
private AudioSystem _audioSys = default!;
|
||||
private SharedPhysicsSystem _broadPhaseSystem = default!;
|
||||
private SharedPhysicsSystem _physics = default!;
|
||||
private SharedTransformSystem _xformSystem = default!;
|
||||
|
||||
public IReadOnlyList<IMidiRenderer> Renderers
|
||||
@@ -81,7 +81,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
private Thread? _midiThread;
|
||||
private ISawmill _midiSawmill = default!;
|
||||
private float _gain = 0f;
|
||||
private bool _volumeDirty = true;
|
||||
private bool _gainDirty = true;
|
||||
|
||||
// Not reliable until Fluidsynth is initialized!
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -96,7 +96,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
return;
|
||||
|
||||
_cfgMan.SetCVar(CVars.MidiVolume, clamped);
|
||||
_volumeDirty = true;
|
||||
_gainDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,8 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
"/usr/share/sounds/sf2/TimGM6mb.sf2",
|
||||
};
|
||||
|
||||
private static readonly string WindowsSoundfont = $@"{Environment.GetEnvironmentVariable("SystemRoot")}\system32\drivers\gm.dls";
|
||||
private static readonly string WindowsSoundfont =
|
||||
$@"{Environment.GetEnvironmentVariable("SystemRoot")}\system32\drivers\gm.dls";
|
||||
|
||||
private const string OsxSoundfont =
|
||||
"/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls";
|
||||
@@ -145,11 +146,13 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (FluidsynthInitialized || _failedInitialize) return;
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
|
||||
{
|
||||
_gain = value;
|
||||
_volumeDirty = true;
|
||||
}, true);
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume,
|
||||
value =>
|
||||
{
|
||||
_gain = value;
|
||||
_gainDirty = true;
|
||||
},
|
||||
true);
|
||||
|
||||
_midiSawmill = _logger.GetSawmill("midi");
|
||||
#if DEBUG
|
||||
@@ -167,13 +170,15 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
// not a directory, preserve the old file and create an actual directory
|
||||
else if (!_resourceManager.UserData.IsDir(CustomSoundfontDirectory))
|
||||
{
|
||||
_resourceManager.UserData.Rename(CustomSoundfontDirectory, CustomSoundfontDirectory.WithName(CustomSoundfontDirectory.Filename + ".old"));
|
||||
_resourceManager.UserData.Rename(CustomSoundfontDirectory,
|
||||
CustomSoundfontDirectory.WithName(CustomSoundfontDirectory.Filename + ".old"));
|
||||
_resourceManager.UserData.CreateDir(CustomSoundfontDirectory);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
NFluidsynth.Logger.SetLoggerMethod(_loggerDelegate); // Will cause a safe DllNotFoundException if not available.
|
||||
NFluidsynth.Logger
|
||||
.SetLoggerMethod(_loggerDelegate); // Will cause a safe DllNotFoundException if not available.
|
||||
|
||||
_settings = new Settings();
|
||||
_settings["synth.sample-rate"].DoubleValue = 44100;
|
||||
@@ -193,7 +198,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
//_settings["synth.verbose"].IntValue = 1; // Useful for debugging.
|
||||
|
||||
var midiParallel = _cfgMan.GetCVar(CVars.MidiParallelism);
|
||||
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(midiParallel) * 2048), 1, 65535);
|
||||
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int) (Math.Log2(midiParallel) * 2048), 1, 65535);
|
||||
_settings["synth.cpu-cores"].IntValue = Math.Clamp(midiParallel, 1, 256);
|
||||
|
||||
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
|
||||
@@ -219,7 +224,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
};
|
||||
|
||||
_audioSys = _entityManager.EntitySysManager.GetEntitySystem<AudioSystem>();
|
||||
_broadPhaseSystem = _entityManager.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>();
|
||||
_physics = _entityManager.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>();
|
||||
_xformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
_entityManager.GetEntityQuery<PhysicsComponent>();
|
||||
_entityManager.GetEntityQuery<TransformComponent>();
|
||||
@@ -263,7 +268,8 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
soundfontLoader.SetCallbacks(_soundfontLoaderCallbacks);
|
||||
|
||||
var renderer = new MidiRenderer(_settings!, soundfontLoader, mono, this, _audio, _taskManager, _midiSawmill);
|
||||
var renderer =
|
||||
new MidiRenderer(_settings!, soundfontLoader, mono, this, _audio, _taskManager, _midiSawmill);
|
||||
|
||||
LoadSoundFontSetup(renderer);
|
||||
|
||||
@@ -273,6 +279,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
_renderers.Add(renderer);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
finally
|
||||
@@ -309,99 +316,23 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
_updateSemaphore.Release();
|
||||
|
||||
_volumeDirty = false;
|
||||
_gainDirty = false;
|
||||
}
|
||||
|
||||
private void UpdateRenderer(IMidiRenderer renderer, MapCoordinates listener)
|
||||
{
|
||||
// TODO: This should be sharing more code with AudioSystem.
|
||||
try
|
||||
{
|
||||
if (renderer.Disposed)
|
||||
return;
|
||||
|
||||
if (_volumeDirty)
|
||||
{
|
||||
renderer.Source.Gain = Gain;
|
||||
}
|
||||
|
||||
if (!renderer.Mono)
|
||||
{
|
||||
renderer.Source.Global = true;
|
||||
return;
|
||||
}
|
||||
|
||||
MapCoordinates mapPos;
|
||||
|
||||
if (renderer.TrackingEntity is {} trackedEntity && !_entityManager.Deleted(trackedEntity))
|
||||
{
|
||||
renderer.TrackingCoordinates = _xformSystem.GetMapCoordinates(renderer.TrackingEntity.Value);
|
||||
|
||||
// Pause it if the attached entity is paused.
|
||||
if (_entityManager.IsPaused(renderer.TrackingEntity))
|
||||
{
|
||||
renderer.Source.Pause();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (renderer.TrackingCoordinates == null)
|
||||
{
|
||||
renderer.Source.Pause();
|
||||
return;
|
||||
}
|
||||
|
||||
mapPos = renderer.TrackingCoordinates.Value;
|
||||
|
||||
// If it's on a different map then just mute it, not pause.
|
||||
if (mapPos.MapId == MapId.Nullspace || mapPos.MapId != listener.MapId)
|
||||
{
|
||||
renderer.Source.Gain = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
// Was previously muted maybe so try unmuting it?
|
||||
if (renderer.Source.Gain == 0f)
|
||||
{
|
||||
renderer.Source.Gain = Gain;
|
||||
}
|
||||
|
||||
var worldPos = mapPos.Position;
|
||||
var delta = worldPos - listener.Position;
|
||||
var distance = delta.Length();
|
||||
|
||||
// Update position
|
||||
// Out of range so just clip it for us.
|
||||
if (distance > renderer.Source.MaxDistance)
|
||||
{
|
||||
// Still keeps the source playing, just with no volume.
|
||||
renderer.Source.Gain = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
// Same imprecision suppression as audiosystem.
|
||||
if (distance > 0f && distance < 0.01f)
|
||||
{
|
||||
worldPos = listener.Position;
|
||||
delta = Vector2.Zero;
|
||||
distance = 0f;
|
||||
}
|
||||
|
||||
renderer.Source.Position = worldPos;
|
||||
|
||||
// Update velocity (doppler).
|
||||
if (!_entityManager.Deleted(renderer.TrackingEntity))
|
||||
{
|
||||
var velocity = _broadPhaseSystem.GetMapLinearVelocity(renderer.TrackingEntity.Value);
|
||||
renderer.Source.Velocity = velocity;
|
||||
}
|
||||
if (!renderer.Source.Global)
|
||||
UpdateLocalRenderer(renderer, listener);
|
||||
else
|
||||
{
|
||||
renderer.Source.Velocity = Vector2.Zero;
|
||||
}
|
||||
|
||||
// Update occlusion
|
||||
var occlusion = _audioSys.GetOcclusion(listener, delta, distance, renderer.TrackingEntity);
|
||||
renderer.Source.Occlusion = occlusion;
|
||||
UpdateGlobalRenderer(renderer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -409,6 +340,58 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLocalRenderer(IMidiRenderer renderer, MapCoordinates listener)
|
||||
{
|
||||
if (_entityManager.Deleted(renderer.TrackingEntity) || _entityManager.IsPaused(renderer.TrackingEntity))
|
||||
{
|
||||
renderer.Source.Gain = 0f;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MapCoordinates mapCoords = _xformSystem.GetMapCoordinates(renderer.TrackingEntity.Value);
|
||||
renderer.TrackingCoordinates = mapCoords;
|
||||
|
||||
if (mapCoords.MapId == MapId.Nullspace || mapCoords.MapId != listener.MapId)
|
||||
{
|
||||
renderer.Source.Gain = 0f;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 mapPosition = mapCoords.Position;
|
||||
Vector2 listenerDelta = mapPosition - listener.Position;
|
||||
var listenerDeltaLength = listenerDelta.Length();
|
||||
|
||||
if (listenerDeltaLength > renderer.Source.MaxDistance)
|
||||
{
|
||||
renderer.Source.Gain = 0f;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (listenerDeltaLength is > 0f and < 0.01f)
|
||||
{
|
||||
mapPosition = listener.Position;
|
||||
listenerDelta = Vector2.Zero;
|
||||
listenerDeltaLength = 0f;
|
||||
}
|
||||
|
||||
if (_gainDirty || renderer.Source.Gain == 0f)
|
||||
renderer.Source.Gain = Gain;
|
||||
|
||||
renderer.Source.Position = mapPosition;
|
||||
renderer.Source.Velocity = _physics.GetMapLinearVelocity(renderer.TrackingEntity.Value);
|
||||
renderer.Source.Occlusion =
|
||||
_audioSys.GetOcclusion(listener, listenerDelta, listenerDeltaLength, renderer.TrackingEntity);
|
||||
}
|
||||
|
||||
private void UpdateGlobalRenderer(IMidiRenderer renderer)
|
||||
{
|
||||
if (_gainDirty)
|
||||
renderer.Source.Gain = Gain;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main method for the thread rendering the midi audio.
|
||||
/// </summary>
|
||||
@@ -428,7 +411,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (!renderer.Disposed)
|
||||
{
|
||||
if (renderer.Master is { Disposed: true })
|
||||
if (renderer.Master is {Disposed: true})
|
||||
renderer.Master = null;
|
||||
|
||||
renderer.Render();
|
||||
|
||||
@@ -214,6 +214,11 @@ internal sealed partial class MidiRenderer : IMidiRenderer
|
||||
[ViewVariables]
|
||||
public BitArray FilteredChannels { get; } = new(RobustMidiEvent.MaxChannels);
|
||||
|
||||
[ViewVariables]
|
||||
public byte MinVolume { get => _minVolume; set => _minVolume = value; }
|
||||
|
||||
private byte _minVolume;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public byte? VelocityOverride { get; set; } = null;
|
||||
|
||||
@@ -539,14 +544,7 @@ internal sealed partial class MidiRenderer : IMidiRenderer
|
||||
if (velocity <= 0)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
_synth.NoteOn(channel, key, velocity);
|
||||
}
|
||||
catch (FluidSynthInteropException e)
|
||||
{
|
||||
_midiSawmill.Error($"CH:{channel} KEY:{key} VEL:{velocity} {e.ToStringBetter()}");
|
||||
}
|
||||
_synth.TryNoteOn(channel, key, velocity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,19 +572,32 @@ internal sealed partial class MidiRenderer : IMidiRenderer
|
||||
{
|
||||
case RobustMidiCommand.NoteOff:
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = 0;
|
||||
_synth.NoteOff(midiEvent.Channel, midiEvent.Key);
|
||||
break;
|
||||
_synth.TryNoteOff(midiEvent.Channel, midiEvent.Key);
|
||||
|
||||
break;
|
||||
case RobustMidiCommand.NoteOn:
|
||||
// Velocity 0 *can* represent a NoteOff event.
|
||||
var velocity = midiEvent.Velocity;
|
||||
if (velocity == 0)
|
||||
{
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = 0;
|
||||
_synth.TryNoteOn(midiEvent.Channel, midiEvent.Key, velocity);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (FilteredChannels[midiEvent.Channel])
|
||||
break;
|
||||
|
||||
var velocity = VelocityOverride ?? midiEvent.Velocity;
|
||||
if (MinVolume > 0)
|
||||
velocity = (byte)Math.Floor(MathHelper.Lerp(MinVolume, 127, (float)velocity / 127));
|
||||
|
||||
velocity = VelocityOverride ?? velocity;
|
||||
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = velocity;
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, velocity);
|
||||
break;
|
||||
_synth.TryNoteOn(midiEvent.Channel, midiEvent.Key, velocity);
|
||||
|
||||
break;
|
||||
case RobustMidiCommand.AfterTouch:
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = midiEvent.Value;
|
||||
_synth.KeyPressure(midiEvent.Channel, midiEvent.Key, midiEvent.Value);
|
||||
|
||||
@@ -1,7 +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.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -77,7 +76,7 @@ internal sealed class AudioSource : BaseAudioSource
|
||||
else
|
||||
{
|
||||
if (FilterHandle != 0)
|
||||
EFX.DeleteFilter(FilterHandle);
|
||||
ALC.EFX.DeleteFilter(FilterHandle);
|
||||
|
||||
AL.DeleteSource(SourceHandle);
|
||||
Master.RemoveAudioSource(SourceHandle);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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;
|
||||
@@ -82,9 +81,9 @@ public abstract class BaseAudioSource : IAudioSource
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
var state = AL.GetSourceState(SourceHandle);
|
||||
var state = AL.GetSource(SourceHandle, ALGetSourcei.SourceState);
|
||||
Master._checkAlError();
|
||||
return state == ALSourceState.Playing;
|
||||
return state == (int)ALSourceState.Playing;
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -362,11 +361,11 @@ public abstract class BaseAudioSource : IAudioSource
|
||||
|
||||
if (audio is AuxiliaryAudio impAudio)
|
||||
{
|
||||
EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, impAudio.Handle, 0, 0);
|
||||
ALC.EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, impAudio.Handle, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, 0, 0, 0);
|
||||
ALC.EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, 0, 0, 0);
|
||||
}
|
||||
|
||||
Master._checkAlError();
|
||||
@@ -376,12 +375,12 @@ public abstract class BaseAudioSource : IAudioSource
|
||||
{
|
||||
if (FilterHandle == 0)
|
||||
{
|
||||
FilterHandle = EFX.GenFilter();
|
||||
EFX.Filter(FilterHandle, FilterInteger.FilterType, (int) FilterType.Lowpass);
|
||||
FilterHandle = ALC.EFX.GenFilter();
|
||||
ALC.EFX.Filter(FilterHandle, FilterInteger.FilterType, (int) FilterType.Lowpass);
|
||||
}
|
||||
|
||||
EFX.Filter(FilterHandle, FilterFloat.LowpassGain, gain);
|
||||
EFX.Filter(FilterHandle, FilterFloat.LowpassGainHF, cutoff);
|
||||
ALC.EFX.Filter(FilterHandle, FilterFloat.LowpassGain, gain);
|
||||
ALC.EFX.Filter(FilterHandle, FilterFloat.LowpassGainHF, cutoff);
|
||||
AL.Source(SourceHandle, ALSourcei.EfxDirectFilter, FilterHandle);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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;
|
||||
@@ -37,9 +36,9 @@ internal sealed class BufferedAudioSource : BaseAudioSource, IBufferedAudioSourc
|
||||
get
|
||||
{
|
||||
_checkDisposed();
|
||||
var state = AL.GetSourceState(SourceHandle);
|
||||
var state = AL.GetSource(SourceHandle, ALGetSourcei.SourceState);
|
||||
_master._checkAlError();
|
||||
return state == ALSourceState.Playing;
|
||||
return state == (int)ALSourceState.Playing;
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -84,7 +83,7 @@ internal sealed class BufferedAudioSource : BaseAudioSource, IBufferedAudioSourc
|
||||
else
|
||||
{
|
||||
if (FilterHandle != 0)
|
||||
EFX.DeleteFilter(FilterHandle);
|
||||
ALC.EFX.DeleteFilter(FilterHandle);
|
||||
|
||||
AL.DeleteSource(SourceHandle);
|
||||
AL.DeleteBuffers(BufferHandles);
|
||||
|
||||
@@ -144,6 +144,7 @@ namespace Robust.Client
|
||||
deps.Register<IViewVariablesManager, ClientViewVariablesManager>();
|
||||
deps.Register<IClientViewVariablesManager, ClientViewVariablesManager>();
|
||||
deps.Register<IClientViewVariablesManagerInternal, ClientViewVariablesManager>();
|
||||
deps.Register<IViewVariableControlFactory, ViewVariableControlFactory>();
|
||||
deps.Register<IClientConGroupController, ClientConGroupController>();
|
||||
deps.Register<IScriptClient, ScriptClient>();
|
||||
deps.Register<IRobustSerializer, ClientRobustSerializer>();
|
||||
|
||||
@@ -154,6 +154,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
|
||||
_sprite = new TabSpriteView();
|
||||
_tabContainer.AddChild(_sprite);
|
||||
_tabContainer.AddChild(TabCursorShapes());
|
||||
}
|
||||
|
||||
public void OnClosed()
|
||||
@@ -210,6 +211,53 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
return label;
|
||||
}
|
||||
|
||||
private Control TabCursorShapes()
|
||||
{
|
||||
var box = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
};
|
||||
var styleBox = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = Color.Black
|
||||
};
|
||||
foreach (var cursorName in Enum.GetNames<CursorShape>())
|
||||
{
|
||||
// Go over names due to duplicate definitions in the enum.
|
||||
var cursor = Enum.Parse<CursorShape>(cursorName);
|
||||
// Wow was I bad at API design.
|
||||
if (cursor == CursorShape.Custom)
|
||||
continue;
|
||||
|
||||
var panel = new PanelContainer
|
||||
{
|
||||
PanelOverride = styleBox,
|
||||
DefaultCursorShape = cursor,
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
MinHeight = 30,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = cursorName,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Margin = new Thickness(4)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
box.AddChild(panel);
|
||||
}
|
||||
|
||||
return new ScrollContainer
|
||||
{
|
||||
Children = { box },
|
||||
VScrollEnabled = true,
|
||||
HScrollEnabled = false,
|
||||
Name = nameof(Tab.TabCursorShapes),
|
||||
};
|
||||
}
|
||||
|
||||
public void SelectTab(Tab tab)
|
||||
{
|
||||
_tabContainer.CurrentTab = (int)tab;
|
||||
@@ -226,6 +274,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
TextEdit = 6,
|
||||
RichText = 7,
|
||||
SpriteView = 8,
|
||||
TabCursorShapes = 9,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Robust.Client.Console
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
Result = result;
|
||||
var compl = new FormattedMessage();
|
||||
var dim = Color.FromHsl((0f, 0f, 0.8f, 1f));
|
||||
var dim = Color.FromHsl(new Vector4(0f, 0f, 0.8f, 1f));
|
||||
|
||||
// warning: ew ahead
|
||||
string basen = "default";
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
foreach (var ent in _mapSystem.GetAnchoredEntities(gridUid, grid, spot))
|
||||
{
|
||||
if (TryComp<MetaDataComponent>(ent, out var meta))
|
||||
if (TryComp(ent, out MetaDataComponent? meta))
|
||||
{
|
||||
text.AppendLine($"uid: {ent}, {meta.EntityName}");
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ namespace Robust.Client
|
||||
|
||||
_prof.Initialize();
|
||||
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null);
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null, hideUserDataDir: true);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions)
|
||||
|
||||
@@ -30,8 +30,6 @@ using Robust.Shared.ViewVariables;
|
||||
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteSpecifierSerializer;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
using SysVec4 = System.Numerics.Vector4;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed class ScaleVisualsSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ScaleVisualsComponent, AppearanceChangeEvent>(OnChangeData);
|
||||
}
|
||||
|
||||
private void OnChangeData(EntityUid uid, ScaleVisualsComponent component, ref AppearanceChangeEvent ev)
|
||||
{
|
||||
if (!ev.AppearanceData.TryGetValue(ScaleVisuals.Scale, out var scale) ||
|
||||
ev.Sprite == null) return;
|
||||
|
||||
var vecScale = (Vector2)scale;
|
||||
|
||||
// Set it directly because prediction may call this multiple times.
|
||||
ev.Sprite.Scale = vecScale;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public sealed partial class SpriteSystem
|
||||
bounds = bounds.Union(GetLocalBounds(layer));
|
||||
}
|
||||
|
||||
sprite.Comp._bounds = bounds;
|
||||
sprite.Comp._bounds = bounds.Scale(sprite.Comp.Scale);
|
||||
sprite.Comp.BoundsDirty = false;
|
||||
return sprite.Comp._bounds;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
using SysVec4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
@@ -157,7 +155,7 @@ public sealed partial class SpriteSystem
|
||||
// Negative color modulation values are by the default shader to disable light shading.
|
||||
// Specifically we set colour = - 1 - colour
|
||||
// This is good enough to ensure that non-negative values become negative & is trivially invertible.
|
||||
layerColor = new(new SysVec4(-1) - layerColor.RGBA);
|
||||
layerColor = new(new Vector4(-1) - layerColor.RGBA);
|
||||
}
|
||||
|
||||
drawingHandle.DrawTextureRectRegion(texture, quad, layerColor);
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
internal sealed class NetInterpOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
@@ -32,7 +34,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_lookup = lookup;
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_shader = _prototypeManager.Index(UnshadedShader).Instance();
|
||||
_container = _entityManager.System<SharedContainerSystem>();
|
||||
_xform = _entityManager.System<SharedTransformSystem>();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Log;
|
||||
#if WINDOWS
|
||||
using TerraFX.Interop.Windows;
|
||||
using TerraFX.Interop.DirectX;
|
||||
#endif
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -13,6 +17,8 @@ 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))
|
||||
{
|
||||
@@ -55,6 +61,74 @@ 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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
|
||||
@@ -18,7 +18,6 @@ using Robust.Shared.Graphics;
|
||||
using static Robust.Shared.GameObjects.OccluderComponent;
|
||||
using Robust.Shared.Utility;
|
||||
using TextureWrapMode = Robust.Shared.Graphics.TextureWrapMode;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
|
||||
@@ -11,8 +11,6 @@ using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using TKStencilOp = OpenToolkit.Graphics.OpenGL4.StencilOp;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -541,7 +539,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case Matrix3x2 matrix3:
|
||||
program.SetUniform(name, matrix3);
|
||||
break;
|
||||
case Matrix4 matrix4:
|
||||
case Matrix4x4 matrix4:
|
||||
program.SetUniform(name, matrix4);
|
||||
break;
|
||||
case ClydeTexture clydeTexture:
|
||||
|
||||
@@ -10,8 +10,6 @@ using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -528,7 +526,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix4 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix4x4 value)
|
||||
{
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
data.ParametersDirty = true;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
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;
|
||||
@@ -101,6 +102,10 @@ 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;
|
||||
|
||||
@@ -374,6 +379,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (reg.IsDisposed)
|
||||
return;
|
||||
|
||||
_sawmillWin.Debug($"Destroying window {reg.Id}");
|
||||
|
||||
reg.IsDisposed = true;
|
||||
|
||||
_glContext!.WindowDestroyed(reg);
|
||||
|
||||
@@ -128,7 +128,11 @@ 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);
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -398,7 +396,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix4 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix4x4 value)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ using System.Runtime.CompilerServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -277,20 +275,20 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_clyde.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetUniform(string uniformName, in Matrix4 matrix, bool transpose=true)
|
||||
public void SetUniform(string uniformName, in Matrix4x4 matrix, bool transpose=true)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
SetUniformDirect(uniformId, matrix, transpose);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe void SetUniformDirect(int uniformId, in Matrix4 value, bool transpose=true)
|
||||
private unsafe void SetUniformDirect(int uniformId, in Matrix4x4 value, bool transpose=true)
|
||||
{
|
||||
Matrix4 tmpTranspose = value;
|
||||
Matrix4x4 tmpTranspose = value;
|
||||
if (transpose)
|
||||
{
|
||||
// transposition not supported on GLES2, & no access to _hasGLES
|
||||
tmpTranspose.Transpose();
|
||||
tmpTranspose = Matrix4x4.Transpose(value);
|
||||
}
|
||||
GL.UniformMatrix4(uniformId, 1, false, (float*) &tmpTranspose);
|
||||
_clyde.CheckGlError();
|
||||
@@ -551,7 +549,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformMaybe(string uniformName, in Matrix4 value, bool transpose=true)
|
||||
public void SetUniformMaybe(string uniformName, in Matrix4x4 value, bool transpose=true)
|
||||
{
|
||||
if (TryGetUniform(uniformName, out var slot))
|
||||
{
|
||||
|
||||
@@ -128,6 +128,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
AddStandardCursor(StandardCursorShape.Hand, CursorShape.Hand);
|
||||
AddStandardCursor(StandardCursorShape.HResize, CursorShape.HResize);
|
||||
AddStandardCursor(StandardCursorShape.VResize, CursorShape.VResize);
|
||||
AddStandardCursor(StandardCursorShape.Progress, CursorShape.Arrow);
|
||||
AddStandardCursor(StandardCursorShape.NWSEResize, CursorShape.Crosshair);
|
||||
AddStandardCursor(StandardCursorShape.NESWResize, CursorShape.Crosshair);
|
||||
AddStandardCursor(StandardCursorShape.Move, CursorShape.Crosshair);
|
||||
AddStandardCursor(StandardCursorShape.NotAllowed, CursorShape.Arrow);
|
||||
AddStandardCursor(StandardCursorShape.NWResize, CursorShape.Crosshair);
|
||||
AddStandardCursor(StandardCursorShape.NResize, CursorShape.VResize);
|
||||
AddStandardCursor(StandardCursorShape.NEResize, CursorShape.Crosshair);
|
||||
AddStandardCursor(StandardCursorShape.EResize, CursorShape.HResize);
|
||||
AddStandardCursor(StandardCursorShape.SEResize, CursorShape.Crosshair);
|
||||
AddStandardCursor(StandardCursorShape.SResize, CursorShape.VResize);
|
||||
AddStandardCursor(StandardCursorShape.SWResize, CursorShape.Crosshair);
|
||||
AddStandardCursor(StandardCursorShape.WResize, CursorShape.HResize);
|
||||
}
|
||||
|
||||
private sealed class CursorImpl : ICursor
|
||||
|
||||
@@ -49,6 +49,7 @@ public static partial class SDL
|
||||
|
||||
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 const string SDL_PROP_FILE_DIALOG_WINDOW_POINTER = "SDL.filedialog.window";
|
||||
|
||||
public static int SDL_VERSIONNUM_MAJOR(int version) => version / 1000000;
|
||||
public static int SDL_VERSIONNUM_MINOR(int version) => version / 1000 % 1000;
|
||||
|
||||
@@ -76,7 +76,7 @@ public static unsafe partial class SDL
|
||||
}
|
||||
}
|
||||
|
||||
private const string nativeLibName = "SDL3";
|
||||
internal const string nativeLibName = "SDL3";
|
||||
|
||||
// /usr/local/include/SDL3/SDL_stdinc.h
|
||||
|
||||
|
||||
@@ -94,6 +94,19 @@ internal partial class Clyde
|
||||
Add(StandardCursorShape.Hand, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_POINTER);
|
||||
Add(StandardCursorShape.HResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_EW_RESIZE);
|
||||
Add(StandardCursorShape.VResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NS_RESIZE);
|
||||
Add(StandardCursorShape.Progress, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_PROGRESS);
|
||||
Add(StandardCursorShape.NWSEResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NWSE_RESIZE);
|
||||
Add(StandardCursorShape.NESWResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NESW_RESIZE);
|
||||
Add(StandardCursorShape.Move, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_MOVE);
|
||||
Add(StandardCursorShape.NotAllowed, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NOT_ALLOWED);
|
||||
Add(StandardCursorShape.NWResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NW_RESIZE);
|
||||
Add(StandardCursorShape.NResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_N_RESIZE);
|
||||
Add(StandardCursorShape.NEResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NE_RESIZE);
|
||||
Add(StandardCursorShape.EResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_E_RESIZE);
|
||||
Add(StandardCursorShape.SEResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SE_RESIZE);
|
||||
Add(StandardCursorShape.SResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_S_RESIZE);
|
||||
Add(StandardCursorShape.SWResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SW_RESIZE);
|
||||
Add(StandardCursorShape.WResize, SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_W_RESIZE);
|
||||
|
||||
void Add(StandardCursorShape shape, SDL.SDL_SystemCursor sysCursor)
|
||||
{
|
||||
|
||||
@@ -81,6 +81,11 @@ 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;
|
||||
@@ -255,5 +260,15 @@ 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,6 +46,10 @@ 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,5 +278,9 @@ internal partial class Clyde
|
||||
private sealed class EventKeyMapChanged : EventBase;
|
||||
|
||||
private sealed class EventQuit : EventBase;
|
||||
|
||||
#if MACOS
|
||||
private sealed class EventWindowDestroyed : EventBase;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ 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;
|
||||
|
||||
@@ -142,9 +144,12 @@ internal partial class Clyde
|
||||
});
|
||||
}
|
||||
|
||||
private static void WinThreadWinDestroy(CmdWinDestroy cmd)
|
||||
private void WinThreadWinDestroy(CmdWinDestroy cmd)
|
||||
{
|
||||
SDL.SDL_DestroyWindow(cmd.Window);
|
||||
#if MACOS
|
||||
SendEvent(new EventWindowDestroyed());
|
||||
#endif
|
||||
}
|
||||
|
||||
private (nint window, nint context) CreateSdl3WindowForRenderer(
|
||||
@@ -461,6 +466,7 @@ 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.
|
||||
@@ -473,7 +479,7 @@ internal partial class Clyde
|
||||
var dwmFlush = false;
|
||||
var swapInterval = 0;
|
||||
|
||||
if (OperatingSystem.IsWindows() && !reg.Fullscreen && reg.SwapInterval > 0)
|
||||
if (!reg.Fullscreen && reg.SwapInterval > 0)
|
||||
{
|
||||
BOOL compositing;
|
||||
// 6.2 is Windows 8
|
||||
@@ -492,9 +498,12 @@ 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;
|
||||
@@ -505,6 +514,7 @@ internal partial class Clyde
|
||||
|
||||
SDL.SDL_GL_SetSwapInterval(swapInterval);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public uint? WindowGetX11Id(WindowReg window)
|
||||
@@ -547,17 +557,18 @@ 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 = rect.Left,
|
||||
y = rect.Top,
|
||||
w = rect.Width,
|
||||
h = rect.Height
|
||||
x = (int)(rect.Left / ratio.X),
|
||||
y = (int)(rect.Top / ratio.Y),
|
||||
w = (int)(rect.Width / ratio.X),
|
||||
h = (int)(rect.Height / ratio.Y)
|
||||
},
|
||||
Cursor = cursor
|
||||
Cursor = (int)(cursor / ratio.X)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ 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)
|
||||
{
|
||||
|
||||
@@ -4,8 +4,6 @@ using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
@@ -169,7 +167,7 @@ namespace Robust.Client.Graphics
|
||||
SetParameterImpl(name, value);
|
||||
}
|
||||
|
||||
public void SetParameter(string name, in Matrix4 value)
|
||||
public void SetParameter(string name, in Matrix4x4 value)
|
||||
{
|
||||
EnsureAlive();
|
||||
EnsureMutable();
|
||||
@@ -236,7 +234,7 @@ namespace Robust.Client.Graphics
|
||||
private protected abstract void SetParameterImpl(string name, bool value);
|
||||
private protected abstract void SetParameterImpl(string name, bool[] value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix3x2 value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix4 value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix4x4 value);
|
||||
private protected abstract void SetParameterImpl(string name, Texture value);
|
||||
private protected abstract void SetStencilImpl(StencilParameters value);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
@@ -222,7 +220,7 @@ namespace Robust.Client.Graphics
|
||||
case Matrix3x2 i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
case Matrix4 i:
|
||||
case Matrix4x4 i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@ namespace Robust.Client.Graphics
|
||||
/// </summary>
|
||||
IBeam,
|
||||
|
||||
/// <summary>
|
||||
/// Alias for <see cref="IBeam"/>.
|
||||
/// </summary>
|
||||
Text = IBeam,
|
||||
|
||||
/// <summary>
|
||||
/// The crosshair shape. Used when dragging and dropping.
|
||||
/// </summary>
|
||||
@@ -25,16 +30,135 @@ namespace Robust.Client.Graphics
|
||||
/// </summary>
|
||||
Hand,
|
||||
|
||||
/// <summary>
|
||||
/// Alias for <see cref="Hand"/>
|
||||
/// </summary>
|
||||
Pointer = Hand,
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal resize shape. Used when mousing over something that can be horizontally resized.
|
||||
/// </summary>
|
||||
HResize,
|
||||
|
||||
/// <summary>
|
||||
/// Alias for <see cref="EWResize"/>
|
||||
/// </summary>
|
||||
EWResize = HResize,
|
||||
|
||||
/// <summary>
|
||||
/// The vertical resize shape. Used when mousing over something that can be vertically resized.
|
||||
/// </summary>
|
||||
VResize,
|
||||
|
||||
/// <summary>
|
||||
/// Alias for <see cref="VResize"/>.
|
||||
/// </summary>
|
||||
NSResize = VResize,
|
||||
|
||||
/// <summary>
|
||||
/// Program is busy doing something.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
Progress,
|
||||
|
||||
/// <summary>
|
||||
/// Diagonal resize shape for northwest-southeast resizing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
NWSEResize,
|
||||
|
||||
/// <summary>
|
||||
/// Diagonal resize shape for northeast-southwest resizing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
NESWResize,
|
||||
|
||||
/// <summary>
|
||||
/// 4-way arrow move icon.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
Move,
|
||||
|
||||
/// <summary>
|
||||
/// An action is not allowed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
NotAllowed,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the northwest.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
NWResize,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the north.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
NResize,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the northeast.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
NEResize,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the east.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
EResize,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the southeast.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
SEResize,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the south.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
SResize,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the southwest.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
SWResize,
|
||||
|
||||
/// <summary>
|
||||
/// One-directional resize to the west.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cursor is not always available and may be substituted.
|
||||
/// </remarks>
|
||||
WResize,
|
||||
|
||||
/// <summary>
|
||||
/// Not a real value
|
||||
/// </summary>
|
||||
|
||||
@@ -197,6 +197,13 @@ namespace Robust.Client.Input
|
||||
locId += "-linux";
|
||||
}
|
||||
|
||||
#if MACOS
|
||||
if (key == Key.Alt)
|
||||
{
|
||||
locId += "-mac";
|
||||
}
|
||||
#endif
|
||||
|
||||
if (loc.TryGetString(locId, out var name))
|
||||
return name;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -18,6 +21,10 @@ namespace Robust.Client.Placement
|
||||
PlacementMode? CurrentMode { get; set; }
|
||||
PlacementInformation? CurrentPermission { get; set; }
|
||||
|
||||
IEntityManager EntityManager { get; }
|
||||
IEyeManager EyeManager { get; }
|
||||
IMapManager MapManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The direction to spawn the entity in (presently exposed for EntitySpawnWindow UI)
|
||||
/// </summary>
|
||||
@@ -49,5 +56,15 @@ namespace Robust.Client.Placement
|
||||
void ToggleEraserHijacked(PlacementHijack hijack);
|
||||
|
||||
void FrameUpdate(FrameEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the placement mode option to just use the default for the selected entity.
|
||||
/// </summary>
|
||||
const string DefaultModeName = "Default";
|
||||
|
||||
/// <summary>
|
||||
/// An array of the names of all available placement modes.
|
||||
/// </summary>
|
||||
string[] AllModeNames { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ using Robust.Shared.Utility;
|
||||
using Robust.Shared.Log;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Linq;
|
||||
|
||||
namespace Robust.Client.Placement
|
||||
{
|
||||
@@ -32,17 +33,23 @@ namespace Robust.Client.Placement
|
||||
[Dependency] internal readonly IPlayerManager PlayerManager = default!;
|
||||
[Dependency] internal readonly IResourceCache ResourceCache = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] internal readonly IMapManager MapManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IGameTiming _time = default!;
|
||||
[Dependency] internal readonly IEyeManager EyeManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] internal readonly IInputManager InputManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] internal readonly IEntityManager EntityManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] internal readonly IClyde Clyde = default!;
|
||||
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
public IEntityManager EntityManager => _entityManager;
|
||||
public IEyeManager EyeManager => _eyeManager;
|
||||
public IMapManager MapManager => _mapManager;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private SharedMapSystem Maps => EntityManager.System<SharedMapSystem>();
|
||||
@@ -198,6 +205,15 @@ namespace Robust.Client.Placement
|
||||
}
|
||||
}
|
||||
|
||||
private string[]? _allModeNames;
|
||||
public string[] AllModeNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return _allModeNames ??= [IPlacementManager.DefaultModeName, .. _modeDictionary.Keys.Order()];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler? DirectionChanged;
|
||||
|
||||
@@ -209,7 +225,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_drawingShader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_drawingShader = _prototypeManager.Index(UnshadedShader).Instance();
|
||||
_sawmill = _logManager.GetSawmill("placement");
|
||||
|
||||
_networkManager.RegisterNetMessage<MsgPlacement>(HandlePlacementMessage);
|
||||
|
||||
@@ -138,9 +138,16 @@ namespace Robust.Client.ResourceManagement
|
||||
var sw = Stopwatch.StartNew();
|
||||
var resList = GetTypeData<RSIResource>().Resources;
|
||||
|
||||
var rsiList = _manager.ContentFindFiles("/Textures/")
|
||||
var foundRsiList = _manager.ContentFindFiles("/Textures/")
|
||||
.Where(p => p.ToString().EndsWith(".rsi/meta.json"))
|
||||
.Select(c => c.Directory)
|
||||
.Select(c => c.Directory);
|
||||
|
||||
var foundRsicList = _manager.ContentFindFiles("/Textures/")
|
||||
.Where(p => p.Extension == "rsic")
|
||||
.Select(c => c.WithExtension("rsi"));
|
||||
|
||||
var rsiList = foundRsiList
|
||||
.Concat(foundRsicList)
|
||||
.Where(p => !resList.ContainsKey(p))
|
||||
.Select(p => new RSIResource.LoadStepData {Path = p})
|
||||
.ToArray();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
@@ -58,12 +57,88 @@ namespace Robust.Client.ResourceManagement
|
||||
internal static void LoadPreTexture(IResourceManager manager, LoadStepData data)
|
||||
{
|
||||
var manifestPath = data.Path / "meta.json";
|
||||
if (manager.TryContentFileRead(manifestPath, out var manifestFile))
|
||||
{
|
||||
LoadPreTextureFolder(manager, data, manifestFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
var rsicPath = data.Path.WithExtension("rsic");
|
||||
if (manager.TryContentFileRead(rsicPath, out var rsicFile))
|
||||
{
|
||||
LoadPreTextureRsic(data, rsicFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException($"Unable to find .rsi file: {data.Path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadPreTextureFolder(IResourceManager manager, LoadStepData data, Stream manifestFile)
|
||||
{
|
||||
RsiLoading.RsiMetadata metadata;
|
||||
using (var manifestFile = manager.ContentFileRead(manifestPath))
|
||||
using (manifestFile)
|
||||
{
|
||||
metadata = RsiLoading.LoadRsiMetadata(manifestFile);
|
||||
}
|
||||
|
||||
data.FrameCounts = RsiLoading.CalculateFrameCounts(metadata);
|
||||
data.Images = RsiLoading.LoadImages(
|
||||
metadata,
|
||||
SixLabors.ImageSharp.Configuration.Default,
|
||||
name =>
|
||||
{
|
||||
var texPath = data.Path / (name + ".png");
|
||||
return manager.ContentFileRead(texPath);
|
||||
});
|
||||
|
||||
var sheet = RsiLoading.GenerateAtlas(
|
||||
metadata,
|
||||
data.FrameCounts,
|
||||
data.Images,
|
||||
SixLabors.ImageSharp.Configuration.Default,
|
||||
out var dimensionX);
|
||||
|
||||
LoadPreTextureCommon(metadata, data);
|
||||
|
||||
data.AtlasSheet = sheet;
|
||||
data.DimX = dimensionX;
|
||||
data.LoadParameters = metadata.LoadParameters;
|
||||
data.MetaAtlas = metadata.MetaAtlas;
|
||||
}
|
||||
|
||||
private static void LoadPreTextureRsic(LoadStepData data, Stream rsicFile)
|
||||
{
|
||||
Image<Rgba32> image;
|
||||
using (rsicFile)
|
||||
{
|
||||
image = Image.Load<Rgba32>(rsicFile);
|
||||
}
|
||||
|
||||
data.AtlasSheet = image;
|
||||
|
||||
var textDataList = image.Metadata.GetPngMetadata().TextData;
|
||||
if (!textDataList.TryFirstOrNull(
|
||||
static data => data.Keyword == RsiLoading.RsicPngField,
|
||||
out var pngMetadata))
|
||||
throw new InvalidDataException(".rsic does not have metadata field");
|
||||
|
||||
var metadata = RsiLoading.LoadRsiMetadata(pngMetadata.Value.Value);
|
||||
|
||||
data.FrameCounts = RsiLoading.CalculateFrameCounts(metadata);
|
||||
|
||||
LoadPreTextureCommon(metadata, data);
|
||||
|
||||
data.DimX = image.Width / metadata.Size.X;
|
||||
data.LoadParameters = metadata.LoadParameters;
|
||||
data.MetaAtlas = metadata.MetaAtlas;
|
||||
}
|
||||
|
||||
private static void LoadPreTextureCommon(
|
||||
RsiLoading.RsiMetadata metadata,
|
||||
LoadStepData data)
|
||||
{
|
||||
var stateCount = metadata.States.Length;
|
||||
var toAtlas = new StateReg[stateCount];
|
||||
|
||||
@@ -72,40 +147,12 @@ namespace Robust.Client.ResourceManagement
|
||||
|
||||
var callbackOffsets = new Dictionary<RSI.StateId, Vector2i[][]>(stateCount);
|
||||
|
||||
// Check for duplicate states
|
||||
for (var i = 0; i < metadata.States.Length; i++)
|
||||
{
|
||||
var stateId = metadata.States[i].StateId;
|
||||
|
||||
for (int j = i + 1; j < metadata.States.Length; j++)
|
||||
{
|
||||
if (stateId == metadata.States[j].StateId)
|
||||
throw new RSILoadException($"RSI '{data.Path}' has a duplicate stateId '{stateId}'.");
|
||||
}
|
||||
}
|
||||
|
||||
// Do every state.
|
||||
for (var index = 0; index < metadata.States.Length; index++)
|
||||
{
|
||||
ref var reg = ref toAtlas[index];
|
||||
|
||||
var stateObject = metadata.States[index];
|
||||
// Load image from disk.
|
||||
var texPath = data.Path / (stateObject.StateId + ".png");
|
||||
using (var stream = manager.ContentFileRead(texPath))
|
||||
{
|
||||
reg.Src = Image.Load<Rgba32>(stream);
|
||||
}
|
||||
|
||||
if (reg.Src.Width % frameSize.X != 0 || reg.Src.Height % frameSize.Y != 0)
|
||||
{
|
||||
var regDims = $"{reg.Src.Width}x{reg.Src.Height}";
|
||||
var iconDims = $"{frameSize.X}x{frameSize.Y}";
|
||||
throw new RSILoadException($"State '{stateObject.StateId}' image size ({regDims}) is not a multiple of the icon size ({iconDims}).");
|
||||
}
|
||||
|
||||
// Load all frames into a list so we can operate on it more sanely.
|
||||
reg.TotalFrameCount = stateObject.Delays.Sum(delayList => delayList.Length);
|
||||
|
||||
var (foldedDelays, foldedIndices) = FoldDelays(stateObject.Delays);
|
||||
|
||||
@@ -130,60 +177,23 @@ namespace Robust.Client.ResourceManagement
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
|
||||
var state = new RSI.State(frameSize, rsi, stateObject.StateId, dirType, foldedDelays,
|
||||
var state = new RSI.State(
|
||||
frameSize,
|
||||
rsi,
|
||||
stateObject.StateId,
|
||||
dirType,
|
||||
foldedDelays,
|
||||
textures);
|
||||
|
||||
rsi.AddState(state);
|
||||
|
||||
callbackOffsets[stateObject.StateId] = callbackOffset;
|
||||
}
|
||||
|
||||
// Poorly hacked in texture atlas support here.
|
||||
var totalFrameCount = toAtlas.Sum(p => p.TotalFrameCount);
|
||||
|
||||
// Generate atlas.
|
||||
var dimensionX = (int) MathF.Ceiling(MathF.Sqrt(totalFrameCount));
|
||||
var dimensionY = (int) MathF.Ceiling((float) totalFrameCount / dimensionX);
|
||||
|
||||
var sheet = new Image<Rgba32>(dimensionX * frameSize.X, dimensionY * frameSize.Y);
|
||||
|
||||
var sheetIndex = 0;
|
||||
for (var index = 0; index < toAtlas.Length; index++)
|
||||
{
|
||||
ref var reg = ref toAtlas[index];
|
||||
// Blit all the frames over.
|
||||
for (var i = 0; i < reg.TotalFrameCount; i++)
|
||||
{
|
||||
var srcWidth = (reg.Src.Width / frameSize.X);
|
||||
var srcColumn = i % srcWidth;
|
||||
var srcRow = i / srcWidth;
|
||||
var srcPos = (srcColumn * frameSize.X, srcRow * frameSize.Y);
|
||||
|
||||
var sheetColumn = (sheetIndex + i) % dimensionX;
|
||||
var sheetRow = (sheetIndex + i) / dimensionX;
|
||||
var sheetPos = (sheetColumn * frameSize.X, sheetRow * frameSize.Y);
|
||||
|
||||
var srcBox = UIBox2i.FromDimensions(srcPos, frameSize);
|
||||
|
||||
reg.Src.Blit(srcBox, sheet, sheetPos);
|
||||
}
|
||||
|
||||
sheetIndex += reg.TotalFrameCount;
|
||||
}
|
||||
|
||||
for (var i = 0; i < toAtlas.Length; i++)
|
||||
{
|
||||
ref var reg = ref toAtlas[i];
|
||||
reg.Src.Dispose();
|
||||
}
|
||||
|
||||
data.Rsi = rsi;
|
||||
data.AtlasSheet = sheet;
|
||||
data.CallbackOffsets = callbackOffsets;
|
||||
data.AtlasList = toAtlas;
|
||||
data.FrameSize = frameSize;
|
||||
data.DimX = dimensionX;
|
||||
data.CallbackOffsets = callbackOffsets;
|
||||
data.LoadParameters = metadata.LoadParameters;
|
||||
data.MetaAtlas = metadata.MetaAtlas;
|
||||
}
|
||||
|
||||
internal static void LoadPostTexture(LoadStepData data)
|
||||
@@ -216,7 +226,7 @@ namespace Robust.Client.ResourceManagement
|
||||
}
|
||||
}
|
||||
|
||||
sheetOffset += reg.TotalFrameCount;
|
||||
sheetOffset += data.FrameCounts[toAtlasIndex];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,6 +391,8 @@ namespace Robust.Client.ResourceManagement
|
||||
public Image<Rgba32> AtlasSheet = default!;
|
||||
public int DimX;
|
||||
public StateReg[] AtlasList = default!;
|
||||
public int[] FrameCounts = default!;
|
||||
public Image<Rgba32>[] Images = default!;
|
||||
public Vector2i FrameSize;
|
||||
public Dictionary<RSI.StateId, Vector2i[][]> CallbackOffsets = default!;
|
||||
public Texture AtlasTexture = default!;
|
||||
@@ -392,11 +404,9 @@ namespace Robust.Client.ResourceManagement
|
||||
|
||||
internal struct StateReg
|
||||
{
|
||||
public Image<Rgba32> Src;
|
||||
public Texture[][] Output;
|
||||
public int[][] Indices;
|
||||
public Vector2i[][] Offsets;
|
||||
public int TotalFrameCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace Robust.Client.ResourceManagement
|
||||
|
||||
public override void Load(IDependencyCollection dependencies, ResPath path)
|
||||
{
|
||||
if (path.Directory.Filename.EndsWith(".rsi"))
|
||||
if (IsInRsi(path))
|
||||
{
|
||||
Logger.WarningS(
|
||||
"res",
|
||||
var sawmill = dependencies.Resolve<ILogManager>().GetSawmill("res");
|
||||
sawmill.Warning(
|
||||
"Loading raw texture inside RSI: {Path}. Refer to the RSI state instead of the raw PNG.",
|
||||
path);
|
||||
}
|
||||
@@ -38,6 +38,15 @@ namespace Robust.Client.ResourceManagement
|
||||
LoadFinish(dependencies.Resolve<IResourceCache>(), data);
|
||||
}
|
||||
|
||||
private static bool IsInRsi(ResPath path)
|
||||
{
|
||||
var dir = path.Directory;
|
||||
if (dir == ResPath.Root)
|
||||
return false;
|
||||
|
||||
return dir.Filename.EndsWith(".rsi");
|
||||
}
|
||||
|
||||
internal static void LoadPreTextureData(IResourceManager cache, LoadStepData data)
|
||||
{
|
||||
using (var stream = cache.ContentFileRead(data.Path))
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.OpenAL" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.Audio.OpenAL" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" PrivateAssets="compile" />
|
||||
<PackageReference Include="Robust.Natives" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" PrivateAssets="compile" />
|
||||
@@ -63,6 +63,7 @@
|
||||
<RobustLinkAssemblies Include="TerraFX.Interop.Windows" />
|
||||
<RobustLinkAssemblies Include="TerraFX.Interop.Xlib" />
|
||||
<RobustLinkAssemblies Include="OpenToolkit.Graphics" />
|
||||
<RobustLinkAssemblies Include="SpaceWizards.SharpFont" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
@@ -11,14 +11,124 @@ namespace Robust.Client.UserInterface
|
||||
/// <summary>
|
||||
/// Default common cursor shapes available in the UI.
|
||||
/// </summary>
|
||||
/// <seealso cref="StandardCursorShape"/>
|
||||
public enum CursorShape: byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.Arrow"/>
|
||||
/// </summary>
|
||||
Arrow,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.IBeam"/>
|
||||
/// </summary>
|
||||
IBeam,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.Text"/>
|
||||
/// </summary>
|
||||
Text = IBeam,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.Crosshair"/>
|
||||
/// </summary>
|
||||
Crosshair,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.Hand"/>
|
||||
/// </summary>
|
||||
Hand,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.Pointer"/>
|
||||
/// </summary>
|
||||
Pointer = Hand,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.HResize"/>
|
||||
/// </summary>
|
||||
HResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.EWResize"/>
|
||||
/// </summary>
|
||||
EWResize = HResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.VResize"/>
|
||||
/// </summary>
|
||||
VResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.NSResize"/>
|
||||
/// </summary>
|
||||
NSResize = VResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.Progress"/>
|
||||
/// </summary>
|
||||
Progress,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.NWSEResize"/>
|
||||
/// </summary>
|
||||
NWSEResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.NESWResize"/>
|
||||
/// </summary>
|
||||
NESWResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.Move"/>
|
||||
/// </summary>
|
||||
Move,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.NotAllowed"/>
|
||||
/// </summary>
|
||||
NotAllowed,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.NWResize"/>
|
||||
/// </summary>
|
||||
NWResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.NResize"/>
|
||||
/// </summary>
|
||||
NResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.NEResize"/>
|
||||
/// </summary>
|
||||
NEResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.EResize"/>
|
||||
/// </summary>
|
||||
EResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.SEResize"/>
|
||||
/// </summary>
|
||||
SEResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.SResize"/>
|
||||
/// </summary>
|
||||
SResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.SWResize"/>
|
||||
/// </summary>
|
||||
SWResize,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to <see cref="StandardCursorShape.WResize"/>
|
||||
/// </summary>
|
||||
WResize,
|
||||
|
||||
/// <summary>
|
||||
/// Special cursor shape indicating that <see cref="CustomCursorShape"/> is set and being used.
|
||||
/// </summary>
|
||||
|
||||
@@ -151,7 +151,7 @@ public sealed class EntitySpawningUIController : UIController
|
||||
{
|
||||
var newObjInfo = new PlacementInformation
|
||||
{
|
||||
PlacementOption = EntitySpawnWindow.InitOpts[args.Id],
|
||||
PlacementOption = _placement.AllModeNames[args.Id],
|
||||
EntityType = _placement.CurrentPermission!.EntityType,
|
||||
Range = 2,
|
||||
IsTile = _placement.CurrentPermission.IsTile
|
||||
@@ -364,10 +364,11 @@ public sealed class EntitySpawningUIController : UIController
|
||||
_window.SelectedButton = null;
|
||||
_window.SelectedPrototype = null;
|
||||
|
||||
var overrideMode = EntitySpawnWindow.InitOpts[_window.OverrideMenu.SelectedId];
|
||||
|
||||
var overrideMode = _placement.AllModeNames[_window.OverrideMenu.SelectedId];
|
||||
var newObjInfo = new PlacementInformation
|
||||
{
|
||||
PlacementOption = overrideMode != "Default" ? overrideMode : item.Prototype.PlacementMode,
|
||||
PlacementOption = overrideMode != IPlacementManager.DefaultModeName ? overrideMode : item.Prototype.PlacementMode,
|
||||
EntityType = item.PrototypeID,
|
||||
Range = 2,
|
||||
IsTile = false
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.ColorNaming;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -8,22 +11,17 @@ namespace Robust.Client.UserInterface.Controls;
|
||||
// condensed version of the original ColorSlider set
|
||||
public sealed class ColorSelectorSliders : Control
|
||||
{
|
||||
[Dependency] private readonly ILocalizationManager _localization = default!;
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get => _currentColor;
|
||||
set
|
||||
{
|
||||
_currentColor = value;
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
_colorData = new Vector4(_currentColor.R, _currentColor.G, _currentColor.B, _currentColor.A);
|
||||
break;
|
||||
case ColorSelectorType.Hsv:
|
||||
_colorData = Color.ToHsv(value);
|
||||
break;
|
||||
}
|
||||
Update();
|
||||
_colorData = GetStrategy().ToColorData(value);
|
||||
|
||||
UpdateAllSliders();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,19 +30,12 @@ public sealed class ColorSelectorSliders : Control
|
||||
get => _currentType;
|
||||
set
|
||||
{
|
||||
switch ((_currentType, value))
|
||||
{
|
||||
case (ColorSelectorType.Rgb, ColorSelectorType.Hsv):
|
||||
_colorData = Color.ToHsv(Color);
|
||||
break;
|
||||
case (ColorSelectorType.Hsv, ColorSelectorType.Rgb):
|
||||
_colorData = new Vector4(_currentColor.R, _currentColor.G, _currentColor.B, _currentColor.A);
|
||||
break;
|
||||
}
|
||||
_currentType = value;
|
||||
_typeSelector.Select(_types.IndexOf(value));
|
||||
_colorData = GetStrategy().ToColorData(_currentColor);
|
||||
|
||||
UpdateType();
|
||||
Update();
|
||||
UpdateAllSliders();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +52,11 @@ public sealed class ColorSelectorSliders : Control
|
||||
|
||||
public Action<Color>? OnColorChanged;
|
||||
|
||||
private bool _updating = false;
|
||||
private readonly static HsvSliderStrategy _hsvStrategy = new();
|
||||
private readonly static RgbSliderStrategy _rgbStrategy = new();
|
||||
|
||||
private const float AlphaDivisor = 100.0f;
|
||||
|
||||
private Color _currentColor = Color.White;
|
||||
private Vector4 _colorData;
|
||||
private ColorSelectorType _currentType = ColorSelectorType.Rgb;
|
||||
@@ -83,6 +78,7 @@ public sealed class ColorSelectorSliders : Control
|
||||
private Label _middleSliderLabel = new();
|
||||
private Label _bottomSliderLabel = new();
|
||||
private Label _alphaSliderLabel = new();
|
||||
private Label _colorDescriptionLabel = new();
|
||||
|
||||
private OptionButton _typeSelector;
|
||||
private List<ColorSelectorType> _types = new();
|
||||
@@ -93,6 +89,8 @@ public sealed class ColorSelectorSliders : Control
|
||||
|
||||
public ColorSelectorSliders()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_topColorSlider = new ColorableSlider
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
@@ -124,10 +122,10 @@ public sealed class ColorSelectorSliders : Control
|
||||
MaxValue = 1.0f,
|
||||
};
|
||||
|
||||
_topColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_middleColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_bottomColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_alphaSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_topColorSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Top); };
|
||||
_middleColorSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Middle); };
|
||||
_bottomColorSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Bottom); };
|
||||
_alphaSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Alpha); };
|
||||
|
||||
_topInputBox = new SpinBox
|
||||
{
|
||||
@@ -153,25 +151,10 @@ public sealed class ColorSelectorSliders : Control
|
||||
};
|
||||
_alphaInputBox.InitDefaultButtons();
|
||||
|
||||
_topInputBox.ValueChanged += value =>
|
||||
{
|
||||
_topColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Top);
|
||||
};
|
||||
|
||||
_middleInputBox.ValueChanged += value =>
|
||||
{
|
||||
_middleColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Middle);
|
||||
};
|
||||
|
||||
_bottomInputBox.ValueChanged += value =>
|
||||
{
|
||||
_bottomColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Bottom);
|
||||
};
|
||||
|
||||
_alphaInputBox.ValueChanged += value =>
|
||||
{
|
||||
_alphaSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Alpha);
|
||||
};
|
||||
_topInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Top); };
|
||||
_middleInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Middle); };
|
||||
_bottomInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Bottom); };
|
||||
_alphaInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Alpha); };
|
||||
|
||||
_alphaSliderLabel.Text = Loc.GetString("color-selector-sliders-alpha");
|
||||
|
||||
@@ -188,6 +171,8 @@ public sealed class ColorSelectorSliders : Control
|
||||
_typeSelector.Select(args.Id);
|
||||
};
|
||||
|
||||
_colorDescriptionLabel.Text = ColorNaming.Describe(_currentColor, _localization);
|
||||
|
||||
// TODO: Maybe some engine widgets could be laid out in XAML?
|
||||
|
||||
var rootBox = new BoxContainer
|
||||
@@ -200,6 +185,7 @@ public sealed class ColorSelectorSliders : Control
|
||||
rootBox.AddChild(headerBox);
|
||||
|
||||
headerBox.AddChild(_typeSelector);
|
||||
headerBox.AddChild(_colorDescriptionLabel);
|
||||
|
||||
var bodyBox = new BoxContainer()
|
||||
{
|
||||
@@ -241,165 +227,114 @@ public sealed class ColorSelectorSliders : Control
|
||||
Color = _currentColor;
|
||||
}
|
||||
|
||||
private void UpdateType()
|
||||
private ColorSliderStrategy GetStrategy()
|
||||
{
|
||||
(string topLabel, string middleLabel, string bottomLabel) labels = GetSliderLabels();
|
||||
|
||||
_topSliderLabel.Text = labels.topLabel;
|
||||
_middleSliderLabel.Text = labels.middleLabel;
|
||||
_bottomSliderLabel.Text = labels.bottomLabel;
|
||||
|
||||
bool hsv = SelectorType == ColorSelectorType.Hsv;
|
||||
_topStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Hue : ColorSelectorStyleBox.ColorSliderPreset.Red);
|
||||
_middleStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Saturation : ColorSelectorStyleBox.ColorSliderPreset.Green);
|
||||
_bottomStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Value : ColorSelectorStyleBox.ColorSliderPreset.Blue);
|
||||
return SelectorType switch
|
||||
{
|
||||
ColorSelectorType.Rgb => _rgbStrategy,
|
||||
ColorSelectorType.Hsv => _hsvStrategy,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
private void Update()
|
||||
private (Slider slider, SpinBox inputBox) GetSliderByOrder(ColorSliderOrder order)
|
||||
{
|
||||
// This code is a mess of UI events causing stack overflows. Also, updating one slider triggers all sliders to
|
||||
// update, which due to rounding errors causes them to actually change values, specifically for HSV sliders.
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
_updating = true;
|
||||
_topStyle.SetBaseColor(_colorData);
|
||||
_middleStyle.SetBaseColor(_colorData);
|
||||
_bottomStyle.SetBaseColor(_colorData);
|
||||
|
||||
switch (SelectorType)
|
||||
return order switch
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
_topColorSlider.Value = _colorData.X;
|
||||
_middleColorSlider.Value = _colorData.Y;
|
||||
_bottomColorSlider.Value = _colorData.Z;
|
||||
|
||||
_topInputBox.Value = (int)(_colorData.X * 255.0f);
|
||||
_middleInputBox.Value = (int)(_colorData.Y * 255.0f);
|
||||
_bottomInputBox.Value = (int)(_colorData.Z * 255.0f);
|
||||
|
||||
break;
|
||||
case ColorSelectorType.Hsv:
|
||||
// dumb workaround because the formula for
|
||||
// HSV calculation results in a negative
|
||||
// number in any value past 300 degrees
|
||||
if (_colorData.X > 0)
|
||||
{
|
||||
_topColorSlider.Value = _colorData.X;
|
||||
_topInputBox.Value = (int)(_colorData.X * 360.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
_topInputBox.Value = (int)(_topColorSlider.Value * 360.0f);
|
||||
}
|
||||
|
||||
_middleColorSlider.Value = _colorData.Y;
|
||||
_bottomColorSlider.Value = _colorData.Z;
|
||||
|
||||
_middleInputBox.Value = (int)(_colorData.Y * 100.0f);
|
||||
_bottomInputBox.Value = (int)(_colorData.Z * 100.0f);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_alphaSlider.Value = Color.A;
|
||||
_alphaInputBox.Value = (int)(Color.A * 100.0f);
|
||||
_updating = false;
|
||||
}
|
||||
|
||||
private bool IsSpinBoxValid(int value, ColorSliderOrder ordering)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ordering == ColorSliderOrder.Alpha)
|
||||
{
|
||||
return value <= 100;
|
||||
}
|
||||
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return value <= byte.MaxValue;
|
||||
case ColorSelectorType.Hsv:
|
||||
switch (ordering)
|
||||
{
|
||||
case ColorSliderOrder.Top:
|
||||
return value <= 360;
|
||||
default:
|
||||
return value <= 100;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private (string, string, string) GetSliderLabels()
|
||||
{
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-red"),
|
||||
Loc.GetString("color-selector-sliders-green"),
|
||||
Loc.GetString("color-selector-sliders-blue")
|
||||
);
|
||||
case ColorSelectorType.Hsv:
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-hue"),
|
||||
Loc.GetString("color-selector-sliders-saturation"),
|
||||
Loc.GetString("color-selector-sliders-value")
|
||||
);
|
||||
}
|
||||
|
||||
return ("ERR", "ERR", "ERR");
|
||||
ColorSliderOrder.Top => (_topColorSlider, _topInputBox),
|
||||
ColorSliderOrder.Middle => (_middleColorSlider, _middleInputBox),
|
||||
ColorSliderOrder.Bottom => (_bottomColorSlider, _bottomInputBox),
|
||||
ColorSliderOrder.Alpha => (_alphaSlider, _alphaInputBox),
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
private float GetColorValueDivisor(ColorSliderOrder order)
|
||||
{
|
||||
if (order == ColorSliderOrder.Alpha)
|
||||
{
|
||||
return 100.0f;
|
||||
}
|
||||
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return 255.0f;
|
||||
case ColorSelectorType.Hsv:
|
||||
switch (order)
|
||||
{
|
||||
case ColorSliderOrder.Top:
|
||||
return 360.0f;
|
||||
default:
|
||||
return 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
return order == ColorSliderOrder.Alpha
|
||||
? AlphaDivisor
|
||||
: GetStrategy().GetColorValueDivisor(order);
|
||||
}
|
||||
|
||||
private void OnColorSet()
|
||||
private void UpdateType()
|
||||
{
|
||||
// stack overflow otherwise due to value sets
|
||||
if (_updating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var strategy = GetStrategy();
|
||||
var labels = strategy.GetSliderLabelTexts();
|
||||
_topSliderLabel.Text = labels.top;
|
||||
_middleSliderLabel.Text = labels.middle;
|
||||
_bottomSliderLabel.Text = labels.bottom;
|
||||
|
||||
_colorData = new Vector4(_topColorSlider.Value, _middleColorSlider.Value, _bottomColorSlider.Value, _alphaSlider.Value);
|
||||
_topStyle.ConfigureSlider(strategy.TopSliderStyle);
|
||||
_middleStyle.ConfigureSlider(strategy.MiddleSliderStyle);
|
||||
_bottomStyle.ConfigureSlider(strategy.BottomSliderStyle);
|
||||
}
|
||||
|
||||
_currentColor = SelectorType switch
|
||||
private void UpdateSlider(ColorSliderOrder order)
|
||||
{
|
||||
var (slider, inputBox) = GetSliderByOrder(order);
|
||||
var divisor = GetColorValueDivisor(order);
|
||||
|
||||
var dataValue = order switch
|
||||
{
|
||||
ColorSelectorType.Hsv => Color.FromHsv(_colorData),
|
||||
_ => new Color(_colorData.X, _colorData.Y, _colorData.Z, _colorData.W)
|
||||
ColorSliderOrder.Top => _colorData.X,
|
||||
ColorSliderOrder.Middle => _colorData.Y,
|
||||
ColorSliderOrder.Bottom => _colorData.Z,
|
||||
ColorSliderOrder.Alpha => _colorData.W,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(order))
|
||||
};
|
||||
|
||||
Update();
|
||||
slider.SetValueWithoutEvent(dataValue);
|
||||
inputBox.OverrideValue((int)(dataValue * divisor));
|
||||
}
|
||||
|
||||
private void UpdateSliderVisuals()
|
||||
{
|
||||
_topStyle.SetBaseColor(_colorData);
|
||||
_middleStyle.SetBaseColor(_colorData);
|
||||
_bottomStyle.SetBaseColor(_colorData);
|
||||
_colorDescriptionLabel.Text = ColorNaming.Describe(Color, _localization);
|
||||
}
|
||||
|
||||
private void UpdateAllSliders()
|
||||
{
|
||||
UpdateSliderVisuals();
|
||||
UpdateSlider(ColorSliderOrder.Top);
|
||||
UpdateSlider(ColorSliderOrder.Middle);
|
||||
UpdateSlider(ColorSliderOrder.Bottom);
|
||||
UpdateSlider(ColorSliderOrder.Alpha);
|
||||
}
|
||||
|
||||
private bool IsSpinBoxValid(int value, ColorSliderOrder ordering)
|
||||
{
|
||||
var divisor = GetColorValueDivisor(ordering);
|
||||
var channelValue = value / divisor;
|
||||
|
||||
return channelValue >= 0.0f && channelValue <= 1.0f;
|
||||
}
|
||||
|
||||
private void OnInputBoxValueChanged(ValueChangedEventArgs args, ColorSliderOrder order)
|
||||
{
|
||||
var (slider, _) = GetSliderByOrder(order);
|
||||
var value = args.Value / GetColorValueDivisor(order);
|
||||
|
||||
// We are intentionally triggering the slider OnValueChanged event here.
|
||||
// This is so that the color data values of the sliders are updated accordingly.
|
||||
slider.Value = value;
|
||||
}
|
||||
|
||||
private void OnSliderValueChanged(ColorSliderOrder order)
|
||||
{
|
||||
_colorData = new Vector4(
|
||||
_topColorSlider.Value,
|
||||
_middleColorSlider.Value,
|
||||
_bottomColorSlider.Value,
|
||||
_alphaSlider.Value);
|
||||
|
||||
_currentColor = GetStrategy().FromColorData(_colorData);
|
||||
OnColorChanged?.Invoke(_currentColor);
|
||||
|
||||
UpdateSliderVisuals();
|
||||
UpdateSlider(order);
|
||||
}
|
||||
|
||||
private enum ColorSliderOrder
|
||||
@@ -415,4 +350,121 @@ public sealed class ColorSelectorSliders : Control
|
||||
Rgb,
|
||||
Hsv,
|
||||
}
|
||||
|
||||
private abstract class ColorSliderStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// The style preset used by the top slider.
|
||||
/// </summary>
|
||||
public abstract ColorSelectorStyleBox.ColorSliderPreset TopSliderStyle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The style preset used by the middle slider.
|
||||
/// </summary>
|
||||
public abstract ColorSelectorStyleBox.ColorSliderPreset MiddleSliderStyle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The style preset used by the bottom slider.
|
||||
/// </summary>
|
||||
public abstract ColorSelectorStyleBox.ColorSliderPreset BottomSliderStyle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Color to a Vector4 representation of its components.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each value in the Vector4 must be between 0.0f and 1.0f; this is used in the
|
||||
/// context of slider values, which are between these ranges.
|
||||
/// </remarks>
|
||||
/// <param name="color">A Color to convert into Vector4 slider values.</param>
|
||||
/// <returns>A Vector4 representation of a Color's slider values.</returns>
|
||||
public abstract Vector4 ToColorData(Color color);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector4 representation of color slider values into a Color.
|
||||
/// </summary>
|
||||
/// <param name="colorData">A Vector4 representation of color slider values.</param>
|
||||
/// <returns>A color generated from slider values.</returns>
|
||||
public abstract Color FromColorData(Vector4 colorData);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a color component divisor for the given slider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used for converting slider values to/from color component values.
|
||||
/// For example, in RGB coloration, each channel ranges from 0 to 255,
|
||||
/// so if you had a slider value of 0.2, you would multiply 0.2 * 255 = 51
|
||||
/// for the "channel" value.
|
||||
///
|
||||
/// This does not apply to the Alpha channel, as the Alpha channel
|
||||
/// always uses the same divisor; this is defined in ColorSelectorSliders.
|
||||
/// </remarks>
|
||||
/// <param name="order">The slider to retrieve a divisor for.</param>
|
||||
/// <returns>The divisor for the given slider.</returns>
|
||||
public abstract float GetColorValueDivisor(ColorSliderOrder order);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a label text string for the first three color sliders.
|
||||
/// </summary>
|
||||
/// <returns>Label text strings for the top, middle, and bottom sliders.</returns>
|
||||
public abstract (string top, string middle, string bottom) GetSliderLabelTexts();
|
||||
}
|
||||
|
||||
private sealed class RgbSliderStrategy : ColorSliderStrategy
|
||||
{
|
||||
private const float ChannelMaxValue = byte.MaxValue;
|
||||
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset TopSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Red;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset MiddleSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Green;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset BottomSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Blue;
|
||||
|
||||
public override Vector4 ToColorData(Color color) => new(color.R, color.G, color.B, color.A);
|
||||
public override Color FromColorData(Vector4 colorData)
|
||||
=> new(colorData.X, colorData.Y, colorData.Z, colorData.W);
|
||||
|
||||
public override float GetColorValueDivisor(ColorSliderOrder order) => ChannelMaxValue;
|
||||
|
||||
public override (string top, string middle, string bottom) GetSliderLabelTexts()
|
||||
{
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-red"),
|
||||
Loc.GetString("color-selector-sliders-green"),
|
||||
Loc.GetString("color-selector-sliders-blue"));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class HsvSliderStrategy : ColorSliderStrategy
|
||||
{
|
||||
private const float HueMaxValue = 360.0f;
|
||||
private const float SliderMaxValue = 100.0f;
|
||||
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset TopSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Hue;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset MiddleSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Saturation;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset BottomSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Value;
|
||||
|
||||
public override Vector4 ToColorData(Color color) => Color.ToHsv(color);
|
||||
public override Color FromColorData(Vector4 colorData) => Color.FromHsv(colorData);
|
||||
|
||||
public override float GetColorValueDivisor(ColorSliderOrder order)
|
||||
{
|
||||
return order switch
|
||||
{
|
||||
ColorSliderOrder.Top => HueMaxValue,
|
||||
_ => SliderMaxValue,
|
||||
};
|
||||
}
|
||||
|
||||
public override (string top, string middle, string bottom) GetSliderLabelTexts()
|
||||
{
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-hue"),
|
||||
Loc.GetString("color-selector-sliders-saturation"),
|
||||
Loc.GetString("color-selector-sliders-value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,19 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
/// <summary>
|
||||
/// Base background colour.
|
||||
/// </summary>
|
||||
public Robust.Shared.Maths.Vector4 BaseColor;
|
||||
public Vector4 BaseColor;
|
||||
|
||||
/// <summary>
|
||||
/// Colour to add to the background colour along the X-axis.
|
||||
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
|
||||
/// </summary>
|
||||
public Robust.Shared.Maths.Vector4 XAxis;
|
||||
public Vector4 XAxis;
|
||||
|
||||
/// <summary>
|
||||
/// Colour to add to the background colour along the y-axis.
|
||||
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
|
||||
/// </summary>
|
||||
public Robust.Shared.Maths.Vector4 YAxis;
|
||||
public Vector4 YAxis;
|
||||
|
||||
/// <summary>
|
||||
/// If true, then <see cref="BaseColor"/>, <see cref="XAxis"/>, and <see cref="YAxis"/> will be interpreted as HSVa
|
||||
@@ -93,14 +93,14 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
{
|
||||
var colorData = Hsv
|
||||
? Color.ToHsv(color)
|
||||
: new Robust.Shared.Maths.Vector4(color.R, color.G, color.B, color.A);
|
||||
: new Vector4(color.R, color.G, color.B, color.A);
|
||||
SetBaseColor(colorData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that sets the base color by taking in some color and removing the components that are controlled by the x and y axes.
|
||||
/// </summary>
|
||||
public void SetBaseColor(Robust.Shared.Maths.Vector4 colorData)
|
||||
public void SetBaseColor(Vector4 colorData)
|
||||
{
|
||||
BaseColor = colorData - colorData * XAxis - colorData * YAxis;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
var itemHeight = 0f;
|
||||
if (item.Icon != null)
|
||||
{
|
||||
itemHeight = item.IconSize.Y;
|
||||
itemHeight = item.IconSize.Y * item.IconScale;
|
||||
}
|
||||
|
||||
itemHeight = Math.Max(itemHeight, ActualFont.GetHeight(UIScale));
|
||||
@@ -111,21 +111,21 @@ namespace Robust.Client.UserInterface.Controls
|
||||
Recalculate();
|
||||
}
|
||||
|
||||
public void AddItems(IEnumerable<string> texts, Texture? icon = null, bool selectable = true, object? metadata = null)
|
||||
public void AddItems(IEnumerable<string> texts, Texture? icon = null, bool selectable = true, object? metadata = null, float iconScale = 1)
|
||||
{
|
||||
var items = new ValueList<Item>();
|
||||
|
||||
foreach (var text in texts)
|
||||
{
|
||||
items.Add(new Item(this) {Text = text, Icon = icon, Selectable = selectable, Metadata = metadata});
|
||||
items.Add(new Item(this) {Text = text, Icon = icon, IconScale = iconScale, Selectable = selectable, Metadata = metadata});
|
||||
}
|
||||
|
||||
Add(items);
|
||||
}
|
||||
|
||||
public Item AddItem(string text, Texture? icon = null, bool selectable = true, object? metadata = null)
|
||||
public Item AddItem(string text, Texture? icon = null, bool selectable = true, object? metadata = null, float iconScale = 1)
|
||||
{
|
||||
var item = new Item(this) {Text = text, Icon = icon, Selectable = selectable, Metadata = metadata};
|
||||
var item = new Item(this) {Text = text, Icon = icon, IconScale = iconScale, Selectable = selectable, Metadata = metadata};
|
||||
Add(item);
|
||||
return item;
|
||||
}
|
||||
@@ -477,7 +477,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
var itemHeight = 0f;
|
||||
if (item.Icon != null)
|
||||
{
|
||||
itemHeight = item.IconSize.Y;
|
||||
itemHeight = item.IconSize.Y * item.IconScale;
|
||||
}
|
||||
|
||||
itemHeight = Math.Max(itemHeight, font.GetHeight(UIScale));
|
||||
@@ -496,19 +496,19 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
if (item.IconRegion.Size == Vector2.Zero)
|
||||
{
|
||||
handle.DrawTextureRect(item.Icon, UIBox2.FromDimensions(drawOffset, item.Icon.Size),
|
||||
handle.DrawTextureRect(item.Icon, UIBox2.FromDimensions(drawOffset, item.Icon.Size * item.IconScale),
|
||||
item.IconModulate);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.DrawTextureRectRegion(item.Icon, UIBox2.FromDimensions(drawOffset, item.Icon.Size),
|
||||
handle.DrawTextureRectRegion(item.Icon, UIBox2.FromDimensions(drawOffset, item.Icon.Size * item.IconScale),
|
||||
item.IconRegion, item.IconModulate);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Text != null)
|
||||
{
|
||||
var textBox = new UIBox2(contentBox.Left + item.IconSize.X, contentBox.Top, contentBox.Right,
|
||||
var textBox = new UIBox2(contentBox.Left + item.IconSize.X * item.IconScale, contentBox.Top, contentBox.Right,
|
||||
contentBox.Bottom);
|
||||
DrawTextInternal(handle, item.Text, textBox);
|
||||
}
|
||||
@@ -722,6 +722,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public Texture? Icon { get; set; }
|
||||
public UIBox2 IconRegion { get; set; }
|
||||
public Color IconModulate { get; set; } = Color.White;
|
||||
public float IconScale { get; set; } = 1;
|
||||
public bool Selectable { get; set; } = true;
|
||||
public bool TooltipEnabled { get; set; } = true;
|
||||
public UIBox2? Region { get; set; }
|
||||
|
||||
@@ -326,7 +326,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
var parentSize = control.Parent?.Size ?? Vector2.Zero;
|
||||
|
||||
var anchorLeft = control.GetValue<float>(AnchorLeftProperty);
|
||||
var anchorTop = control.GetValue<float>(AnchorBottomProperty);
|
||||
var anchorTop = control.GetValue<float>(AnchorTopProperty);
|
||||
var anchorRight = control.GetValue<float>(AnchorRightProperty);
|
||||
var anchorBottom = control.GetValue<float>(AnchorBottomProperty);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
|
||||
@@ -119,9 +119,12 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
|
||||
case DragMode.Bottom | DragMode.Left:
|
||||
case DragMode.Top | DragMode.Right:
|
||||
cursor = CursorShape.NESWResize;
|
||||
break;
|
||||
|
||||
case DragMode.Bottom | DragMode.Right:
|
||||
case DragMode.Top | DragMode.Left:
|
||||
cursor = CursorShape.Crosshair;
|
||||
cursor = CursorShape.NWSEResize;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
MinSize="350 200">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<LineEdit Name="SearchBar" Access="Public" HorizontalExpand="True" PlaceHolder="{Loc entity-spawn-window-search-bar-placeholder}"/>
|
||||
<Button Name="ClearButton" Access="Public" Disabled="True" Text="{Loc entity-spawn-window-clear-button}" />
|
||||
<LineEdit Name="SearchBar" Access="Public" HorizontalExpand="True" PlaceHolder="{Loc window-search-bar-placeholder}"/>
|
||||
<Button Name="ClearButton" Access="Public" Disabled="True" Text="{Loc window-clear-button}" />
|
||||
</BoxContainer>
|
||||
<ScrollContainer Name="PrototypeScrollContainer" Access="Public" MinSize="200 0" VerticalExpand="True">
|
||||
<PrototypeListContainer Name="PrototypeList" Access="Public"/>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -12,34 +16,24 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class EntitySpawnWindow : DefaultWindow
|
||||
{
|
||||
public static readonly string[] InitOpts =
|
||||
{
|
||||
"Default",
|
||||
"PlaceFree",
|
||||
"PlaceNearby",
|
||||
"SnapgridCenter",
|
||||
"SnapgridBorder",
|
||||
"AlignSimilar",
|
||||
"AlignTileAny",
|
||||
"AlignTileEmpty",
|
||||
"AlignTileNonDense",
|
||||
"AlignTileDense",
|
||||
"AlignWall",
|
||||
"AlignWallProper",
|
||||
};
|
||||
[Dependency] private readonly IPlacementManager _placement = default!;
|
||||
|
||||
public EntitySpawnButton? SelectedButton;
|
||||
public EntityPrototype? SelectedPrototype;
|
||||
|
||||
[Obsolete("Use IPlacementManager.AllModeNames")]
|
||||
public static string[] InitOpts =>IoCManager.Resolve<IPlacementManager>().AllModeNames;
|
||||
public EntitySpawnWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MeasureButton.Measure(Vector2Helpers.Infinity);
|
||||
|
||||
for (var i = 0; i < InitOpts.Length; i++)
|
||||
var modes = _placement.AllModeNames;
|
||||
for (var i = 0; i < modes.Length; i++)
|
||||
{
|
||||
OverrideMenu.AddItem(InitOpts[i], i);
|
||||
OverrideMenu.AddItem(modes[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
MinSize="300 200">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<LineEdit Name="SearchBar" Access="Public" HorizontalExpand="True" PlaceHolder="Search"/>
|
||||
<Button Name="ClearButton" Access="Public" Text="Clear"/>
|
||||
<LineEdit Name="SearchBar" Access="Public" HorizontalExpand="True" PlaceHolder="{Loc window-search-bar-placeholder}"/>
|
||||
<Button Name="ClearButton" Access="Public" Text="{Loc window-clear-button}"/>
|
||||
</BoxContainer>
|
||||
<ItemList Name="TileList" Access="Public" VerticalExpand="True"/>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
|
||||
@@ -202,7 +202,14 @@ internal partial class UserInterfaceManager
|
||||
return;
|
||||
}
|
||||
|
||||
var shape = cursorTarget.DefaultCursorShape switch
|
||||
var shape = MapCursorShape(cursorTarget.DefaultCursorShape);
|
||||
|
||||
_clyde.SetCursor(_clyde.GetStandardCursor(shape));
|
||||
}
|
||||
|
||||
private static StandardCursorShape MapCursorShape(Control.CursorShape shape)
|
||||
{
|
||||
return shape switch
|
||||
{
|
||||
Control.CursorShape.Arrow => StandardCursorShape.Arrow,
|
||||
Control.CursorShape.IBeam => StandardCursorShape.IBeam,
|
||||
@@ -210,10 +217,21 @@ internal partial class UserInterfaceManager
|
||||
Control.CursorShape.Crosshair => StandardCursorShape.Crosshair,
|
||||
Control.CursorShape.VResize => StandardCursorShape.VResize,
|
||||
Control.CursorShape.HResize => StandardCursorShape.HResize,
|
||||
Control.CursorShape.Progress => StandardCursorShape.Progress,
|
||||
Control.CursorShape.NWSEResize => StandardCursorShape.NWSEResize,
|
||||
Control.CursorShape.NESWResize => StandardCursorShape.NESWResize,
|
||||
Control.CursorShape.Move => StandardCursorShape.Move,
|
||||
Control.CursorShape.NotAllowed => StandardCursorShape.NotAllowed,
|
||||
Control.CursorShape.NWResize => StandardCursorShape.NWResize,
|
||||
Control.CursorShape.NResize => StandardCursorShape.NResize,
|
||||
Control.CursorShape.NEResize => StandardCursorShape.NEResize,
|
||||
Control.CursorShape.EResize => StandardCursorShape.EResize,
|
||||
Control.CursorShape.SEResize => StandardCursorShape.SEResize,
|
||||
Control.CursorShape.SResize => StandardCursorShape.SResize,
|
||||
Control.CursorShape.SWResize => StandardCursorShape.SWResize,
|
||||
Control.CursorShape.WResize => StandardCursorShape.WResize,
|
||||
_ => StandardCursorShape.Arrow
|
||||
};
|
||||
|
||||
_clyde.SetCursor(_clyde.GetStandardCursor(shape));
|
||||
}
|
||||
|
||||
public void MouseWheel(MouseWheelEventArgs args)
|
||||
|
||||
@@ -3,7 +3,9 @@ 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;
|
||||
@@ -18,24 +20,25 @@ namespace Robust.Client.UserInterface.XAML.Proxy;
|
||||
/// </remarks>
|
||||
internal sealed class XamlHotReloadManager : IXamlHotReloadManager
|
||||
{
|
||||
private const string MarkerFileName = "SpaceStation14.sln";
|
||||
|
||||
[Dependency] ILogManager _logManager = null!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = null!;
|
||||
[Dependency] private readonly 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;
|
||||
}
|
||||
|
||||
@@ -129,7 +132,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,6 +1,9 @@
|
||||
using System;
|
||||
#if !WINDOWS
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using SDL3;
|
||||
|
||||
namespace Robust.Client.Utility
|
||||
{
|
||||
@@ -9,32 +12,40 @@ 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 (OperatingSystem.IsLinux())
|
||||
return NativeLibrary.Load("libswnfd.so", assembly, path);
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
return NativeLibrary.Load("libswnfd.dylib", assembly, path);
|
||||
|
||||
return IntPtr.Zero;
|
||||
#if LINUX || FREEBSD
|
||||
return NativeLibrary.Load("libswnfd.so", assembly, path);
|
||||
#elif MACOS
|
||||
return NativeLibrary.Load("libswnfd.dylib", assembly, path);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (name == "libEGL.dll")
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
return NativeLibrary.Load("libEGL.so", assembly, path);
|
||||
#if LINUX || FREEBSD
|
||||
return NativeLibrary.Load("libEGL.so", assembly, path);
|
||||
#endif
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
if (name == SDL.nativeLibName)
|
||||
{
|
||||
#if LINUX || FREEBSD
|
||||
return NativeLibrary.Load("libSDL3.so.0", assembly, path);
|
||||
#elif MACOS
|
||||
return NativeLibrary.Load("libSDL3.0.dylib", assembly, path);
|
||||
#endif
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
});
|
||||
|
||||
#if MACOS
|
||||
OpenALLibraryNameContainer.OverridePath = "libopenal.1.dylib";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.ViewVariables.Editors;
|
||||
using Robust.Client.ViewVariables.Instances;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.ViewVariables.Editors.VVPropEditorNumeric;
|
||||
|
||||
namespace Robust.Client.ViewVariables
|
||||
{
|
||||
@@ -31,8 +24,7 @@ namespace Robust.Client.ViewVariables
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IRobustSerializer _robustSerializer = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly IResourceManager _resManager = default!;
|
||||
[Dependency] private readonly IViewVariableControlFactory _controlFactory = default!;
|
||||
|
||||
private uint _nextReqId = 1;
|
||||
private readonly Vector2i _defaultWindowSize = (640, 420);
|
||||
@@ -64,190 +56,7 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public VVPropEditor PropertyFor(Type? type)
|
||||
{
|
||||
// TODO: make this more flexible.
|
||||
if (type == null)
|
||||
{
|
||||
return new VVPropEditorDummy();
|
||||
}
|
||||
|
||||
if (type == typeof(sbyte))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.SByte);
|
||||
}
|
||||
|
||||
if (type == typeof(byte))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Byte);
|
||||
}
|
||||
|
||||
if (type == typeof(ushort))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.UShort);
|
||||
}
|
||||
|
||||
if (type == typeof(short))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Short);
|
||||
}
|
||||
|
||||
if (type == typeof(uint))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.UInt);
|
||||
}
|
||||
|
||||
if (type == typeof(int))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Int);
|
||||
}
|
||||
|
||||
if (type == typeof(ulong))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.ULong);
|
||||
}
|
||||
|
||||
if (type == typeof(long))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Long);
|
||||
}
|
||||
|
||||
if (type == typeof(float))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Float);
|
||||
}
|
||||
|
||||
if (type == typeof(double))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Double);
|
||||
}
|
||||
|
||||
if (type == typeof(decimal))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Decimal);
|
||||
}
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
return new VVPropEditorString();
|
||||
}
|
||||
|
||||
if (type == typeof(EntProtoId?))
|
||||
{
|
||||
return new VVPropEditorNullableEntProtoId();
|
||||
}
|
||||
|
||||
if (type == typeof(EntProtoId))
|
||||
{
|
||||
return new VVPropEditorEntProtoId();
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ProtoId<>))
|
||||
{
|
||||
var editor =
|
||||
(VVPropEditor)Activator.CreateInstance(
|
||||
typeof(VVPropEditorProtoId<>).MakeGenericType(type.GenericTypeArguments[0]))!;
|
||||
|
||||
IoCManager.InjectDependencies(editor);
|
||||
return editor;
|
||||
}
|
||||
|
||||
if (typeof(IPrototype).IsAssignableFrom(type) || typeof(ViewVariablesBlobMembers.PrototypeReferenceToken).IsAssignableFrom(type))
|
||||
{
|
||||
return (VVPropEditor)Activator.CreateInstance(typeof(VVPropEditorIPrototype<>).MakeGenericType(type))!;
|
||||
}
|
||||
|
||||
if (typeof(ISelfSerialize).IsAssignableFrom(type))
|
||||
{
|
||||
return (VVPropEditor)Activator.CreateInstance(typeof(VVPropEditorISelfSerializable<>).MakeGenericType(type))!;
|
||||
}
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
return new VVPropEditorEnum();
|
||||
}
|
||||
|
||||
if (type == typeof(Vector2))
|
||||
{
|
||||
return new VVPropEditorVector2(intVec: false);
|
||||
}
|
||||
|
||||
if (type == typeof(Vector2i))
|
||||
{
|
||||
return new VVPropEditorVector2(intVec: true);
|
||||
}
|
||||
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
return new VVPropEditorBoolean();
|
||||
}
|
||||
|
||||
if (type == typeof(Angle))
|
||||
{
|
||||
return new VVPropEditorAngle();
|
||||
}
|
||||
|
||||
if (type == typeof(Box2))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2);
|
||||
}
|
||||
|
||||
if (type == typeof(Box2i))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2i);
|
||||
}
|
||||
|
||||
if (type == typeof(UIBox2))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2);
|
||||
}
|
||||
|
||||
if (type == typeof(UIBox2i))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2i);
|
||||
}
|
||||
|
||||
if (type == typeof(EntityCoordinates))
|
||||
{
|
||||
return new VVPropEditorEntityCoordinates();
|
||||
}
|
||||
|
||||
if (type == typeof(EntityUid))
|
||||
{
|
||||
return new VVPropEditorEntityUid();
|
||||
}
|
||||
|
||||
if (type == typeof(NetEntity))
|
||||
{
|
||||
return new VVPropEditorNetEntity();
|
||||
}
|
||||
|
||||
if (type == typeof(Color))
|
||||
{
|
||||
return new VVPropEditorColor();
|
||||
}
|
||||
|
||||
if (type == typeof(TimeSpan))
|
||||
{
|
||||
return new VVPropEditorTimeSpan();
|
||||
}
|
||||
|
||||
if (typeof(SoundSpecifier).IsAssignableFrom(type))
|
||||
{
|
||||
var control = new VVPropEditorSoundSpecifier(_protoManager, _resManager);
|
||||
return control;
|
||||
}
|
||||
|
||||
if (type == typeof(ViewVariablesBlobMembers.ServerKeyValuePairToken) ||
|
||||
type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
|
||||
{
|
||||
return new VVPropEditorKeyValuePair();
|
||||
}
|
||||
|
||||
if (type != typeof(ViewVariablesBlobMembers.ServerValueTypeToken) && !type.IsValueType)
|
||||
{
|
||||
return new VVPropEditorReference();
|
||||
}
|
||||
|
||||
return new VVPropEditorDummy();
|
||||
return _controlFactory.CreateFor(type);
|
||||
}
|
||||
|
||||
public void OpenVV(object obj)
|
||||
@@ -263,7 +72,7 @@ namespace Robust.Client.ViewVariables
|
||||
instance = new ViewVariablesInstanceObject(this, _robustSerializer);
|
||||
}
|
||||
|
||||
var window = new DefaultWindow {Title = Loc.GetString("view-variables")};
|
||||
var window = new DefaultWindow { Title = Loc.GetString("view-variables") };
|
||||
instance.Initialize(window, obj);
|
||||
window.OnClose += () => _closeInstance(instance, false);
|
||||
_windows.Add(instance, window);
|
||||
@@ -273,7 +82,7 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public void OpenVV(string path)
|
||||
{
|
||||
if (ReadPath(path) is {} obj)
|
||||
if (ReadPath(path) is { } obj)
|
||||
OpenVV(obj);
|
||||
}
|
||||
|
||||
@@ -284,7 +93,7 @@ namespace Robust.Client.ViewVariables
|
||||
Title = Loc.GetString("view-variables"),
|
||||
SetSize = _defaultWindowSize
|
||||
};
|
||||
var loadingLabel = new Label {Text = "Retrieving remote object data from server..."};
|
||||
var loadingLabel = new Label { Text = "Retrieving remote object data from server..." };
|
||||
window.Contents.AddChild(loadingLabel);
|
||||
|
||||
// We need to request the data, THEN create an instance.
|
||||
@@ -352,7 +161,7 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public async Task<T> RequestData<T>(ViewVariablesRemoteSession session, ViewVariablesRequest meta) where T : ViewVariablesBlob
|
||||
{
|
||||
return (T) await RequestData(session, meta);
|
||||
return (T)await RequestData(session, meta);
|
||||
}
|
||||
|
||||
public void CloseSession(ViewVariablesRemoteSession session)
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors;
|
||||
|
||||
internal sealed class VVPropEditorEntProtoId : VVPropEditor
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
|
||||
public VVPropEditorEntProtoId()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var lineEdit = new LineEdit
|
||||
{
|
||||
Text = (EntProtoId) (value ?? ""),
|
||||
Text = (EntProtoId)(value ?? ""),
|
||||
Editable = !ReadOnly,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
@@ -19,7 +27,9 @@ internal sealed class VVPropEditorEntProtoId : VVPropEditor
|
||||
{
|
||||
lineEdit.OnTextEntered += e =>
|
||||
{
|
||||
ValueChanged((EntProtoId) e.Text);
|
||||
var id = (EntProtoId)e.Text;
|
||||
if (_protoMan.HasIndex(id))
|
||||
ValueChanged(id);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user