mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
135 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a886222946 | ||
|
|
5843f1087e | ||
|
|
93c0ce815f | ||
|
|
1c64fa1f28 | ||
|
|
c825c1e413 | ||
|
|
f30fb47834 | ||
|
|
5d255e06c8 | ||
|
|
80357c8ec4 | ||
|
|
ac3a434bdf | ||
|
|
21719b8884 | ||
|
|
dbb6b90654 | ||
|
|
4b92be5324 | ||
|
|
95169b7a71 | ||
|
|
b699e22c85 | ||
|
|
e48cc62d0b | ||
|
|
aade062a49 | ||
|
|
e02166d5c4 | ||
|
|
8037bfae14 | ||
|
|
49781791af | ||
|
|
92719aa29f | ||
|
|
1d47a9677d | ||
|
|
14fe8eba6d | ||
|
|
2ff99d4a62 | ||
|
|
ce8b2d82a3 | ||
|
|
f8f99450db | ||
|
|
a3cf4877e4 | ||
|
|
6ab08f7dc1 | ||
|
|
819f6921cf | ||
|
|
cf91369d27 | ||
|
|
0e1328675c | ||
|
|
a7315b1c95 | ||
|
|
c8f2a55cbe | ||
|
|
7812502b0b | ||
|
|
3149f99954 | ||
|
|
ef8c6379cd | ||
|
|
f932e023ee | ||
|
|
a137c839fc | ||
|
|
3e43b88518 | ||
|
|
3d974e0305 | ||
|
|
b3682017ac | ||
|
|
194743a9b0 | ||
|
|
34d65a7960 | ||
|
|
6cd4a37a8f | ||
|
|
40d879fddc | ||
|
|
df398c5b13 | ||
|
|
804b698172 | ||
|
|
346514c6e0 | ||
|
|
d65e2eb169 | ||
|
|
ff41329ad7 | ||
|
|
6151a26622 | ||
|
|
1c7ae13bfa | ||
|
|
cb6645aebe | ||
|
|
fffe3c56e9 | ||
|
|
204e881690 | ||
|
|
161b1874c2 | ||
|
|
7c3634f1f5 | ||
|
|
9969899f38 | ||
|
|
ed35839942 | ||
|
|
380ccfacd8 | ||
|
|
cc0cc6afb1 | ||
|
|
8cc2a17444 | ||
|
|
9c64fbfce2 | ||
|
|
0218c4b969 | ||
|
|
cd72523701 | ||
|
|
76bb9b4b19 | ||
|
|
afdfbba312 | ||
|
|
8b4925863e | ||
|
|
e1597da4c7 | ||
|
|
8375a4038b | ||
|
|
8270442d66 | ||
|
|
85e1920b95 | ||
|
|
5347eb3350 | ||
|
|
e4a14d1ec8 | ||
|
|
c52db4d3f2 | ||
|
|
89f78d76ab | ||
|
|
bbc4668f9c | ||
|
|
ce4016965e | ||
|
|
21e74c9881 | ||
|
|
aa52e8c2ef | ||
|
|
e106d3f72b | ||
|
|
8eae802fb6 | ||
|
|
681feaf0c7 | ||
|
|
0a5a214a06 | ||
|
|
d80be16f6c | ||
|
|
d967bc9fdc | ||
|
|
177ca6b627 | ||
|
|
3c262afaa4 | ||
|
|
bce2901b0f | ||
|
|
c392d4f996 | ||
|
|
d7ee2bccd7 | ||
|
|
4e816fa5e7 | ||
|
|
545e55055e | ||
|
|
6b059ed356 | ||
|
|
b69b4fd8fe | ||
|
|
cb63499ec9 | ||
|
|
dd12110c34 | ||
|
|
a811cfc1a1 | ||
|
|
229a45bea2 | ||
|
|
78376ccca1 | ||
|
|
e4a1415627 | ||
|
|
69589195e0 | ||
|
|
ce3b92aea2 | ||
|
|
5dc980ae92 | ||
|
|
6edeafeed1 | ||
|
|
623aa6a0ae | ||
|
|
1399b71572 | ||
|
|
bbf8827efd | ||
|
|
591c261ff5 | ||
|
|
75f0cf9dd7 | ||
|
|
bde8c2c6e8 | ||
|
|
7d1ad527d9 | ||
|
|
6da708f285 | ||
|
|
f6a5e0ed93 | ||
|
|
21534e7568 | ||
|
|
e7aa2cbc7d | ||
|
|
5ddba87487 | ||
|
|
a69573178f | ||
|
|
1ac39cc65f | ||
|
|
70897b6ea9 | ||
|
|
e763d59617 | ||
|
|
d4902a9714 | ||
|
|
23a40e58e4 | ||
|
|
e71f1cc8a5 | ||
|
|
b82d246988 | ||
|
|
d309872334 | ||
|
|
e33c37ed09 | ||
|
|
3ee95b1a71 | ||
|
|
9a4721a3ee | ||
|
|
8fd1e9047f | ||
|
|
e44e4ac7ed | ||
|
|
581ef074a0 | ||
|
|
9bb61b8a35 | ||
|
|
e7497c7e4f | ||
|
|
7bc54d8f73 | ||
|
|
1631d93e41 |
@@ -23,7 +23,7 @@
|
||||
<PropertyGroup Condition="'$(FullRelease)' != 'True'">
|
||||
<DefineConstants>$(DefineConstants);DEVELOPMENT</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release' Or '$(Configuration)' == 'Tools'">
|
||||
<DefineConstants>$(DefineConstants);EXCEPTION_TOLERANCE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(EnableClientScripting)' == 'True'">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
405
RELEASE-NOTES.md
405
RELEASE-NOTES.md
@@ -54,6 +54,411 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 145.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed some old SpriteComponent data-fields ("rsi", and "layerDatums").
|
||||
|
||||
### New features
|
||||
|
||||
* Added `ActorSystem.TryGetActorFromUserId()`.
|
||||
* Added IPrototypeManager.EnumerateKinds().
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed SpriteSpecifierSerializer yaml validation not working properly.
|
||||
* Fixed IoC/Threading exceptions in `Resource.Load()`.
|
||||
* Fixed `TransformSystem.SetCoordinates()` throwing uninformative client-side errors.
|
||||
* Fixed `IResourceManager.ContentFileExists()` and `TryContentFileRead()` throwing exceptions on windows when trying to open a directory.
|
||||
|
||||
|
||||
## 144.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix some EntityLookup queries incorrectly being double transformed internally.
|
||||
* Shrink TileEnlargement even further for EntityLookup default queries.
|
||||
|
||||
|
||||
## 144.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Add new args to entitylookup methods to allow for shrinkage of tile-bounds checks. Default changed to shrink the grid-local AABB by the polygon skin to avoid clipping neighboring tile entities.
|
||||
* Non-hard fixtures will no longer count by default for EntityLookup.
|
||||
|
||||
### New features
|
||||
|
||||
* Added new EntityLookup flag to return non-hard fixtures or not.
|
||||
|
||||
|
||||
## 143.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Entity placement and spawn commands now raise informative events that content can handle.
|
||||
* Replay clients can now optionally ignore some errors instead of refusing to load the replay.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* `AudioParams.PlayOffsetSecond` will no longer apply an offset that is larger then the length of the audio stream.
|
||||
* Fixed yaml serialization of arrays of virtual/abstract objects.
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* Removed an incorrect gamestate debug assert.
|
||||
|
||||
|
||||
## 143.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add support for tests to load extra prototypes from multiple sources.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix named toolshed command.
|
||||
* Unsubscribe from grid rendering events on shutdown.
|
||||
|
||||
### Other
|
||||
|
||||
* Remove unnecessary test prototypes.
|
||||
|
||||
|
||||
## 143.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add locale support for grammatical measure words.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Don't raise contact events for entities that were QueueDeleted during the tick.
|
||||
* Exception on duplicate broadcast subscriptions as this was unsupported behaviour.
|
||||
|
||||
### Other
|
||||
|
||||
* Add VV ReadWrite to PhysicsComponent BodyStatus.
|
||||
|
||||
|
||||
## 143.0.0
|
||||
|
||||
### New features
|
||||
|
||||
|
||||
- Toolshed, a tacit shell language, has been introduced.
|
||||
- Use Robust.Shared.ToolshedManager to invoke commands, with optional input and output.
|
||||
- Implement IInvocationContext for custom invocation contexts i.e. scripting systems.
|
||||
|
||||
|
||||
## 142.1.2
|
||||
|
||||
### Other
|
||||
|
||||
* Don't log an error on failing to resolve for joint relay refreshing.
|
||||
|
||||
|
||||
## 142.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed a bad debug assert in `DetachParentToNull()`
|
||||
|
||||
|
||||
## 142.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* `IHttpClientHolder` holds a shared `HttpClient` for use by content. It has Happy Eyeballs fixed and an appropriate `User-Agent`.
|
||||
* Added `DataNode.ToString()`. Makes it easier to save yaml files and debug code.
|
||||
* Added some cvars to modify discord rich presence icons.
|
||||
* .ogg files now read the `Artist` and `Title` tags and make them available via new fields in `AudioStream`.
|
||||
* The default fragment shaders now have access to the local light level (`lowp vec3 lightSample`).
|
||||
* Added `IPrototypeManager.ValidateAllPrototypesSerializable()`, which can be used to check that all currently loaded prototypes can be serialised & deserialised.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix certain debug commands and tools crashing on non-SS14 RobustToolbox games due to a missing font.
|
||||
* Discord rich presence strings are now truncated if they are too long.
|
||||
* Fixed a couple of broadphase/entity-lookup update bugs that were affecting containers and entities attached to other (non-grid/map) entities.
|
||||
* Fixed `INetChannel.Disconnect()` not properly disconnecting clients in integration tests.
|
||||
|
||||
### Other
|
||||
|
||||
* Outgoing HTTP requests now all use Happy Eyeballs to try to prioritize IPv6. This is necessary because .NET still does not support this critical feature itself.
|
||||
* Made various physics related component properties VV-editable.
|
||||
* The default EntitySystem sawmill log level now defaults to `Info` instead of `Verbose`. The level remains verbose when in debug mode.
|
||||
|
||||
### Internal
|
||||
|
||||
* The debug asserts in `DetachParentToNull()` are now more informative.
|
||||
|
||||
|
||||
## 142.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix Enum serialization.
|
||||
|
||||
|
||||
## 142.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `EntityManager.GetAllComponents()` now returns a (EntityUid, Component) tuple
|
||||
|
||||
### New features
|
||||
|
||||
* Added `IPrototypeManager.ValidateFields()`, which uses reflection to validate that the default values of c# string fields correspond to valid entity prototypes. Validates any fields with a `ValidatePrototypeIdAttribute` and any data-field that uses the PrototypeIdSerializer custom type serializer.
|
||||
|
||||
### Other
|
||||
|
||||
* Replay playback will now log errors when encountering unhandled messages.
|
||||
* Made `GetAssemblyByName()` throw descriptive error messages.
|
||||
* Improved performance of various EntityLookupSystem functions
|
||||
|
||||
|
||||
## 141.2.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix component trait dictionaries not clearing on reconnect leading to bad GetComponent in areas (e.g. entire game looks black due to no entities).
|
||||
|
||||
|
||||
## 141.2.0
|
||||
|
||||
### Other
|
||||
|
||||
* Fix bug in `NetManager` that allowed exception spam through protocol abuse.
|
||||
|
||||
|
||||
## 141.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* MapInitEvent is run clientside for placementmanager entities to predict entity appearances.
|
||||
* Add CollisionLayerChangeEvent for physics fixtures.
|
||||
|
||||
|
||||
## 141.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Component.Initialize has been fully replaced with the Eventbus.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed potential crashes if buffered audio sources (e.g. MIDI) fail to create due to running out of audio streams.
|
||||
|
||||
### Other
|
||||
|
||||
* Pressing `^C` twice on the server will now cause it to hard-exit immediately.
|
||||
* `Tools` now has `EXCEPTION_TOLERANCE` enabled.
|
||||
|
||||
|
||||
## 140.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `IReplayRecordingManager.RecordingFinished` now takes a `ReplayRecordingFinished` object as argument.
|
||||
* `IReplayRecordingManager.GetReplayStats` now returns a `ReplayRecordingStats` struct instead of a tuple. The units have also been normalized
|
||||
|
||||
### New features
|
||||
|
||||
* `IReplayRecordingManager` can now track a "state" object for an active recording.
|
||||
* If the path given to `IReplayRecordingManager.TryStartRecording` is rooted, the base replay directory is ignored.
|
||||
|
||||
### Other
|
||||
|
||||
* `IReplayRecordingManager` no longer considers itself recording inside `RecordingFinished`.
|
||||
* `IReplayRecordingManager.Initialize()` was moved to an engine-internal interface.
|
||||
|
||||
|
||||
## 139.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove Component.Startup(), fully replacing it with the Eventbus.
|
||||
|
||||
|
||||
## 138.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add rotation methods to TransformSystem for no lerp.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix AnimationCompleted ordering.
|
||||
|
||||
|
||||
## 138.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Obsoleted unused `IMidiRenderer.VolumeBoost` property. Use `IMidiRenderer.VelocityOverride` instead.
|
||||
* `IMidiRenderer.TrackedCoordinates` is now a `MapCoordinates`.
|
||||
|
||||
### New features
|
||||
|
||||
* Added `Master` property to `IMidiRenderer`, which allows it to copy all MIDI events from another renderer.
|
||||
* Added `FilteredChannels` property to `IMidiRenderer`, which allows it to filter out notes from certain channels.
|
||||
* Added `SystemReset` helper property to `IMidiRenderer`, which allows you to easily send it a SystemReset MIDI message.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed some cases were `MidiRenderer` would not respect the `MidiBank` and `MidiProgram.
|
||||
* Fixed user soundfonts not loading.
|
||||
* Fixed `ItemList` item selection unselecting everything when in `Multiple` mode.
|
||||
|
||||
|
||||
## 137.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added BQL `paused` selector.
|
||||
* `ModUpdateLevel.PostInput` allows running content code after network and async task processing.
|
||||
|
||||
### Other
|
||||
|
||||
* BQL `with` now includes paused entities.
|
||||
* The game loop now times more accurately and avoids sleeping more than necessary.
|
||||
* Sandboxing (and thus, client startup) should be much faster when ran from the launcher.
|
||||
|
||||
|
||||
## 137.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Component network state handler methods have been fully deprecated and replaced with the eventbus event equivalents (ComponentGetState and ComponentHandleState).
|
||||
|
||||
|
||||
## 136.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed debugging on Linux when CEF is enabled.
|
||||
|
||||
|
||||
## 136.0.0
|
||||
|
||||
### New features
|
||||
|
||||
* Several more style box properties now scale with UI scale. Signature of some stylebox methods have been changed.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed OutputPanel scroll-bar not functioning properly.
|
||||
|
||||
|
||||
## 135.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Style boxes now scale with the current UI scale. This affects how the the margins, padding, and style box textures are drawn and how controls are arranged. Various style box methods now need to be provided with the current UI scale.
|
||||
|
||||
|
||||
## 134.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Several methods were moved out of the `UserInterface` components and into the UI system.
|
||||
* The BUI constructor arguments have changed and now require an EntityUid to be given instead of a component.
|
||||
|
||||
|
||||
## 133.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Replace Robust's Vector2 with System.Numerics.Vector2.
|
||||
|
||||
### New features
|
||||
|
||||
* `AssetPassPipe` has a new `CheckDuplicates` property that makes it explicitly check for and drop duplicate asset files passed through.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Static entities that are parented to other entities will no longer collide with their parent.
|
||||
* Fix some miscellaneous doc comments and typos (e.g. PvsSystem and EntityManager).
|
||||
* Fix ContentGetDirectoryEntries.
|
||||
|
||||
|
||||
## 132.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add method to clear all joints + relayed joints on an entity.
|
||||
|
||||
### Other
|
||||
|
||||
* Lower default MTU to `1000`.
|
||||
|
||||
### Internal
|
||||
|
||||
* Resolved some warnings and unnecessary component resolves.
|
||||
|
||||
|
||||
## 132.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* `Robust.Shared.Physics.Events.CollisionChangeEvent` now has the `EntityUid` of the physics body.
|
||||
|
||||
### Other
|
||||
|
||||
* Paused entities now pause their animations. There's no guarantee they'll resume at the same point (use SyncSprite instead).
|
||||
|
||||
### Internal
|
||||
|
||||
* Fix ComponentTreeSystem warnings.
|
||||
* Fix some miscellaneous other warnings.
|
||||
|
||||
|
||||
## 132.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Return maps first from FindGridsIntersecting which fixes rendering order issues for grids.
|
||||
|
||||
|
||||
## 132.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* TimeOffsetSerializer now always reads & writes zeros unless it is reading/writing an initialized map. EntityPrototypes with TimeOffsetSerializer data-fields need to default to zero.\
|
||||
* TimeOffsetSerializer now only applies a time offset when reading from yaml, not when copying.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a function to count the number of prototypes of a given kind. See `IPrototypeManager.Count<T>()`.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed a bug in `IPrototypeManager.EnumerateParents()` that was causing it to not actually return the parent prototypes.
|
||||
|
||||
### Other
|
||||
|
||||
* Map serialisation will now log errors when saving an uninitialized map that contains initialized entities.
|
||||
|
||||
|
||||
## 131.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add NextByte method to random.
|
||||
* Add method to get a random tile variant.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix replay component state bug.
|
||||
|
||||
### Internal
|
||||
|
||||
* Remove some AggressiveOptimization attributes.
|
||||
|
||||
|
||||
## 131.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
BIN
Resources/EngineFonts/NotoSans/NotoSansMono-Regular.ttf
Normal file
BIN
Resources/EngineFonts/NotoSans/NotoSansMono-Regular.ttf
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
- type: uiTheme
|
||||
id: Default
|
||||
path: /Textures/Interface/Default
|
||||
path: /Textures/Interface/Default/
|
||||
colors:
|
||||
# Root
|
||||
rootBackground: "#000000"
|
||||
|
||||
@@ -17,15 +17,15 @@ cmd-error-dir-not-found = Could not find directory: {$dir}.
|
||||
cmd-failure-no-attached-entity = There is no entity attached to this shell.
|
||||
|
||||
## 'help' command
|
||||
cmd-help-desc = Display general help or help text for a specific command
|
||||
cmd-help-help = Usage: help [command name]
|
||||
cmd-oldhelp-desc = Display general help or help text for a specific command
|
||||
cmd-oldhelp-help = Usage: help [command name]
|
||||
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
|
||||
|
||||
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
cmd-help-unknown = Unknown command: { $command }
|
||||
cmd-help-top = { $command } - { $description }
|
||||
cmd-help-invalid-args = Invalid amount of arguments.
|
||||
cmd-help-arg-cmdname = [command name]
|
||||
cmd-oldhelp-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
cmd-oldhelp-unknown = Unknown command: { $command }
|
||||
cmd-oldhelp-top = { $command } - { $description }
|
||||
cmd-oldhelp-invalid-args = Invalid amount of arguments.
|
||||
cmd-oldhelp-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Gets or sets a CVar.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
discord-rpc-in-main-menu = In Main Menu
|
||||
discord-rpc-in-main-menu-logo-text = I think coolsville SUCKS
|
||||
discord-rpc-character = Username: {$username}
|
||||
discord-rpc-on-server = On Server: {$servername}
|
||||
discord-rpc-players = Players: {$players}/{$maxplayers}
|
||||
discord-rpc-players = Players: {$players}/{$maxplayers}
|
||||
|
||||
@@ -22,7 +22,7 @@ cmd-replay-skip-hint = Ticks or timespan (HH:MM:SS).
|
||||
|
||||
cmd-replay-set-time-desc = Jump forwards or backwards to some specific time.
|
||||
cmd-replay-set-time-help = replay_set <tick or time>
|
||||
cmd-replay-set-time-hint = Tick or timespan (HH:MM:SS), starting from
|
||||
cmd-replay-set-time-hint = Tick or timespan (HH:MM:SS), starting from
|
||||
|
||||
cmd-replay-error-time = "{$time}" is not an integer or timespan.
|
||||
cmd-replay-error-args = Wrong number of arguments.
|
||||
@@ -33,7 +33,7 @@ cmd-replay-error-run-level = You cannot load a replay while connected to a serve
|
||||
# Recording commands
|
||||
|
||||
cmd-replay-recording-start-desc = Starts a replay recording, optionally with some time limit.
|
||||
cmd-replay-recording-start-help = Usage: replay_recording_start [name] [overwrite] [time limit]
|
||||
cmd-replay-recording-start-help = Usage: replay_recording_start [name] [overwrite] [time limit]
|
||||
cmd-replay-recording-start-success = Started recording a replay.
|
||||
cmd-replay-recording-start-already-recording = Already recording a replay.
|
||||
cmd-replay-recording-start-error = An error occurred while trying to start the recording.
|
||||
@@ -48,7 +48,7 @@ cmd-replay-recording-stop-not-recording = Not currently recording a replay.
|
||||
|
||||
cmd-replay-recording-stats-desc = Displays information about the current replay recording.
|
||||
cmd-replay-recording-stats-help = Usage: replay_recording_stats
|
||||
cmd-replay-recording-stats-result = Duration: {$time} min, Ticks: {$ticks}, Size: {$size} mb, rate: {$rate} mb/min.
|
||||
cmd-replay-recording-stats-result = Duration: {$time} min, Ticks: {$ticks}, Size: {$size} MB, rate: {$rate} MB/min.
|
||||
|
||||
|
||||
# Time Control UI
|
||||
@@ -56,4 +56,4 @@ replay-time-box-scrubbing-label = Dynamic Scrubbing
|
||||
replay-time-box-replay-time-label = Recording Time: {$current} / {$end} ({$percentage}%)
|
||||
replay-time-box-server-time-label = Server Time: {$current} / {$end}
|
||||
replay-time-box-index-label = Index: {$current} / {$total}
|
||||
replay-time-box-tick-label = Tick: {$current} / {$total}
|
||||
replay-time-box-tick-label = Tick: {$current} / {$total}
|
||||
|
||||
163
Resources/Locale/en-US/toolshed-commands.ftl
Normal file
163
Resources/Locale/en-US/toolshed-commands.ftl
Normal file
@@ -0,0 +1,163 @@
|
||||
command-description-tpto =
|
||||
Teleport the given entities to some target entity.
|
||||
command-description-player-list =
|
||||
Returns a list of all player sessions.
|
||||
command-description-player-self =
|
||||
Returns the current player session.
|
||||
command-description-player-imm =
|
||||
Returns the session associated with the player given as argument.
|
||||
command-description-player-entity =
|
||||
Returns the entities of the input sessions.
|
||||
command-description-self =
|
||||
Returns the current attached entity.
|
||||
command-description-physics-velocity =
|
||||
Returns the velocity of the input entities.
|
||||
command-description-physics-angular-velocity =
|
||||
Returns the angular velocity of the input entities.
|
||||
command-description-buildinfo =
|
||||
Provides information about the build of the game.
|
||||
command-description-cmd-list =
|
||||
Returns a list of all commands, for this side.
|
||||
command-description-explain =
|
||||
Explains the given expression, providing command descriptions and signatures.
|
||||
command-description-search =
|
||||
Searches through the input for the provided value.
|
||||
command-description-stopwatch =
|
||||
Measures the execution time of the given expression.
|
||||
command-description-types-consumers =
|
||||
Provides all commands that can consume the given type.
|
||||
command-description-types-tree =
|
||||
Debug tool to return all types the command interpreter can downcast the input to.
|
||||
command-description-types-gettype =
|
||||
Returns the type of the input.
|
||||
command-description-types-fullname =
|
||||
Returns the full name of the input type according to CoreCLR.
|
||||
command-description-as =
|
||||
Casts the input to the given type.
|
||||
Effectively a type hint if you know the type but the interpreter does not.
|
||||
command-description-count =
|
||||
Counts the amount of entries in it's input, returning an integer.
|
||||
command-description-map =
|
||||
Maps the input over the given block, with the provided expected return type.
|
||||
This command may be modified to not need an explicit return type in the future.
|
||||
command-description-select =
|
||||
Selects N objects or N% of objects from the input.
|
||||
One can additionally invert this command with not to make it select everything except N objects instead.
|
||||
command-description-comp =
|
||||
Returns the given component from the input entities, discarding entities without that component.
|
||||
command-description-delete =
|
||||
Deletes the input entities.
|
||||
command-description-ent =
|
||||
Returns the provided entity ID.
|
||||
command-description-entities =
|
||||
Returns all entities on the server.
|
||||
command-description-paused =
|
||||
Filters the input entities by whether or not they are paused.
|
||||
This command can be inverted with not.
|
||||
command-description-with =
|
||||
Filters the input entities by whether or not they have the given component.
|
||||
This command can be inverted with not.
|
||||
command-description-fuck =
|
||||
Throws an exception.
|
||||
command-description-ecscomp-listty =
|
||||
Lists every type of component registered.
|
||||
command-description-cd =
|
||||
Changes the session's current directory to the given relative or absolute path.
|
||||
command-description-ls-here =
|
||||
Lists the contents of the current directory.
|
||||
command-description-ls-in =
|
||||
Lists the contents of the given relative or absolute path.
|
||||
command-description-methods-get =
|
||||
Returns all methods associated with the input type.
|
||||
command-description-methods-overrides =
|
||||
Returns all methods overriden on the input type.
|
||||
command-description-methods-overridesfrom =
|
||||
Returns all methods overriden from the given type on the input type.
|
||||
command-description-cmd-moo =
|
||||
Asks the important questions.
|
||||
command-description-cmd-descloc =
|
||||
Returns the localization string for a command's description.
|
||||
command-description-cmd-getshim =
|
||||
Returns a command's execution shim.
|
||||
command-description-help =
|
||||
Provides a quick rundown of how to use toolshed.
|
||||
command-description-ioc-registered =
|
||||
Returns all the types registered with IoCManager on the current thread (usually the game thread)
|
||||
command-description-ioc-get =
|
||||
Gets an instance of an IoC registration.
|
||||
command-description-loc-tryloc =
|
||||
Tries to get a localization string, returning null if unable.
|
||||
command-description-loc-loc =
|
||||
Gets a localization string, returning the unlocalized string if unable.
|
||||
command-description-physics-angular_velocity =
|
||||
Returns the angular velocity of the given entities.
|
||||
command-description-vars =
|
||||
Provides a list of all variables set in this session.
|
||||
command-description-any =
|
||||
Returns true if there's any values in the input, otherwise false.
|
||||
command-description-ArrowCommand =
|
||||
Assigns the input to a variable.
|
||||
command-description-isempty =
|
||||
Returns true if the input is empty, otherwise false.
|
||||
command-description-isnull =
|
||||
Returns true if the input is null, otherwise false.
|
||||
command-description-unique =
|
||||
Filters the input sequence for uniqueness, removing duplicate values.
|
||||
command-description-where =
|
||||
Given some input sequence IEnumerable<T>, takes a block of signature T -> bool that decides if each input value should be included in the output sequence.
|
||||
command-description-do =
|
||||
Backwards compatibility with BQL, applies the given old commands over the input sequence.
|
||||
command-description-named =
|
||||
Filters the input entities by their name, with the regex ^selector$.
|
||||
command-description-prototyped =
|
||||
Filters the input entities by their prototype.
|
||||
command-description-nearby =
|
||||
Creates a new list of all entities nearby the inputs within the given range.
|
||||
command-description-first =
|
||||
Returns the first entry of the given enumerable.
|
||||
command-description-splat =
|
||||
"Splats" a block, value, or variable, creating N copies of it in a list.
|
||||
command-description-val =
|
||||
Casts the given value, block, or variable to the given type. This is mostly a workaround for current limitations of variables.
|
||||
command-description-actor-controlled =
|
||||
Filters entities by whether or not they're actively controlled.
|
||||
command-description-actor-session =
|
||||
Returns the sessions associated with the input entities.
|
||||
command-description-physics-parent =
|
||||
Returns the parent(s) of the input entities.
|
||||
command-description-emplace =
|
||||
Runs the given block over it's inputs, with the input value placed into the variable $value within the block.
|
||||
Additionally breaks out $wx, $wy, $proto, $desc, $name, and $paused for entities.
|
||||
Can also have breakout values for other types, consult the documentation for that type for further info.
|
||||
command-description-AddCommand =
|
||||
Performs numeric addition.
|
||||
command-description-SubtractCommand =
|
||||
Performs numeric subtraction.
|
||||
command-description-MultiplyCommand =
|
||||
Performs numeric multiplication.
|
||||
command-description-DivideCommand =
|
||||
Performs numeric division.
|
||||
command-description-min =
|
||||
Returns the minimum of two values.
|
||||
command-description-max =
|
||||
Returns the maximum of two values.
|
||||
command-description-BitAndCommand =
|
||||
Performs bitwise AND.
|
||||
command-description-BitOrCommand =
|
||||
Performs bitwise OR.
|
||||
command-description-BitXorCommand =
|
||||
Performs bitwise XOR.
|
||||
command-description-neg =
|
||||
Negates the input.
|
||||
command-description-GreaterThanCommand =
|
||||
Performs a greater-than comparison, x > y.
|
||||
command-description-LessThanCommand =
|
||||
Performs a less-than comparison, x < y.
|
||||
command-description-GreaterThanOrEqualCommand =
|
||||
Performs a greater-than-or-equal comparison, x >= y.
|
||||
command-description-LessThanOrEqualCommand =
|
||||
Performs a less-than-or-equal comparison, x <= y.
|
||||
command-description-EqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are equal.
|
||||
command-description-NotEqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are not equal.
|
||||
@@ -905,7 +905,7 @@ public class ArchetypeComponentAccessBenchmark
|
||||
var curLength = array.Length;
|
||||
if (curLength <= idx.Value)
|
||||
{
|
||||
var newLength = MathHelper.NextPowerOfTwo(Math.Max(8, idx.Value));
|
||||
var newLength = MathHelper.NextPowerOfTwo(System.Math.Max(8, idx.Value));
|
||||
Array.Resize(ref array, newLength);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
public class ComponentIteratorBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
|
||||
[UsedImplicitly]
|
||||
[Params(1, 10, 100, 1000)]
|
||||
public int N;
|
||||
|
||||
public A[] Comps = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterComponents(f => f.RegisterClass<A>())
|
||||
.InitializeInstance();
|
||||
|
||||
_entityManager = _simulation.Resolve<IEntityManager>();
|
||||
|
||||
Comps = new A[N+2];
|
||||
|
||||
var coords = new MapCoordinates(0, 0, new MapId(1));
|
||||
_simulation.AddMap(coords.MapId);
|
||||
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
var uid = _entityManager.SpawnEntity(null, coords);
|
||||
_entityManager.AddComponent<A>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public A[] ComponentStructEnumerator()
|
||||
{
|
||||
var query = _entityManager.EntityQueryEnumerator<A>();
|
||||
var i = 0;
|
||||
|
||||
while (query.MoveNext(out var comp))
|
||||
{
|
||||
Comps[i] = comp;
|
||||
i++;
|
||||
}
|
||||
|
||||
return Comps;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public A[] ComponentIEnumerable()
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
foreach (var comp in _entityManager.EntityQuery<A>())
|
||||
{
|
||||
Comps[i] = comp;
|
||||
i++;
|
||||
}
|
||||
|
||||
return Comps;
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,32 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Benchmarks.NumericsHelpers
|
||||
namespace Robust.Benchmarks.NumericsHelpers;
|
||||
|
||||
[Virtual]
|
||||
public class AddBenchmark
|
||||
{
|
||||
[Virtual]
|
||||
public class AddBenchmark
|
||||
[Params(32, 128)]
|
||||
public int N { get; set; }
|
||||
|
||||
[Params(1,2)]
|
||||
public int T { get; set; }
|
||||
|
||||
private float[] _inputA = default!;
|
||||
private float[] _inputB = default!;
|
||||
private float[] _output = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
[Params(32, 128)]
|
||||
public int N { get; set; }
|
||||
_inputA = new float[N];
|
||||
_inputB = new float[N];
|
||||
_output = new float[N];
|
||||
}
|
||||
|
||||
[Params(1,2)]
|
||||
public int T { get; set; }
|
||||
|
||||
private float[] _inputA = default!;
|
||||
private float[] _inputB = default!;
|
||||
private float[] _output = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_inputA = new float[N];
|
||||
_inputB = new float[N];
|
||||
_output = new float[N];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Bench()
|
||||
{
|
||||
Shared.Maths.NumericsHelpers.Add(_inputA, _inputB, _output);
|
||||
}
|
||||
[Benchmark]
|
||||
public void Bench()
|
||||
{
|
||||
Shared.Maths.NumericsHelpers.Add(_inputA, _inputB, _output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Robust.Build.Tasks
|
||||
Single = ts.GetType("System.Single");
|
||||
Int32 = ts.GetType("System.Int32");
|
||||
|
||||
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2", Single, 2);
|
||||
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("System.Numerics.Vector2", Single, 2);
|
||||
(Vector2i, Vector2iConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2i", Int32, 2);
|
||||
(Thickness, ThicknessConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Thickness", Single, 4);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
@@ -85,12 +85,10 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
_app = new RobustCefApp(_sawmill);
|
||||
|
||||
// We pass no main arguments...
|
||||
CefRuntime.Initialize(new CefMainArgs(null), settings, _app, IntPtr.Zero);
|
||||
|
||||
// TODO CEF: After this point, debugging breaks. No, literally. My client crashes but ONLY with the debugger.
|
||||
// I have tried using the DEBUG and RELEASE versions of libcef.so, stripped or non-stripped...
|
||||
// And nothing seemed to work. Odd.
|
||||
// So these arguments look like nonsense, but it turns out CEF is just *like that*.
|
||||
// The first argument is literally nonsense, but it needs to be there as otherwise the second argument doesn't apply
|
||||
// The second argument turns off CEF's bullshit error handling, which breaks dotnet's error handling.
|
||||
CefRuntime.Initialize(new CefMainArgs(new string[]{"binary","--disable-in-process-stack-traces"}), settings, _app, IntPtr.Zero);
|
||||
|
||||
if (_cfg.GetCVar(WCVars.WebResProtocol))
|
||||
{
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
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
|
||||
{
|
||||
@@ -117,7 +120,7 @@ namespace Robust.Client.Animations
|
||||
switch (a)
|
||||
{
|
||||
case Vector2 vector2:
|
||||
return Vector2.InterpolateCubic((Vector2) preA, vector2, (Vector2) b, (Vector2) postB, t);
|
||||
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);
|
||||
case Vector4 vector4:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Robust.Client.Audio;
|
||||
@@ -8,13 +8,17 @@ public sealed class AudioStream
|
||||
public TimeSpan Length { get; }
|
||||
internal ClydeHandle? ClydeHandle { get; }
|
||||
public string? Name { get; }
|
||||
public string? Title { get; }
|
||||
public string? Artist { get; }
|
||||
public int ChannelCount { get; }
|
||||
|
||||
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null)
|
||||
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null, string? title = null, string? artist = null)
|
||||
{
|
||||
ClydeHandle = handle;
|
||||
Length = length;
|
||||
ChannelCount = channelCount;
|
||||
Name = name;
|
||||
Title = title;
|
||||
Artist = artist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -15,7 +17,6 @@ public enum MidiRendererStatus : byte
|
||||
|
||||
public interface IMidiRenderer : IDisposable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The buffered audio source of this renderer.
|
||||
/// </summary>
|
||||
@@ -34,6 +35,7 @@ public interface IMidiRenderer : IDisposable
|
||||
/// <summary>
|
||||
/// This increases all note on velocities to 127.
|
||||
/// </summary>
|
||||
[Obsolete($"Use {nameof(VelocityOverride)} instead, you can set it to 127 to achieve the same effect.")]
|
||||
bool VolumeBoost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -94,6 +96,27 @@ public interface IMidiRenderer : IDisposable
|
||||
/// </summary>
|
||||
double SequencerTimeScale { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this renderer will subscribe to another and copy its events.
|
||||
/// See <see cref="FilteredChannels"/> to filter specific channels.
|
||||
/// </summary>
|
||||
IMidiRenderer? Master { get; set; }
|
||||
|
||||
// NOTE: Why is the properties below BitArray, you ask?
|
||||
// Well see, MIDI 2.0 supports up to 256(!) channels as opposed to MIDI 1.0's meekly 16 channels...
|
||||
// I'd like us to support MIDI 2.0 one day so I'm just future-proofing here. Also BitArray is cool!
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to filter out note events from certain channels.
|
||||
/// Only NoteOn will be filtered.
|
||||
/// </summary>
|
||||
BitArray FilteredChannels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to override all NoteOn velocities. Set to null to disable.
|
||||
/// </summary>
|
||||
byte? VelocityOverride { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start listening for midi input.
|
||||
/// </summary>
|
||||
@@ -120,6 +143,11 @@ public interface IMidiRenderer : IDisposable
|
||||
/// </summary>
|
||||
void StopAllNotes();
|
||||
|
||||
/// <summary>
|
||||
/// Reset renderer back to a clean state.
|
||||
/// </summary>
|
||||
void SystemReset();
|
||||
|
||||
/// <summary>
|
||||
/// Clears all scheduled events.
|
||||
/// </summary>
|
||||
@@ -156,7 +184,7 @@ public interface IMidiRenderer : IDisposable
|
||||
/// This is only used if <see cref="Mono"/> is set to True
|
||||
/// and <see cref="TrackingEntity"/> is null.
|
||||
/// </summary>
|
||||
EntityCoordinates? TrackingCoordinates { get; set; }
|
||||
MapCoordinates? TrackingCoordinates { get; set; }
|
||||
|
||||
MidiRendererState RendererState { get; }
|
||||
|
||||
@@ -164,7 +192,8 @@ public interface IMidiRenderer : IDisposable
|
||||
/// Send a midi event for the renderer to play.
|
||||
/// </summary>
|
||||
/// <param name="midiEvent">The midi event to be played</param>
|
||||
void SendMidiEvent(RobustMidiEvent midiEvent);
|
||||
/// <param name="raiseEvent">Whether to raise an event for this event.</param>
|
||||
void SendMidiEvent(RobustMidiEvent midiEvent, bool raiseEvent = true);
|
||||
|
||||
/// <summary>
|
||||
/// Schedule a MIDI event to be played at a later time.
|
||||
@@ -177,7 +206,7 @@ public interface IMidiRenderer : IDisposable
|
||||
/// <summary>
|
||||
/// Apply a certain state to the renderer.
|
||||
/// </summary>
|
||||
void ApplyState(MidiRendererState state);
|
||||
void ApplyState(MidiRendererState state, bool filterChannels = false);
|
||||
|
||||
/// <summary>
|
||||
/// Actually disposes of this renderer. Do NOT use outside the MIDI thread.
|
||||
|
||||
@@ -3,20 +3,27 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -26,6 +33,13 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
public const string SoundfontEnvironmentVariable = "ROBUST_SOUNDFONT_OVERRIDE";
|
||||
|
||||
private int _minRendererParallel;
|
||||
private float _occlusionUpdateDelay;
|
||||
private float _positionUpdateDelay;
|
||||
|
||||
[ViewVariables] private TimeSpan _nextOcclusionUpdate = TimeSpan.Zero;
|
||||
[ViewVariables] private TimeSpan _nextPositionUpdate = TimeSpan.Zero;
|
||||
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceCacheInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
@@ -33,6 +47,9 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
[Dependency] private readonly IClydeAudio _clydeAudio = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
[Dependency] private readonly ILogManager _logger = default!;
|
||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||
[Dependency] private readonly IRuntimeLog _runtime = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private SharedPhysicsSystem _broadPhaseSystem = default!;
|
||||
|
||||
@@ -59,11 +76,10 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<IMidiRenderer> _renderers = new();
|
||||
[ViewVariables] private readonly List<IMidiRenderer> _renderers = new();
|
||||
|
||||
private bool _alive = true;
|
||||
private Settings? _settings;
|
||||
[ViewVariables] private Settings? _settings;
|
||||
private Thread? _midiThread;
|
||||
private ISawmill _midiSawmill = default!;
|
||||
private float _volume = 0f;
|
||||
@@ -115,7 +131,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
private bool _failedInitialize;
|
||||
|
||||
private NFluidsynth.Logger.LoggerDelegate _loggerDelegate = default!;
|
||||
private ISawmill _sawmill = default!;
|
||||
private ISawmill _fluidsynthSawmill = default!;
|
||||
private float _maxCastLength;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -130,20 +146,28 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (FluidsynthInitialized || _failedInitialize) return;
|
||||
|
||||
_volume = _cfgMan.GetCVar(CVars.MidiVolume);
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
|
||||
{
|
||||
_volume = value;
|
||||
_volumeDirty = true;
|
||||
}, true);
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiMinRendererParallel,
|
||||
value => _minRendererParallel = value, true);
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiOcclusionUpdateDelay,
|
||||
value => _occlusionUpdateDelay = value, true);
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiPositionUpdateDelay,
|
||||
value => _positionUpdateDelay = value, true);
|
||||
|
||||
_midiSawmill = _logger.GetSawmill("midi");
|
||||
#if DEBUG
|
||||
_midiSawmill.Level = LogLevel.Debug;
|
||||
#else
|
||||
_midiSawmill.Level = LogLevel.Error;
|
||||
#endif
|
||||
_sawmill = _logger.GetSawmill("midi.fluidsynth");
|
||||
_fluidsynthSawmill = _logger.GetSawmill("midi.fluidsynth");
|
||||
_loggerDelegate = LoggerDelegate;
|
||||
|
||||
if (!_resourceManager.UserData.Exists(CustomSoundfontDirectory))
|
||||
@@ -167,8 +191,6 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
_settings["synth.lock-memory"].IntValue = 0;
|
||||
_settings["synth.threadsafe-api"].IntValue = 1;
|
||||
_settings["synth.gain"].DoubleValue = 1.0d;
|
||||
_settings["synth.polyphony"].IntValue = 1024;
|
||||
_settings["synth.cpu-cores"].IntValue = 2;
|
||||
_settings["synth.midi-channels"].IntValue = 16;
|
||||
_settings["synth.overflow.age"].DoubleValue = 3000;
|
||||
_settings["audio.driver"].StringValue = "file";
|
||||
@@ -176,8 +198,11 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
_settings["audio.period-size"].IntValue = 4096;
|
||||
_settings["midi.autoconnect"].IntValue = 1;
|
||||
_settings["player.reset-synth"].IntValue = 0;
|
||||
_settings["synth.midi-channels"].IntValue = Math.Clamp(RobustMidiEvent.MaxChannels, 16, 256);
|
||||
_settings["synth.midi-bank-select"].StringValue = "gm";
|
||||
//_settings["synth.verbose"].IntValue = 1; // Useful for debugging.
|
||||
|
||||
_parallel.AddAndInvokeParallelCountChanged(UpdateParallelCount);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -195,6 +220,18 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
FluidsynthInitialized = true;
|
||||
}
|
||||
|
||||
private void UpdateParallelCount()
|
||||
{
|
||||
if (_settings == null)
|
||||
return;
|
||||
|
||||
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(_parallel.ParallelProcessCount) * 2048), 1, 65535);
|
||||
_settings["synth.cpu-cores"].IntValue = Math.Clamp(_parallel.ParallelProcessCount, 1, 256);
|
||||
|
||||
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
|
||||
_midiSawmill.Debug($"Synth Polyphony: {_settings["synth.polyphony"].IntValue}");
|
||||
}
|
||||
|
||||
private void OnRaycastLengthChanged(float value)
|
||||
{
|
||||
_maxCastLength = value;
|
||||
@@ -211,7 +248,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
NFluidsynth.Logger.LogLevel.Debug => LogLevel.Debug,
|
||||
_ => LogLevel.Debug
|
||||
};
|
||||
_sawmill.Log(rLevel, message);
|
||||
_fluidsynthSawmill.Log(rLevel, message);
|
||||
}
|
||||
|
||||
public IMidiRenderer? GetNewRenderer(bool mono = true)
|
||||
@@ -238,7 +275,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
var renderer = new MidiRenderer(_settings!, soundfontLoader, mono, this, _clydeAudio, _taskManager, _midiSawmill);
|
||||
|
||||
_midiSawmill.Debug($"Loading soundfont {FallbackSoundfont}");
|
||||
_midiSawmill.Debug($"Loading fallback soundfont {FallbackSoundfont}");
|
||||
// Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont.
|
||||
renderer.LoadSoundfont(FallbackSoundfont);
|
||||
|
||||
@@ -252,8 +289,8 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
try
|
||||
{
|
||||
_midiSawmill.Debug($"Loading OS soundfont {filepath}");
|
||||
renderer.LoadSoundfont(filepath);
|
||||
_midiSawmill.Debug($"Loaded Linux soundfont {filepath}");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -267,7 +304,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (File.Exists(OsxSoundfont) && SoundFont.IsSoundFont(OsxSoundfont))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading soundfont {OsxSoundfont}");
|
||||
_midiSawmill.Debug($"Loading OS soundfont {OsxSoundfont}");
|
||||
renderer.LoadSoundfont(OsxSoundfont);
|
||||
}
|
||||
}
|
||||
@@ -275,7 +312,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (File.Exists(WindowsSoundfont) && SoundFont.IsSoundFont(WindowsSoundfont))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading soundfont {WindowsSoundfont}");
|
||||
_midiSawmill.Debug($"Loading OS soundfont {WindowsSoundfont}");
|
||||
renderer.LoadSoundfont(WindowsSoundfont);
|
||||
}
|
||||
}
|
||||
@@ -286,27 +323,31 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (File.Exists(soundfontOverride) && SoundFont.IsSoundFont(soundfontOverride))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading soundfont {soundfontOverride} from environment variable.");
|
||||
_midiSawmill.Debug($"Loading environment variable soundfont {soundfontOverride}");
|
||||
renderer.LoadSoundfont(soundfontOverride);
|
||||
}
|
||||
}
|
||||
|
||||
// Load content-specific custom soundfonts, which should override the system/fallback soundfont.
|
||||
_midiSawmill.Debug($"Loading soundfonts from {ContentCustomSoundfontDirectory}");
|
||||
_midiSawmill.Debug($"Loading soundfonts from content directory {ContentCustomSoundfontDirectory}");
|
||||
foreach (var file in _resourceManager.ContentFindFiles(ContentCustomSoundfontDirectory))
|
||||
{
|
||||
if (file.Extension != "sf2" && file.Extension != "dls" && file.Extension != "sf3") continue;
|
||||
_midiSawmill.Debug($"Loading soundfont {file}");
|
||||
_midiSawmill.Debug($"Loading content soundfont {file}");
|
||||
renderer.LoadSoundfont(file.ToString());
|
||||
}
|
||||
|
||||
var userDataPath = _resourceManager.UserData.RootDir == null
|
||||
? CustomSoundfontDirectory
|
||||
: new ResPath(_resourceManager.UserData.RootDir) / CustomSoundfontDirectory.ToRelativePath();
|
||||
|
||||
// Load every soundfont from the user data directory last, since those may override any other soundfont.
|
||||
_midiSawmill.Debug($"Loading soundfonts from {{USERDATA}} {CustomSoundfontDirectory}");
|
||||
var enumerator = _resourceManager.UserData.Find($"{CustomSoundfontDirectory.ToRelativePath()}/*").Item1;
|
||||
_midiSawmill.Debug($"Loading soundfonts from user data directory {userDataPath}");
|
||||
var enumerator = _resourceManager.UserData.Find($"{CustomSoundfontDirectory.ToRelativePath()}*").Item1;
|
||||
foreach (var file in enumerator)
|
||||
{
|
||||
if (file.Extension != "sf2" && file.Extension != "dls" && file.Extension != "sf3") continue;
|
||||
_midiSawmill.Debug($"Loading soundfont {{USERDATA}} {file}");
|
||||
_midiSawmill.Debug($"Loading user soundfont {file}");
|
||||
renderer.LoadSoundfont(file.ToString());
|
||||
}
|
||||
|
||||
@@ -336,72 +377,108 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
lock (_renderers)
|
||||
{
|
||||
foreach (var renderer in _renderers)
|
||||
if (_renderers.Count == 0)
|
||||
return;
|
||||
|
||||
var transQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
|
||||
var opts = new ParallelOptions { MaxDegreeOfParallelism = _parallel.ParallelProcessCount };
|
||||
|
||||
if (_renderers.Count > _minRendererParallel)
|
||||
{
|
||||
if (renderer.Disposed)
|
||||
continue;
|
||||
|
||||
if(_volumeDirty)
|
||||
renderer.Source.SetVolume(Volume);
|
||||
|
||||
if (!renderer.Mono)
|
||||
Parallel.ForEach(_renderers, opts, renderer => UpdateRenderer(renderer, transQuery, physicsQuery));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.Source.SetGlobal();
|
||||
continue;
|
||||
}
|
||||
|
||||
MapCoordinates? mapPos = null;
|
||||
var trackingEntity = renderer.TrackingEntity != null && !_entityManager.Deleted(renderer.TrackingEntity);
|
||||
if (trackingEntity)
|
||||
{
|
||||
renderer.TrackingCoordinates = _entityManager.GetComponent<TransformComponent>(renderer.TrackingEntity!.Value).Coordinates;
|
||||
}
|
||||
|
||||
if (renderer.TrackingCoordinates != null)
|
||||
{
|
||||
mapPos = renderer.TrackingCoordinates.Value.ToMap(_entityManager);
|
||||
}
|
||||
|
||||
if (mapPos != null && mapPos.Value.MapId == _eyeManager.CurrentMap)
|
||||
{
|
||||
var pos = mapPos.Value;
|
||||
|
||||
var sourceRelative = pos.Position - _eyeManager.CurrentEye.Position.Position;
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length > 0)
|
||||
{
|
||||
occlusion = _broadPhaseSystem.IntersectRayPenetration(
|
||||
pos.MapId,
|
||||
new CollisionRay(
|
||||
_eyeManager.CurrentEye.Position.Position,
|
||||
sourceRelative.Normalized,
|
||||
OcclusionCollisionMask),
|
||||
MathF.Min(sourceRelative.Length, _maxCastLength),
|
||||
renderer.TrackingEntity);
|
||||
}
|
||||
|
||||
renderer.Source.SetOcclusion(occlusion);
|
||||
|
||||
if (!renderer.Source.SetPosition(pos.Position))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (trackingEntity)
|
||||
{
|
||||
var vel = _broadPhaseSystem.GetMapLinearVelocity(renderer.TrackingEntity!.Value);
|
||||
renderer.Source.SetVelocity(vel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Source.SetOcclusion(float.MaxValue);
|
||||
UpdateRenderer(renderer, transQuery, physicsQuery);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_nextOcclusionUpdate < _timing.RealTime)
|
||||
_nextOcclusionUpdate = _timing.RealTime.Add(TimeSpan.FromSeconds(_occlusionUpdateDelay));
|
||||
|
||||
if (_nextPositionUpdate < _timing.RealTime)
|
||||
_nextPositionUpdate = _timing.RealTime.Add(TimeSpan.FromSeconds(_positionUpdateDelay));
|
||||
|
||||
_volumeDirty = false;
|
||||
}
|
||||
private void UpdateRenderer(IMidiRenderer renderer, EntityQuery<TransformComponent> transQuery,
|
||||
EntityQuery<PhysicsComponent> physicsQuery)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (renderer.Disposed)
|
||||
return;
|
||||
|
||||
if (_volumeDirty)
|
||||
renderer.Source.SetVolume(Volume);
|
||||
|
||||
if (!renderer.Mono)
|
||||
{
|
||||
renderer.Source.SetGlobal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nextPositionUpdate < _timing.RealTime)
|
||||
{
|
||||
if (renderer.TrackingEntity is {} trackedEntity && !_entityManager.Deleted(trackedEntity))
|
||||
{
|
||||
renderer.TrackingCoordinates = transQuery.GetComponent(renderer.TrackingEntity!.Value).MapPosition;
|
||||
}
|
||||
else if (renderer.TrackingCoordinates == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!renderer.Source.SetPosition(renderer.TrackingCoordinates.Value.Position))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vel = _broadPhaseSystem.GetMapLinearVelocity(renderer.TrackingEntity!.Value,
|
||||
xformQuery: transQuery, physicsQuery: physicsQuery);
|
||||
renderer.Source.SetVelocity(vel);
|
||||
}
|
||||
|
||||
if (renderer.TrackingCoordinates != null && renderer.TrackingCoordinates.Value.MapId == _eyeManager.CurrentMap)
|
||||
{
|
||||
if (_nextOcclusionUpdate >= _timing.RealTime)
|
||||
return;
|
||||
|
||||
var pos = renderer.TrackingCoordinates.Value;
|
||||
|
||||
var sourceRelative = pos.Position - _eyeManager.CurrentEye.Position.Position;
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length() > 0)
|
||||
{
|
||||
occlusion = _broadPhaseSystem.IntersectRayPenetration(
|
||||
pos.MapId,
|
||||
new CollisionRay(
|
||||
_eyeManager.CurrentEye.Position.Position,
|
||||
sourceRelative.Normalized(),
|
||||
OcclusionCollisionMask),
|
||||
MathF.Min(sourceRelative.Length(), _maxCastLength),
|
||||
renderer.TrackingEntity);
|
||||
}
|
||||
|
||||
renderer.Source.SetOcclusion(occlusion);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Source.SetOcclusion(float.MaxValue);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_runtime.LogException(ex, _midiSawmill.Name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main method for the thread rendering the midi audio.
|
||||
@@ -416,7 +493,12 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
var renderer = _renderers[i];
|
||||
if (!renderer.Disposed)
|
||||
{
|
||||
if (renderer.Master is { Disposed: true })
|
||||
renderer.Master = null;
|
||||
|
||||
renderer.Render();
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.InternalDispose();
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using JetBrains.Annotations;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Asynchronous;
|
||||
@@ -9,7 +11,6 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
@@ -21,14 +22,13 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
// TODO: Make this a replicated CVar in MidiManager
|
||||
private const int MidiSizeLimit = 2000000;
|
||||
private const double BytesToMegabytes = 0.000001d;
|
||||
private const int ChannelCount = 16;
|
||||
private const int ChannelCount = RobustMidiEvent.MaxChannels;
|
||||
|
||||
private readonly ISawmill _midiSawmill;
|
||||
|
||||
private readonly Settings _settings;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private bool _debugEvents = false;
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _debugEvents = false;
|
||||
|
||||
// Kept around to avoid the loader callbacks getting GC'd
|
||||
// ReSharper disable once NotAccessedField.Local
|
||||
@@ -48,8 +48,9 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
private readonly SequencerClientId _robustRegister;
|
||||
private readonly SequencerClientId _debugRegister;
|
||||
|
||||
[ViewVariables]
|
||||
private MidiRendererState _rendererState = new();
|
||||
[ViewVariables] private MidiRendererState _rendererState = new();
|
||||
|
||||
private IMidiRenderer? _master;
|
||||
public MidiRendererState RendererState => _rendererState;
|
||||
public IClydeBufferedAudioSource Source { get; set; }
|
||||
IClydeBufferedAudioSource IMidiRenderer.Source => Source;
|
||||
@@ -70,8 +71,8 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
{
|
||||
for (byte i = 0; i < ChannelCount; i++)
|
||||
{
|
||||
// Channel 9 is the percussion channel. Let's not change its instrument...
|
||||
if (i == 9)
|
||||
// Don't change percussion channel instrument.
|
||||
if (i == RobustMidiEvent.PercussionChannel)
|
||||
continue;
|
||||
|
||||
SendMidiEvent(RobustMidiEvent.ProgramChange(i, value, SequencerTick));
|
||||
@@ -96,11 +97,14 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
{
|
||||
for (byte i = 0; i < ChannelCount; i++)
|
||||
{
|
||||
// Channel 9 is the percussion channel. Let's not change its bank...
|
||||
if (i == 9)
|
||||
// Don't change percussion channel bank.
|
||||
if (i == RobustMidiEvent.PercussionChannel)
|
||||
continue;
|
||||
|
||||
SendMidiEvent(RobustMidiEvent.BankSelect(i, value, SequencerTick));
|
||||
|
||||
// Re-select program.
|
||||
SendMidiEvent(RobustMidiEvent.ProgramChange(i, _midiProgram, SequencerTick));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +132,11 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool DisablePercussionChannel { get; set; } = true;
|
||||
public bool DisablePercussionChannel
|
||||
{
|
||||
get => FilteredChannels[RobustMidiEvent.PercussionChannel];
|
||||
set => FilteredChannels[RobustMidiEvent.PercussionChannel] = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool DisableProgramChangeEvent { get; set; } = true;
|
||||
@@ -181,13 +189,62 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool VolumeBoost { get; set; }
|
||||
[Obsolete($"Use {nameof(VelocityOverride)} instead, you can set it to 127 to achieve the same effect.")]
|
||||
public bool VolumeBoost
|
||||
{
|
||||
get => VelocityOverride == 127;
|
||||
set => VelocityOverride = value ? 127 : null;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityUid? TrackingEntity { get; set; } = null;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityCoordinates? TrackingCoordinates { get; set; } = null;
|
||||
public MapCoordinates? TrackingCoordinates { get; set; } = null;
|
||||
|
||||
[ViewVariables]
|
||||
public BitArray FilteredChannels { get; } = new(RobustMidiEvent.MaxChannels);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public byte? VelocityOverride { get; set; } = null;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public IMidiRenderer? Master
|
||||
{
|
||||
get => _master;
|
||||
set
|
||||
{
|
||||
if (value == _master)
|
||||
return;
|
||||
|
||||
if (_master is { Disposed: false })
|
||||
{
|
||||
try
|
||||
{
|
||||
_master.OnMidiEvent -= SendMidiEvent;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
_master = value;
|
||||
|
||||
if (_master == null)
|
||||
return;
|
||||
|
||||
_master.OnMidiEvent += SendMidiEvent;
|
||||
ApplyState(_master.RendererState, true);
|
||||
MidiBank = _midiBank;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables, UsedImplicitly]
|
||||
private double CpuLoad => !_synth.Disposed ? _synth.CpuLoad : 0;
|
||||
|
||||
public event Action<RobustMidiEvent>? OnMidiEvent;
|
||||
public event Action? OnMidiPlayerFinished;
|
||||
|
||||
internal MidiRenderer(Settings settings, SoundFontLoader soundFontLoader, bool mono,
|
||||
IMidiManager midiManager, IClydeAudio clydeAudio, ITaskManager taskManager, ISawmill midiSawmill)
|
||||
@@ -354,6 +411,11 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
public void SystemReset()
|
||||
{
|
||||
SendMidiEvent(RobustMidiEvent.SystemReset(SequencerTick));
|
||||
}
|
||||
|
||||
public void ClearAllEvents()
|
||||
{
|
||||
_sequencer.RemoveEvents(SequencerClientId.Wildcard, SequencerClientId.Wildcard, -1);
|
||||
@@ -368,9 +430,6 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<RobustMidiEvent>? OnMidiEvent;
|
||||
public event Action? OnMidiPlayerFinished;
|
||||
|
||||
void IMidiRenderer.Render()
|
||||
{
|
||||
Render();
|
||||
@@ -432,7 +491,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
if (!Source.IsPlaying) Source.StartPlaying();
|
||||
}
|
||||
|
||||
public void ApplyState(MidiRendererState state)
|
||||
public void ApplyState(MidiRendererState state, bool filterChannels = false)
|
||||
{
|
||||
lock (_playerStateLock)
|
||||
{
|
||||
@@ -440,6 +499,9 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
|
||||
for (var channel = 0; channel < ChannelCount; channel++)
|
||||
{
|
||||
if (filterChannels && !FilteredChannels[channel])
|
||||
continue;
|
||||
|
||||
_synth.AllNotesOff(channel);
|
||||
|
||||
_synth.PitchBend(channel, state.PitchBend.AsSpan[channel]);
|
||||
@@ -462,7 +524,8 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
_synth.ProgramChange(channel, state.Program.AsSpan[channel]);
|
||||
var program = DisableProgramChangeEvent ? MidiProgram : state.Program.AsSpan[channel];
|
||||
_synth.ProgramChange(channel, program);
|
||||
|
||||
for (var key = 0; key < state.NoteVelocities.AsSpan[channel].AsSpan.Length; key++)
|
||||
{
|
||||
@@ -487,7 +550,12 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
public void SendMidiEvent(RobustMidiEvent midiEvent)
|
||||
private void SendMidiEvent(RobustMidiEvent midiEvent)
|
||||
{
|
||||
SendMidiEvent(midiEvent, true);
|
||||
}
|
||||
|
||||
public void SendMidiEvent(RobustMidiEvent midiEvent, bool raiseEvent)
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
@@ -505,11 +573,10 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.NoteOn:
|
||||
// Channel 9 is the percussion channel. We only block NoteOn events to it.
|
||||
if (DisablePercussionChannel && midiEvent.Channel == 9)
|
||||
return;
|
||||
if (FilteredChannels[midiEvent.Channel])
|
||||
break;
|
||||
|
||||
var velocity = (byte)(VolumeBoost ? 127 : midiEvent.Velocity);
|
||||
var velocity = VelocityOverride ?? midiEvent.Velocity;
|
||||
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = velocity;
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, velocity);
|
||||
@@ -523,7 +590,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
case RobustMidiCommand.ControlChange:
|
||||
// CC0 is bank selection
|
||||
if (midiEvent.Control == 0x0 && DisableProgramChangeEvent)
|
||||
return;
|
||||
break;
|
||||
|
||||
_rendererState.Controllers.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Control] = midiEvent.Value;
|
||||
if(midiEvent.Control != 0x0)
|
||||
@@ -534,7 +601,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
|
||||
case RobustMidiCommand.ProgramChange:
|
||||
if (DisableProgramChangeEvent)
|
||||
return;
|
||||
break;
|
||||
|
||||
_rendererState.Program.AsSpan[midiEvent.Channel] = midiEvent.Program;
|
||||
_synth.ProgramChange(midiEvent.Channel, midiEvent.Program);
|
||||
@@ -561,14 +628,14 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
switch (midiEvent.Control)
|
||||
{
|
||||
case 0x0 when midiEvent.Status == 0xFF:
|
||||
_rendererState = new ();
|
||||
_rendererState = new MidiRendererState();
|
||||
_synth.SystemReset();
|
||||
|
||||
// Reset the instrument to the one we were using.
|
||||
if (DisableProgramChangeEvent)
|
||||
{
|
||||
MidiProgram = _midiProgram;
|
||||
MidiBank = _midiBank;
|
||||
MidiProgram = _midiProgram;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -597,7 +664,10 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
//_midiSawmill.Error("Exception while sending midi event of type {0}: {1}", midiEvent.Type, e, midiEvent);
|
||||
}
|
||||
|
||||
_taskManager.RunOnMainThread(() => OnMidiEvent?.Invoke(midiEvent));
|
||||
if (raiseEvent)
|
||||
{
|
||||
_taskManager.RunOnMainThread(() => OnMidiEvent?.Invoke(midiEvent));
|
||||
}
|
||||
}
|
||||
|
||||
public void ScheduleMidiEvent(RobustMidiEvent midiEvent, uint time, bool absolute = false)
|
||||
@@ -633,6 +703,9 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
/// <inheritdoc />
|
||||
void IMidiRenderer.InternalDispose()
|
||||
{
|
||||
OnMidiEvent = null;
|
||||
OnMidiPlayerFinished = null;
|
||||
|
||||
Source?.Dispose();
|
||||
_driver?.Dispose();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
@@ -12,7 +13,7 @@ public struct MidiRendererState
|
||||
internal FixedArray16<byte> ChannelPressure;
|
||||
internal FixedArray16<ushort> PitchBend;
|
||||
|
||||
internal Span<byte> AsSpan => MemoryMarshal.CreateSpan(ref NoteVelocities._00._00, 4160);
|
||||
[ViewVariables] internal Span<byte> AsSpan => MemoryMarshal.CreateSpan(ref NoteVelocities._00._00, 4160);
|
||||
|
||||
static unsafe MidiRendererState()
|
||||
{
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace Robust.Client
|
||||
deps.Register<IReplayLoadManager, ReplayLoadManager>();
|
||||
deps.Register<IReplayPlaybackManager, ReplayPlaybackManager>();
|
||||
deps.Register<IReplayRecordingManager, ReplayRecordingManager>();
|
||||
deps.Register<IReplayRecordingManagerInternal, ReplayRecordingManager>();
|
||||
deps.Register<IClientGameStateManager, ClientGameStateManager>();
|
||||
deps.Register<IBaseClient, BaseClient>();
|
||||
deps.Register<IPlayerManager, PlayerManager>();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -18,8 +19,9 @@ public sealed class LightTreeSystem : ComponentTreeSystem<LightTreeComponent, Po
|
||||
{
|
||||
// Really we should be rotating the light offset by the relative rotation. But I assume the light offset will
|
||||
// always be relatively small, so fuck it, this is probably faster than having to compute the angle every time.
|
||||
var radius = entry.Component.Radius + entry.Component.Offset.Length;
|
||||
return new Box2(pos - radius, pos + radius);
|
||||
var radius = entry.Component.Radius + entry.Component.Offset.Length();
|
||||
var radiusVec = new Vector2(radius, radius);
|
||||
return new Box2(pos - radiusVec, pos + radiusVec);
|
||||
}
|
||||
|
||||
protected override Box2 ExtractAabb(in ComponentTreeEntry<PointLightComponent> entry)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -14,7 +14,7 @@ internal sealed partial class ClientConsoleHost
|
||||
private int _completionSeq;
|
||||
|
||||
|
||||
public async Task<CompletionResult> GetCompletions(List<string> args, CancellationToken cancel)
|
||||
public async Task<CompletionResult> GetCompletions(List<string> args, string argStr, CancellationToken cancel)
|
||||
{
|
||||
// Last element is the command currently being typed. May be empty.
|
||||
|
||||
@@ -24,10 +24,10 @@ internal sealed partial class ClientConsoleHost
|
||||
if (delay > 0)
|
||||
await Task.Delay((int)(delay * 1000), cancel);
|
||||
|
||||
return await CalcCompletions(args, cancel);
|
||||
return await CalcCompletions(args, argStr, cancel);
|
||||
}
|
||||
|
||||
private Task<CompletionResult> CalcCompletions(List<string> args, CancellationToken cancel)
|
||||
private Task<CompletionResult> CalcCompletions(List<string> args, string argStr, CancellationToken cancel)
|
||||
{
|
||||
if (args.Count == 1)
|
||||
{
|
||||
@@ -44,10 +44,10 @@ internal sealed partial class ClientConsoleHost
|
||||
if (!AvailableCommands.TryGetValue(args[0], out var cmd))
|
||||
return Task.FromResult(CompletionResult.Empty);
|
||||
|
||||
return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], cancel).AsTask();
|
||||
return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], argStr, cancel).AsTask();
|
||||
}
|
||||
|
||||
private Task<CompletionResult> DoServerCompletions(List<string> args, CancellationToken cancel)
|
||||
private Task<CompletionResult> DoServerCompletions(List<string> args, string argStr, CancellationToken cancel)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<CompletionResult>();
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel);
|
||||
@@ -62,6 +62,7 @@ internal sealed partial class ClientConsoleHost
|
||||
var msg = new MsgConCompletion
|
||||
{
|
||||
Args = args.ToArray(),
|
||||
ArgString = argStr,
|
||||
Seq = seq
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -21,13 +22,13 @@ namespace Robust.Client.Console
|
||||
{
|
||||
public sealed class AddStringArgs : EventArgs
|
||||
{
|
||||
public string Text { get; }
|
||||
public FormattedMessage Text { get; }
|
||||
|
||||
public bool Local { get; }
|
||||
|
||||
public bool Error { get; }
|
||||
|
||||
public AddStringArgs(string text, bool local, bool error)
|
||||
public AddStringArgs(FormattedMessage text, bool local, bool error)
|
||||
{
|
||||
Text = text;
|
||||
Local = local;
|
||||
@@ -132,10 +133,17 @@ namespace Robust.Client.Console
|
||||
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
|
||||
}
|
||||
|
||||
public override void WriteLine(ICommonSession? session, FormattedMessage msg)
|
||||
{
|
||||
AddFormattedLine(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteError(ICommonSession? session, string text)
|
||||
{
|
||||
OutputText(text, true, true);
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(text);
|
||||
OutputText(msg, true, true);
|
||||
}
|
||||
|
||||
public bool IsCmdServer(IConsoleCommand cmd)
|
||||
@@ -151,8 +159,13 @@ namespace Robust.Client.Console
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
return;
|
||||
|
||||
WriteLine(null, "");
|
||||
var msg = new FormattedMessage();
|
||||
msg.PushColor(Color.Gold);
|
||||
msg.AddText("> " + command);
|
||||
msg.Pop();
|
||||
// echo the command locally
|
||||
WriteLine(null, "> " + command);
|
||||
OutputText(msg, true, false);
|
||||
|
||||
//Commands are processed locally and then sent to the server to be processed there again.
|
||||
var args = new List<string>();
|
||||
@@ -205,7 +218,9 @@ namespace Robust.Client.Console
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine(ICommonSession? session, string text)
|
||||
{
|
||||
OutputText(text, true, false);
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(text);
|
||||
OutputText(msg, true, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -214,12 +229,12 @@ namespace Robust.Client.Console
|
||||
// We don't have anything to dispose.
|
||||
}
|
||||
|
||||
private void OutputText(string text, bool local, bool error)
|
||||
private void OutputText(FormattedMessage text, bool local, bool error)
|
||||
{
|
||||
AddString?.Invoke(this, new AddStringArgs(text, local, error));
|
||||
|
||||
var level = error ? LogLevel.Warning : LogLevel.Info;
|
||||
_conLogger.Log(level, text);
|
||||
_conLogger.Log(level, text.ToString());
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
|
||||
@@ -229,7 +244,7 @@ namespace Robust.Client.Console
|
||||
|
||||
private void HandleConCmdAck(MsgConCmdAck msg)
|
||||
{
|
||||
OutputText("< " + msg.Text, false, msg.Error);
|
||||
OutputText(msg.Text, false, msg.Error);
|
||||
}
|
||||
|
||||
private void HandleConCmdReg(MsgConCmdReg msg)
|
||||
@@ -303,13 +318,14 @@ namespace Robust.Client.Console
|
||||
public async ValueTask<CompletionResult> GetCompletionAsync(
|
||||
IConsoleShell shell,
|
||||
string[] args,
|
||||
string argStr,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var host = (ClientConsoleHost)shell.ConsoleHost;
|
||||
var argsList = args.ToList();
|
||||
argsList.Insert(0, Command);
|
||||
|
||||
return await host.DoServerCompletions(argsList, cancel);
|
||||
return await host.DoServerCompletions(argsList, argStr, cancel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,10 +343,11 @@ namespace Robust.Client.Console
|
||||
public override async ValueTask<CompletionResult> GetCompletionAsync(
|
||||
IConsoleShell shell,
|
||||
string[] args,
|
||||
string argStr,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var host = (ClientConsoleHost)shell.ConsoleHost;
|
||||
return await host.DoServerCompletions(args.ToList(), cancel);
|
||||
return await host.DoServerCompletions(args.ToList(), argStr[">".Length..], cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,6 +621,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class ChunkInfoCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
[Dependency] private readonly IInputManager _input = default!;
|
||||
@@ -631,16 +632,17 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
var mousePos = _eye.ScreenToMap(_input.MouseScreenPosition);
|
||||
|
||||
if (!_map.TryFindGridAt(mousePos, out _, out var grid))
|
||||
if (!_map.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||
{
|
||||
shell.WriteLine("No grid under your mouse cursor.");
|
||||
return;
|
||||
}
|
||||
|
||||
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
|
||||
var chunk = grid.GetOrAddChunk(chunkIndex);
|
||||
var mapSystem = _entManager.System<SharedMapSystem>();
|
||||
var chunkIndex = mapSystem.LocalToChunkIndices(gridUid, grid, grid.MapToGrid(mousePos));
|
||||
var chunk = mapSystem.GetOrAddChunk(gridUid, grid, chunkIndex);
|
||||
|
||||
shell.WriteLine($"worldBounds: {grid.CalcWorldAABB(chunk)} localBounds: {chunk.CachedBounds}");
|
||||
shell.WriteLine($"worldBounds: {mapSystem.CalcWorldAABB(gridUid, grid, chunk)} localBounds: {chunk.CachedBounds}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -87,7 +88,7 @@ internal sealed partial class UITestControl
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.Scale = (1, 0.75f);
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
@@ -102,9 +103,9 @@ internal sealed partial class UITestControl
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = (64, 64);
|
||||
e.View.SetSize = new(64, 64);
|
||||
e.View.Stretch = SpriteView.StretchMode.None;
|
||||
e.View.Scale = (1, 0.75f);
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
@@ -119,9 +120,9 @@ internal sealed partial class UITestControl
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = (64, 64);
|
||||
e.View.SetSize = new(64, 64);
|
||||
e.View.Stretch = SpriteView.StretchMode.Fit;
|
||||
e.View.Scale = (1, 0.75f);
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
@@ -136,9 +137,9 @@ internal sealed partial class UITestControl
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = (300, 300);
|
||||
e.View.SetSize = new(300, 300);
|
||||
e.View.Stretch = SpriteView.StretchMode.Fill;
|
||||
e.View.Scale = (1, 0.75f);
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
@@ -153,9 +154,9 @@ internal sealed partial class UITestControl
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = (300, 300);
|
||||
e.View.SetSize = new(300, 300);
|
||||
e.View.Stretch = SpriteView.StretchMode.Fill;
|
||||
e.View.Scale = (1, 0.75f);
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
}
|
||||
}
|
||||
@@ -192,7 +193,7 @@ internal sealed partial class UITestControl
|
||||
|
||||
entry = AddEntry("Offset", (e, time) =>
|
||||
{
|
||||
e.Sprite.Offset = (MathF.Sin((float) Angle.FromDegrees(time * _degreesPerSecond)), 0);
|
||||
e.Sprite.Offset = new Vector2(MathF.Sin((float) Angle.FromDegrees(time * _degreesPerSecond)), 0);
|
||||
e.View.InvalidateMeasure();
|
||||
});
|
||||
added.Add(entry);
|
||||
@@ -200,7 +201,7 @@ internal sealed partial class UITestControl
|
||||
entry = AddEntry("Scaled", (e, time) =>
|
||||
{
|
||||
var theta = (float) Angle.FromDegrees(_degreesPerSecond * time).Theta;
|
||||
e.Sprite.Scale = Vector2.One + (0.5f * MathF.Sin(theta), 0.5f * MathF.Cos(theta));
|
||||
e.Sprite.Scale = Vector2.One + new Vector2(0.5f * MathF.Sin(theta), 0.5f * MathF.Cos(theta));
|
||||
e.View.InvalidateMeasure();
|
||||
});
|
||||
added.Add(entry);
|
||||
@@ -214,8 +215,8 @@ internal sealed partial class UITestControl
|
||||
entry = AddEntry("Combination", (e, time) =>
|
||||
{
|
||||
var theta = (float) Angle.FromDegrees(_degreesPerSecond * time * 2).Theta;
|
||||
e.Sprite.Scale = Vector2.One + (0.5f * MathF.Sin(theta), 0.5f * MathF.Cos(theta));
|
||||
e.Sprite.Offset = (MathF.Sin((float) Angle.FromDegrees(time * _degreesPerSecond)), 0);
|
||||
e.Sprite.Scale = Vector2.One + new Vector2(0.5f * MathF.Sin(theta), 0.5f * MathF.Cos(theta));
|
||||
e.Sprite.Offset = new(MathF.Sin((float) Angle.FromDegrees(time * _degreesPerSecond)), 0);
|
||||
e.Sprite.Rotation = Angle.FromDegrees(0.5 * time * _degreesPerSecond);
|
||||
e.Transform.LocalRotation = Angle.FromDegrees(0.25 * time * _degreesPerSecond);
|
||||
e.View.InvalidateMeasure();
|
||||
|
||||
@@ -89,7 +89,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
{
|
||||
grid.AddChild(new Button
|
||||
{
|
||||
MinSize = (50, 50),
|
||||
MinSize = new(50, 50),
|
||||
Text = $"{x}, {y}"
|
||||
});
|
||||
}
|
||||
@@ -235,7 +235,7 @@ internal sealed class UITestCommand : LocalizedCommands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var window = new DefaultWindow { MinSize = (800, 600) };
|
||||
var window = new DefaultWindow { MinSize = new(800, 600) };
|
||||
var control = new UITestControl();
|
||||
window.OnClose += control.OnClosed;
|
||||
window.Contents.AddChild(control);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Input;
|
||||
|
||||
@@ -19,6 +19,6 @@ namespace Robust.Client.Console
|
||||
|
||||
void AddFormattedLine(FormattedMessage message);
|
||||
|
||||
Task<CompletionResult> GetCompletions(List<string> args, CancellationToken cancel);
|
||||
Task<CompletionResult> GetCompletions(List<string> args, string argStr, CancellationToken cancel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Robust.Client.Console
|
||||
public void ReceiveCompletionResponse(MsgScriptCompletionResponse response)
|
||||
{
|
||||
Suggestions.SetSuggestions(response);
|
||||
Suggestions.OpenAt((Position.X + Size.X, Position.Y), (Size.X / 2, Size.Y));
|
||||
Suggestions.OpenAt(new(Position.X + Size.X, Position.Y), new(Size.X / 2, Size.Y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Robust.Client.Console
|
||||
var mainVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = (500, 300),
|
||||
MinSize = new(500, 300),
|
||||
Children =
|
||||
{
|
||||
(_watchesVBox = new BoxContainer
|
||||
@@ -69,7 +69,7 @@ namespace Robust.Client.Console
|
||||
|
||||
Contents.AddChild(mainVBox);
|
||||
|
||||
SetSize = (300, 300);
|
||||
SetSize = new(300, 300);
|
||||
}
|
||||
|
||||
private void AddWatch()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#if DEBUG
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
@@ -217,7 +218,7 @@ namespace Robust.Client.Debugging
|
||||
_debugPhysicsSystem = system;
|
||||
_lookup = lookup;
|
||||
_physicsSystem = physicsSystem;
|
||||
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
_font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
}
|
||||
|
||||
private void DrawWorld(DrawingHandleWorld worldHandle, OverlayDrawArgs args)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Debugging;
|
||||
|
||||
@@ -33,6 +33,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
@@ -84,7 +85,8 @@ namespace Robust.Client
|
||||
[Dependency] private readonly NetworkResourceManager _netResMan = default!;
|
||||
[Dependency] private readonly IReplayLoadManager _replayLoader = default!;
|
||||
[Dependency] private readonly IReplayPlaybackManager _replayPlayback = default!;
|
||||
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
|
||||
[Dependency] private readonly IReplayRecordingManagerInternal _replayRecording = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
|
||||
private IWebViewManagerHook? _webViewHook;
|
||||
|
||||
@@ -162,6 +164,7 @@ namespace Robust.Client
|
||||
// before prototype load.
|
||||
ProgramShared.FinishCheckBadFileExtensions(checkBadExtensions);
|
||||
|
||||
_reflectionManager.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDefaultPrototypes();
|
||||
_prototypeManager.ResolveResults();
|
||||
@@ -200,7 +203,12 @@ namespace Robust.Client
|
||||
// Setup main loop
|
||||
if (_mainLoop == null)
|
||||
{
|
||||
_mainLoop = new GameLoop(_gameTiming, _runtimeLog, _prof)
|
||||
_mainLoop = new GameLoop(
|
||||
_gameTiming,
|
||||
_runtimeLog,
|
||||
_prof,
|
||||
_logManager.GetSawmill("eng"),
|
||||
GameLoopOptions.FromCVars(_configurationManager))
|
||||
{
|
||||
SleepMode = displayMode == DisplayMode.Headless ? SleepMode.Delay : SleepMode.None
|
||||
};
|
||||
@@ -560,6 +568,11 @@ namespace Robust.Client
|
||||
{
|
||||
_taskManager.ProcessPendingTasks(); // tasks like connect
|
||||
}
|
||||
|
||||
using (_prof.Group("Content post engine"))
|
||||
{
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.InputPostEngine, frameEventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private void Tick(FrameEventArgs frameEventArgs)
|
||||
@@ -756,6 +769,8 @@ namespace Robust.Client
|
||||
|
||||
internal void CleanupGameThread()
|
||||
{
|
||||
_replayRecording.Shutdown();
|
||||
|
||||
_modLoader.Shutdown();
|
||||
|
||||
// CEF specifically makes a massive silent stink of it if we don't shut it down from the correct thread.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -14,15 +15,12 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private Eye? _eye = default!;
|
||||
[ViewVariables] internal Eye? _eye = default!;
|
||||
|
||||
// Horrible hack to get around ordering issues.
|
||||
private bool _setCurrentOnInitialize;
|
||||
[DataField("drawFov")]
|
||||
private bool _setDrawFovOnInitialize = true;
|
||||
[DataField("zoom")]
|
||||
private Vector2 _setZoomOnInitialize = Vector2.One;
|
||||
internal bool _setCurrentOnInitialize;
|
||||
[DataField("drawFov")] internal bool _setDrawFovOnInitialize = true;
|
||||
[DataField("zoom")] internal Vector2 _setZoomOnInitialize = Vector2.One;
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this entity is used to update the eye's position instead of just using the component's owner.
|
||||
@@ -118,54 +116,6 @@ namespace Robust.Client.GameObjects
|
||||
[ViewVariables]
|
||||
public MapCoordinates? Position => _eye?.Position;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_eye = new Eye
|
||||
{
|
||||
Position = _entityManager.GetComponent<TransformComponent>(Owner).MapPosition,
|
||||
Zoom = _setZoomOnInitialize,
|
||||
DrawFov = _setDrawFovOnInitialize
|
||||
};
|
||||
|
||||
if ((_eyeManager.CurrentEye == _eye) != _setCurrentOnInitialize)
|
||||
{
|
||||
if (_setCurrentOnInitialize)
|
||||
{
|
||||
_eyeManager.ClearCurrentEye();
|
||||
}
|
||||
else
|
||||
{
|
||||
_eyeManager.CurrentEye = _eye;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not EyeComponentState state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawFov = state.DrawFov;
|
||||
// TODO: Should be a way for content to override lerping and lerp the zoom
|
||||
Zoom = state.Zoom;
|
||||
Offset = state.Offset;
|
||||
VisibilityMask = state.VisibilityMask;
|
||||
}
|
||||
|
||||
protected override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
Current = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Eye of this entity with the transform position. This has to be called every frame to
|
||||
/// keep the view following the entity.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Client.Graphics;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
@@ -166,40 +167,9 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public bool TreeUpdateQueued { get; set; }
|
||||
|
||||
[DataField("layerDatums")]
|
||||
private List<PrototypeLayerData> LayerDatums
|
||||
{
|
||||
get
|
||||
{
|
||||
var layerDatums = new List<PrototypeLayerData>();
|
||||
foreach (var layer in Layers)
|
||||
{
|
||||
layerDatums.Add(layer.ToPrototypeData());
|
||||
}
|
||||
|
||||
return layerDatums;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
Layers.Clear();
|
||||
foreach (var layerDatum in value)
|
||||
{
|
||||
AddLayer(layerDatum);
|
||||
}
|
||||
|
||||
_layerMapShared = true;
|
||||
|
||||
QueueUpdateRenderTree();
|
||||
QueueUpdateIsInert();
|
||||
}
|
||||
}
|
||||
|
||||
private RSI? _baseRsi;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("rsi", priority: 2)]
|
||||
public RSI? BaseRSI
|
||||
{
|
||||
get => _baseRsi;
|
||||
@@ -312,7 +282,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public const string LogCategory = "go.comp.sprite";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] public bool IsInert { get; private set; }
|
||||
[ViewVariables(VVAccess.ReadWrite)] public bool IsInert { get; internal set; }
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
@@ -356,7 +326,16 @@ namespace Robust.Client.GameObjects
|
||||
if (layerDatums.Count != 0)
|
||||
{
|
||||
LayerMap.Clear();
|
||||
LayerDatums = layerDatums;
|
||||
Layers.Clear();
|
||||
foreach (var datum in layerDatums)
|
||||
{
|
||||
AddLayer(datum);
|
||||
}
|
||||
|
||||
_layerMapShared = true;
|
||||
|
||||
QueueUpdateRenderTree();
|
||||
QueueUpdateIsInert();
|
||||
}
|
||||
|
||||
UpdateLocalMatrix();
|
||||
@@ -1397,33 +1376,6 @@ namespace Robust.Client.GameObjects
|
||||
entities.EventBus.RaiseComponentEvent(this, ref ev);
|
||||
}
|
||||
|
||||
internal void DoUpdateIsInert()
|
||||
{
|
||||
_inertUpdateQueued = false;
|
||||
IsInert = true;
|
||||
|
||||
foreach (var layer in Layers)
|
||||
{
|
||||
// Since StateId is a struct, we can't null-check it directly.
|
||||
if (!layer.State.IsValid || !layer.Visible || !layer.AutoAnimated || layer.Blank)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var rsi = layer.RSI ?? BaseRSI;
|
||||
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
|
||||
{
|
||||
state = GetFallbackState(resourceCache);
|
||||
}
|
||||
|
||||
if (state.IsAnimated)
|
||||
{
|
||||
IsInert = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use SpriteSystem instead.")]
|
||||
internal static RSI.State GetFallbackState(IResourceCache cache)
|
||||
{
|
||||
|
||||
@@ -1,104 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
public sealed class ClientUserInterfaceComponent : SharedUserInterfaceComponent, ISerializationHooks
|
||||
public sealed class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntityNetworkManager _netMan = default!;
|
||||
|
||||
internal readonly Dictionary<Enum, BoundUserInterface> _openInterfaces =
|
||||
new();
|
||||
|
||||
[ViewVariables]
|
||||
internal readonly Dictionary<Enum, PrototypeData> _interfaces = new();
|
||||
|
||||
[ViewVariables]
|
||||
public IEnumerable<BoundUserInterface> Interfaces => _openInterfaces.Values;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
_interfaces.Clear();
|
||||
|
||||
foreach (var data in _interfaceData)
|
||||
{
|
||||
_interfaces[data.UiKey] = data;
|
||||
}
|
||||
}
|
||||
|
||||
internal void MessageReceived(BoundUIWrapMessage msg)
|
||||
{
|
||||
switch (msg.Message)
|
||||
{
|
||||
case OpenBoundInterfaceMessage _:
|
||||
if (_openInterfaces.ContainsKey(msg.UiKey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OpenInterface(msg);
|
||||
break;
|
||||
|
||||
case CloseBoundInterfaceMessage _:
|
||||
Close(msg.UiKey, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_openInterfaces.TryGetValue(msg.UiKey, out var bi))
|
||||
{
|
||||
bi.InternalReceiveMessage(msg.Message);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenInterface(BoundUIWrapMessage wrapped)
|
||||
{
|
||||
var data = _interfaces[wrapped.UiKey];
|
||||
// TODO: This type should be cached, but I'm too lazy.
|
||||
var type = _reflectionManager.LooseGetType(data.ClientType);
|
||||
var boundInterface =
|
||||
(BoundUserInterface) _dynamicTypeFactory.CreateInstance(type, new object[] {this, wrapped.UiKey});
|
||||
boundInterface.Open();
|
||||
_openInterfaces[wrapped.UiKey] = boundInterface;
|
||||
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if(playerSession != null)
|
||||
_entityManager.EventBus.RaiseLocalEvent(Owner, new BoundUIOpenedEvent(wrapped.UiKey, Owner, playerSession), true);
|
||||
}
|
||||
|
||||
internal void Close(Enum uiKey, bool remoteCall)
|
||||
{
|
||||
if (!_openInterfaces.TryGetValue(uiKey, out var boundUserInterface))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!remoteCall)
|
||||
SendMessage(new CloseBoundInterfaceMessage(), uiKey);
|
||||
_openInterfaces.Remove(uiKey);
|
||||
boundUserInterface.Dispose();
|
||||
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if(playerSession != null)
|
||||
_entityManager.EventBus.RaiseLocalEvent(Owner, new BoundUIClosedEvent(uiKey, Owner, playerSession), true);
|
||||
}
|
||||
|
||||
internal void SendMessage(BoundUserInterfaceMessage message, Enum uiKey)
|
||||
{
|
||||
_netMan.SendSystemNetworkMessage(new BoundUIWrapMessage(Owner, message, uiKey));
|
||||
}
|
||||
public readonly Dictionary<Enum, BoundUserInterface> OpenInterfaces = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -106,17 +21,22 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public abstract class BoundUserInterface : IDisposable
|
||||
{
|
||||
protected ClientUserInterfaceComponent Owner { get; }
|
||||
[Dependency] protected readonly IEntityManager EntMan = default!;
|
||||
protected readonly UserInterfaceSystem UiSystem = default!;
|
||||
|
||||
public readonly Enum UiKey;
|
||||
public EntityUid Owner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The last received state object sent from the server.
|
||||
/// </summary>
|
||||
protected BoundUserInterfaceState? State { get; private set; }
|
||||
|
||||
protected BoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey)
|
||||
protected BoundUserInterface(EntityUid owner, Enum uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
UiSystem = EntMan.System<UserInterfaceSystem>();
|
||||
|
||||
Owner = owner;
|
||||
UiKey = uiKey;
|
||||
}
|
||||
@@ -148,7 +68,7 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
Owner.Close(UiKey, false);
|
||||
UiSystem.TryCloseUi(Owner, UiKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -156,7 +76,7 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void SendMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
Owner.SendMessage(message, UiKey);
|
||||
UiSystem.SendUiMessage(this, message);
|
||||
}
|
||||
|
||||
internal void InternalReceiveMessage(BoundUserInterfaceMessage message)
|
||||
|
||||
@@ -4,6 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -11,18 +12,40 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
private readonly List<AnimationPlayerComponent> _activeAnimations = new();
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
for (var i = _activeAnimations.Count - 1; i >= 0; i--)
|
||||
// TODO: Active or something idk.
|
||||
for (var i = 0; i < _activeAnimations.Count; i++)
|
||||
{
|
||||
var anim = _activeAnimations[i];
|
||||
if (!Update(anim, frameTime)) continue;
|
||||
var uid = anim.Owner;
|
||||
|
||||
if (!_metaQuery.TryGetComponent(uid, out var metadata) ||
|
||||
metadata.EntityPaused)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Update(uid, anim, frameTime))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_activeAnimations.RemoveSwap(i);
|
||||
i--;
|
||||
anim.HasPlayingAnimation = false;
|
||||
}
|
||||
}
|
||||
@@ -34,16 +57,18 @@ namespace Robust.Client.GameObjects
|
||||
component.HasPlayingAnimation = true;
|
||||
}
|
||||
|
||||
private bool Update(AnimationPlayerComponent component, float frameTime)
|
||||
private bool Update(EntityUid uid, AnimationPlayerComponent component, float frameTime)
|
||||
{
|
||||
if (component.PlayingAnimationCount == 0 ||
|
||||
component.Deleted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var remie = new RemQueue<string>();
|
||||
foreach (var (key, playback) in component.PlayingAnimations)
|
||||
{
|
||||
var keep = AnimationPlaybackShared.UpdatePlayback(component.Owner, playback, frameTime);
|
||||
var keep = AnimationPlaybackShared.UpdatePlayback(uid, playback, frameTime);
|
||||
if (!keep)
|
||||
{
|
||||
remie.Add(key);
|
||||
@@ -53,7 +78,7 @@ namespace Robust.Client.GameObjects
|
||||
foreach (var key in remie)
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
EntityManager.EventBus.RaiseLocalEvent(component.Owner, new AnimationCompletedEvent {Uid = component.Owner, Key = key}, true);
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, new AnimationCompletedEvent {Uid = uid, Key = key}, true);
|
||||
component.AnimationComplete(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
|
||||
// Max distance check
|
||||
var delta = mapPos.Value.Position - listener.Position;
|
||||
var distance = delta.Length;
|
||||
var distance = delta.Length();
|
||||
if (distance > stream.MaxDistance)
|
||||
{
|
||||
stream.Source.SetVolumeDirect(0);
|
||||
@@ -314,9 +314,9 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
return source != null;
|
||||
}
|
||||
|
||||
private PlayingStream CreateAndStartPlayingStream(IClydeAudioSource source, AudioParams? audioParams)
|
||||
private PlayingStream CreateAndStartPlayingStream(IClydeAudioSource source, AudioParams? audioParams, AudioStream stream)
|
||||
{
|
||||
ApplyAudioParams(audioParams, source);
|
||||
ApplyAudioParams(audioParams, source, stream);
|
||||
source.StartPlaying();
|
||||
var playing = new PlayingStream
|
||||
{
|
||||
@@ -365,7 +365,7 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
|
||||
source.SetGlobal();
|
||||
|
||||
return CreateAndStartPlayingStream(source, audioParams);
|
||||
return CreateAndStartPlayingStream(source, audioParams, stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -416,7 +416,7 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
if (!source.SetPosition(worldPos))
|
||||
return Play(stream, fallbackCoordinates.Value, fallbackCoordinates.Value, audioParams);
|
||||
|
||||
var playing = CreateAndStartPlayingStream(source, audioParams);
|
||||
var playing = CreateAndStartPlayingStream(source, audioParams, stream);
|
||||
playing.TrackingEntity = entity;
|
||||
playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null;
|
||||
return playing;
|
||||
@@ -469,7 +469,7 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
}
|
||||
|
||||
var playing = CreateAndStartPlayingStream(source, audioParams);
|
||||
var playing = CreateAndStartPlayingStream(source, audioParams, stream);
|
||||
playing.TrackingCoordinates = coordinates;
|
||||
playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null;
|
||||
return playing;
|
||||
@@ -493,7 +493,7 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source)
|
||||
private void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source, AudioStream audio)
|
||||
{
|
||||
if (!audioParams.HasValue)
|
||||
return;
|
||||
@@ -508,8 +508,12 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
source.SetRolloffFactor(audioParams.Value.RolloffFactor);
|
||||
source.SetMaxDistance(audioParams.Value.MaxDistance);
|
||||
source.SetReferenceDistance(audioParams.Value.ReferenceDistance);
|
||||
source.SetPlaybackPosition(audioParams.Value.PlayOffsetSeconds);
|
||||
source.IsLooping = audioParams.Value.Loop;
|
||||
|
||||
// TODO clamp the offset inside of SetPlaybackPosition() itself.
|
||||
var offset = audioParams.Value.PlayOffsetSeconds;
|
||||
offset = Math.Clamp(offset, 0f, (float) audio.Length.TotalSeconds);
|
||||
source.SetPlaybackPosition(offset);
|
||||
}
|
||||
|
||||
public sealed class PlayingStream : IPlayingAudioStream
|
||||
|
||||
@@ -40,7 +40,7 @@ internal sealed class ClientOccluderSystem : OccluderSystem
|
||||
return;
|
||||
|
||||
comp.Enabled = enabled;
|
||||
Dirty(comp);
|
||||
Dirty(uid, comp);
|
||||
|
||||
var xform = Transform(uid);
|
||||
QueueTreeUpdate(uid, comp, xform);
|
||||
|
||||
@@ -38,7 +38,8 @@ public sealed class DebugEntityLookupSystem : EntitySystem
|
||||
IoCManager.Resolve<IOverlayManager>().AddOverlay(
|
||||
new EntityLookupOverlay(
|
||||
EntityManager,
|
||||
Get<EntityLookupSystem>()));
|
||||
EntityManager.System<EntityLookupSystem>(),
|
||||
EntityManager.System<SharedTransformSystem>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -52,31 +53,35 @@ public sealed class DebugEntityLookupSystem : EntitySystem
|
||||
|
||||
public sealed class EntityLookupOverlay : Overlay
|
||||
{
|
||||
private IEntityManager _entityManager = default!;
|
||||
private EntityLookupSystem _lookup = default!;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityLookupOverlay(IEntityManager entManager, EntityLookupSystem lookup)
|
||||
public EntityLookupOverlay(IEntityManager entManager, EntityLookupSystem lookup, SharedTransformSystem transform)
|
||||
{
|
||||
_entityManager = entManager;
|
||||
_lookup = lookup;
|
||||
_xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var worldBounds = args.WorldBounds;
|
||||
|
||||
foreach (var lookup in _lookup.FindLookupsIntersecting(args.MapId, args.WorldBounds))
|
||||
// TODO: Static version
|
||||
_lookup.FindLookupsIntersecting(args.MapId, worldBounds, (uid, lookup) =>
|
||||
{
|
||||
var lookupXform = xformQuery.GetComponent(lookup.Owner);
|
||||
|
||||
var (_, rotation, matrix, invMatrix) = lookupXform.GetWorldPositionRotationMatrixWithInv();
|
||||
var (_, rotation, matrix, invMatrix) = _transform.GetWorldPositionRotationMatrixWithInv(uid);
|
||||
|
||||
worldHandle.SetTransform(matrix);
|
||||
|
||||
var lookupAABB = invMatrix.TransformBox(args.WorldBounds);
|
||||
var lookupAABB = invMatrix.TransformBox(worldBounds);
|
||||
var ents = new List<EntityUid>();
|
||||
|
||||
lookup.DynamicTree.QueryAabb(ref ents, static (ref List<EntityUid> state, in FixtureProxy value) =>
|
||||
@@ -105,20 +110,22 @@ public sealed class EntityLookupOverlay : Overlay
|
||||
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
if (_entityManager.Deleted(ent)) continue;
|
||||
var xform = xformQuery.GetComponent(ent);
|
||||
if (_entityManager.Deleted(ent))
|
||||
continue;
|
||||
|
||||
var xform = _xformQuery.GetComponent(ent);
|
||||
|
||||
//DebugTools.Assert(!ent.IsInContainer(_entityManager));
|
||||
var (entPos, entRot) = xform.GetWorldPositionRotation();
|
||||
var (entPos, entRot) = _transform.GetWorldPositionRotation(ent);
|
||||
|
||||
var lookupPos = invMatrix.Transform(entPos);
|
||||
var lookupRot = entRot - rotation;
|
||||
|
||||
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, xformQuery);
|
||||
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, _xformQuery);
|
||||
|
||||
worldHandle.DrawRect(aabb, Color.Blue.WithAlpha(0.2f));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
@@ -66,11 +66,11 @@ namespace Robust.Client.GameObjects
|
||||
var map = _eyeManager.CurrentMap;
|
||||
if (map == MapId.Nullspace) return;
|
||||
|
||||
foreach (var treeComp in _trees.GetIntersectingTrees(map, args.WorldBounds))
|
||||
foreach (var (_, treeComp) in _trees.GetIntersectingTrees(map, args.WorldBounds))
|
||||
{
|
||||
foreach (var (light, xform) in treeComp.Tree)
|
||||
foreach (var entry in treeComp.Tree)
|
||||
{
|
||||
var aabb = _lookup.GetWorldAABB(light.Owner, xform);
|
||||
var aabb = _lookup.GetWorldAABB(entry.Uid, entry.Transform);
|
||||
if (!aabb.Intersects(args.WorldAABB)) continue;
|
||||
|
||||
args.WorldHandle.DrawRect(aabb, Color.Green.WithAlpha(0.1f));
|
||||
|
||||
60
Robust.Client/GameObjects/EntitySystems/EyeSystem.cs
Normal file
60
Robust.Client/GameObjects/EntitySystems/EyeSystem.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed class EyeSystem : SharedEyeSystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EyeComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<EyeComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<EyeComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, EyeComponent component, ComponentInit args)
|
||||
{
|
||||
component._eye = new Eye
|
||||
{
|
||||
Position = Transform(uid).MapPosition,
|
||||
Zoom = component._setZoomOnInitialize,
|
||||
DrawFov = component._setDrawFovOnInitialize
|
||||
};
|
||||
|
||||
if ((_eyeManager.CurrentEye == component._eye) != component._setCurrentOnInitialize)
|
||||
{
|
||||
if (component._setCurrentOnInitialize)
|
||||
{
|
||||
_eyeManager.ClearCurrentEye();
|
||||
}
|
||||
else
|
||||
{
|
||||
_eyeManager.CurrentEye = component._eye;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRemove(EntityUid uid, EyeComponent component, ComponentRemove args)
|
||||
{
|
||||
component.Current = false;
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, EyeComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not EyeComponentState state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.DrawFov = state.DrawFov;
|
||||
// TODO: Should be a way for content to override lerping and lerp the zoom
|
||||
component.Zoom = state.Zoom;
|
||||
component.Offset = state.Offset;
|
||||
component.VisibilityMask = state.VisibilityMask;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -16,4 +16,9 @@ internal sealed class GridRenderingSystem : EntitySystem
|
||||
{
|
||||
_clyde.RegisterGridEcsEvents();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_clyde.ShutdownGridEcsEvents();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
|
||||
comp.ContainerOccluded = occluded;
|
||||
Dirty(comp);
|
||||
Dirty(uid, comp);
|
||||
|
||||
if (comp.Enabled)
|
||||
_lightTree.QueueTreeUpdate(uid, comp);
|
||||
@@ -50,7 +50,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
comp._enabled = enabled;
|
||||
RaiseLocalEvent(uid, new PointLightToggleEvent(comp.Enabled));
|
||||
Dirty(comp);
|
||||
Dirty(uid, comp);
|
||||
|
||||
var cast = (PointLightComponent)comp;
|
||||
if (!cast.ContainerOccluded)
|
||||
@@ -63,7 +63,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
|
||||
comp._radius = radius;
|
||||
Dirty(comp);
|
||||
Dirty(uid, comp);
|
||||
|
||||
var cast = (PointLightComponent)comp;
|
||||
if (cast.TreeUid != null)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -91,21 +92,53 @@ namespace Robust.Client.GameObjects
|
||||
_inertUpdateQueue.Enqueue(sprite);
|
||||
}
|
||||
|
||||
private void DoUpdateIsInert(SpriteComponent component)
|
||||
{
|
||||
component._inertUpdateQueued = false;
|
||||
component.IsInert = true;
|
||||
|
||||
foreach (var layer in component.Layers)
|
||||
{
|
||||
// Since StateId is a struct, we can't null-check it directly.
|
||||
if (!layer.State.IsValid || !layer.Visible || !layer.AutoAnimated || layer.Blank)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var rsi = layer.RSI ?? component.BaseRSI;
|
||||
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
|
||||
{
|
||||
state = GetFallbackState();
|
||||
}
|
||||
|
||||
if (state.IsAnimated)
|
||||
{
|
||||
component.IsInert = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
while (_inertUpdateQueue.TryDequeue(out var sprite))
|
||||
{
|
||||
sprite.DoUpdateIsInert();
|
||||
DoUpdateIsInert(sprite);
|
||||
}
|
||||
|
||||
var realtime = _timing.RealTime.TotalSeconds;
|
||||
var spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
var syncQuery = GetEntityQuery<SyncSpriteComponent>();
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
foreach (var uid in _queuedFrameUpdate)
|
||||
{
|
||||
if (!spriteQuery.TryGetComponent(uid, out var sprite))
|
||||
if (!spriteQuery.TryGetComponent(uid, out var sprite) ||
|
||||
metaQuery.GetComponent(uid).EntityPaused)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sprite.IsInert)
|
||||
continue;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -21,6 +22,13 @@ public sealed partial class TransformSystem
|
||||
base.SetLocalPositionNoLerp(xform, value);
|
||||
}
|
||||
|
||||
public override void SetLocalRotationNoLerp(TransformComponent xform, Angle angle)
|
||||
{
|
||||
xform.NextRotation = null;
|
||||
xform.LerpParent = EntityUid.Invalid;
|
||||
base.SetLocalRotationNoLerp(xform, angle);
|
||||
}
|
||||
|
||||
public override void SetLocalRotation(TransformComponent xform, Angle angle)
|
||||
{
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -85,7 +86,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
var lerpDest = transform.NextPosition.Value;
|
||||
var lerpSource = transform.PrevPosition;
|
||||
var distance = (lerpDest - lerpSource).LengthSquared;
|
||||
var distance = (lerpDest - lerpSource).LengthSquared();
|
||||
|
||||
if (distance is > MinInterpolationDistanceSquared and < MaxInterpolationDistanceSquared)
|
||||
{
|
||||
|
||||
@@ -2,25 +2,40 @@ using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
|
||||
{
|
||||
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeNetworkEvent<BoundUIWrapMessage>(MessageReceived);
|
||||
SubscribeLocalEvent<ClientUserInterfaceComponent, ComponentInit>(OnUserInterfaceInit);
|
||||
SubscribeLocalEvent<ClientUserInterfaceComponent, ComponentShutdown>(OnUserInterfaceShutdown);
|
||||
}
|
||||
|
||||
private void OnUserInterfaceInit(EntityUid uid, ClientUserInterfaceComponent component, ComponentInit args)
|
||||
{
|
||||
component._interfaces.Clear();
|
||||
|
||||
foreach (var data in component._interfaceData)
|
||||
{
|
||||
component._interfaces[data.UiKey] = data;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUserInterfaceShutdown(EntityUid uid, ClientUserInterfaceComponent component, ComponentShutdown args)
|
||||
{
|
||||
foreach (var bui in component.Interfaces)
|
||||
foreach (var bui in component.OpenInterfaces.Values)
|
||||
{
|
||||
bui.Dispose();
|
||||
}
|
||||
@@ -29,25 +44,88 @@ namespace Robust.Client.GameObjects
|
||||
private void MessageReceived(BoundUIWrapMessage ev)
|
||||
{
|
||||
var uid = ev.Entity;
|
||||
if (!EntityManager.TryGetComponent<ClientUserInterfaceComponent>(uid, out var cmp))
|
||||
if (!TryComp<ClientUserInterfaceComponent>(uid, out var cmp))
|
||||
return;
|
||||
|
||||
var uiKey = ev.UiKey;
|
||||
var message = ev.Message;
|
||||
// This should probably not happen at this point, but better make extra sure!
|
||||
if(_playerManager.LocalPlayer != null)
|
||||
message.Session = _playerManager.LocalPlayer.Session;
|
||||
|
||||
message.Entity = uid;
|
||||
message.UiKey = ev.UiKey;
|
||||
message.UiKey = uiKey;
|
||||
|
||||
// Raise as object so the correct type is used.
|
||||
RaiseLocalEvent(uid, (object)message, true);
|
||||
|
||||
cmp.MessageReceived(ev);
|
||||
switch (message)
|
||||
{
|
||||
case OpenBoundInterfaceMessage _:
|
||||
TryOpenUi(uid, uiKey, cmp);
|
||||
break;
|
||||
|
||||
case CloseBoundInterfaceMessage _:
|
||||
TryCloseUi(uid, uiKey, remoteCall: true, uiComp: cmp);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (cmp.OpenInterfaces.TryGetValue(uiKey, out var bui))
|
||||
bui.InternalReceiveMessage(message);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Send(BoundUIWrapMessage msg)
|
||||
private bool TryOpenUi(EntityUid uid, Enum uiKey, ClientUserInterfaceComponent? uiComp = null)
|
||||
{
|
||||
RaiseNetworkEvent(msg);
|
||||
if (!Resolve(uid, ref uiComp))
|
||||
return false;
|
||||
|
||||
if (uiComp.OpenInterfaces.ContainsKey(uiKey))
|
||||
return false;
|
||||
|
||||
var data = uiComp._interfaces[uiKey];
|
||||
|
||||
// TODO: This type should be cached, but I'm too lazy.
|
||||
var type = _reflectionManager.LooseGetType(data.ClientType);
|
||||
var boundInterface =
|
||||
(BoundUserInterface) _dynamicTypeFactory.CreateInstance(type, new object[] {uid, uiKey});
|
||||
|
||||
boundInterface.Open();
|
||||
uiComp.OpenInterfaces[uiKey] = boundInterface;
|
||||
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if(playerSession != null)
|
||||
RaiseLocalEvent(uid, new BoundUIOpenedEvent(uiKey, uid, playerSession), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool TryCloseUi(EntityUid uid, Enum uiKey, bool remoteCall = false, ClientUserInterfaceComponent? uiComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref uiComp))
|
||||
return false;
|
||||
|
||||
if (!uiComp.OpenInterfaces.TryGetValue(uiKey, out var boundUserInterface))
|
||||
return false;
|
||||
|
||||
if (!remoteCall)
|
||||
SendUiMessage(boundUserInterface, new CloseBoundInterfaceMessage());
|
||||
|
||||
uiComp.OpenInterfaces.Remove(uiKey);
|
||||
boundUserInterface.Dispose();
|
||||
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if(playerSession != null)
|
||||
RaiseLocalEvent(uid, new BoundUIClosedEvent(uiKey, uid, playerSession), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
|
||||
{
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, msg, bui.UiKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -46,7 +47,7 @@ namespace Robust.Client.GameObjects
|
||||
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
|
||||
_label.Visible = true;
|
||||
|
||||
_label.Text = $"Speed: {body.LinearVelocity.Length:0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
|
||||
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
comp.HandleComponentState(compState, null);
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
|
||||
@@ -561,7 +560,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
var stateEv = new ComponentHandleState(state, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
|
||||
comp.HandleComponentState(state, null);
|
||||
comp.ClearCreationTick(); // don't undo the re-adding.
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
@@ -747,10 +745,6 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
var contQuery = _entities.GetEntityQuery<ContainerManagerComponent>();
|
||||
var physicsQuery = _entities.GetEntityQuery<PhysicsComponent>();
|
||||
var fixturesQuery = _entities.GetEntityQuery<FixturesComponent>();
|
||||
var broadQuery = _entities.GetEntityQuery<BroadphaseComponent>();
|
||||
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
|
||||
|
||||
// Apply entity states.
|
||||
@@ -783,7 +777,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var (uid, xform) in queuedBroadphaseUpdates)
|
||||
{
|
||||
lookupSys.FindAndAddToEntityTree(uid, xform, xforms, metas, contQuery, physicsQuery, fixturesQuery, broadQuery);
|
||||
lookupSys.FindAndAddToEntityTree(uid, true, xform);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -869,13 +863,13 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// This entity is going to get deleted, but maybe some if its children won't be, so lets detach them to
|
||||
// null. First we will detach the parent in order to reduce the number of broadphase/lookup updates.
|
||||
xformSys.DetachParentToNull(ent, xform, xforms, metas);
|
||||
xformSys.DetachParentToNull(ent, xform);
|
||||
|
||||
// Then detach all children.
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
xformSys.DetachParentToNull(child.Value, xforms.GetComponent(child.Value), xforms, metas, xform);
|
||||
xformSys.DetachParentToNull(child.Value, xforms.GetComponent(child.Value), xform);
|
||||
|
||||
if (deleteClientChildren
|
||||
&& !deleteClientEntities // don't add duplicates
|
||||
@@ -917,13 +911,13 @@ namespace Robust.Client.GameStates
|
||||
continue; // Already deleted? or never sent to us?
|
||||
|
||||
// First, a single recursive map change
|
||||
xformSys.DetachParentToNull(id, xform, xforms, metas);
|
||||
xformSys.DetachParentToNull(id, xform);
|
||||
|
||||
// Then detach all children.
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
xformSys.DetachParentToNull(child.Value, xforms.GetComponent(child.Value), xforms, metas, xform);
|
||||
xformSys.DetachParentToNull(child.Value, xforms.GetComponent(child.Value), xform);
|
||||
}
|
||||
|
||||
// Finally, delete the entity.
|
||||
@@ -1002,7 +996,7 @@ namespace Robust.Client.GameStates
|
||||
var xform = xforms.GetComponent(ent);
|
||||
if (xform.ParentUid.IsValid())
|
||||
{
|
||||
lookupSys.RemoveFromEntityTree(ent, xform, xforms);
|
||||
lookupSys.RemoveFromEntityTree(ent, xform);
|
||||
xform.Broadphase = BroadphaseData.Invalid;
|
||||
|
||||
// In some cursed scenarios an entity inside of a container can leave PVS without the container itself leaving PVS.
|
||||
@@ -1017,7 +1011,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
meta._flags |= MetaDataFlags.Detached;
|
||||
xformSys.DetachParentToNull(ent, xform, xforms, metas);
|
||||
xformSys.DetachParentToNull(ent, xform);
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
|
||||
|
||||
if (container != null)
|
||||
@@ -1172,7 +1166,6 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
var handleState = new ComponentHandleState(cur, next);
|
||||
bus.RaiseComponentEvent(comp, ref handleState);
|
||||
comp.HandleComponentState(cur, next);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1223,7 +1216,7 @@ namespace Robust.Client.GameStates
|
||||
return;
|
||||
|
||||
using var _ = _timing.StartStateApplicationArea();
|
||||
ResetEnt(meta, false);
|
||||
ResetEnt(uid, meta, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1301,24 +1294,24 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
using var _ = _timing.StartStateApplicationArea();
|
||||
|
||||
foreach (var meta in _entities.EntityQuery<MetaDataComponent>(true))
|
||||
var query = _entityManager.AllEntityQueryEnumerator<MetaDataComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var meta))
|
||||
{
|
||||
ResetEnt(meta);
|
||||
ResetEnt(uid, meta);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset a given entity to the most recent server state.
|
||||
/// </summary>
|
||||
private void ResetEnt(MetaDataComponent meta, bool skipDetached = true)
|
||||
private void ResetEnt(EntityUid uid, MetaDataComponent meta, bool skipDetached = true)
|
||||
{
|
||||
if (skipDetached && (meta.Flags & MetaDataFlags.Detached) != 0)
|
||||
return;
|
||||
|
||||
meta.Flags &= ~MetaDataFlags.Detached;
|
||||
|
||||
var uid = meta.Owner;
|
||||
|
||||
if (!_processor.TryGetLastServerStates(uid, out var lastState))
|
||||
return;
|
||||
|
||||
@@ -1334,7 +1327,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
var handleState = new ComponentHandleState(state, null);
|
||||
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
comp.HandleComponentState(state, null);
|
||||
}
|
||||
|
||||
// ensure we don't have any extra components
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Timing;
|
||||
@@ -40,7 +41,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var cache = IoCManager.Resolve<IResourceCache>();
|
||||
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
_font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
_lineHeight = _font.GetLineHeight(1);
|
||||
|
||||
_gameStateManager.GameStateApplied += HandleGameStateApplied;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
@@ -52,7 +53,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var cache = IoCManager.Resolve<IResourceCache>();
|
||||
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
_font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
|
||||
_gameStateManager.GameStateApplied += HandleGameStateApplied;
|
||||
}
|
||||
@@ -227,7 +228,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// average payload line
|
||||
var avg = height - BytesToPixels(_totalHistoryPayload/HistorySize);
|
||||
var avgEnd = new Vector2(LeftMargin + width, avg)+ (70,0);
|
||||
var avgEnd = new Vector2(LeftMargin + width, avg) + new Vector2(70, 0);
|
||||
handle.DrawLine(new Vector2(LeftMargin, avg), avgEnd, Color.DarkGray.WithAlpha(0.8f));
|
||||
|
||||
// top payload warning line
|
||||
@@ -252,7 +253,8 @@ namespace Robust.Client.GameStates
|
||||
handle.DrawString(_font, new Vector2(LeftMargin + width, minYoff), "8.19Kbit/s");
|
||||
|
||||
// avg text
|
||||
handle.DrawString(_font, avgEnd - _font.GetLineHeight(1)/2f, "average");
|
||||
var lineHeight = _font.GetLineHeight(1) / 2f;
|
||||
handle.DrawString(_font, avgEnd - new Vector2(lineHeight, lineHeight), "average");
|
||||
|
||||
// lag text info
|
||||
if(lastLagY != -1)
|
||||
|
||||
@@ -14,7 +14,8 @@ using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
using Robust.Shared.Maths;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Client.Graphics.Audio
|
||||
readSamples += read;
|
||||
}
|
||||
|
||||
return new OggVorbisData(totalSamples, sampleRate, channels, buffer);
|
||||
return new OggVorbisData(totalSamples, sampleRate, channels, buffer, vorbis.Tags.Title, vorbis.Tags.Artist);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,13 +37,17 @@ namespace Robust.Client.Graphics.Audio
|
||||
public readonly long SampleRate;
|
||||
public readonly long Channels;
|
||||
public readonly ReadOnlyMemory<float> Data;
|
||||
public readonly string Title;
|
||||
public readonly string Artist;
|
||||
|
||||
public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory<float> data)
|
||||
public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory<float> data, string title, string artist)
|
||||
{
|
||||
TotalSamples = totalSamples;
|
||||
SampleRate = sampleRate;
|
||||
Channels = channels;
|
||||
Data = data;
|
||||
Title = title;
|
||||
Artist = artist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using OpenTK.Mathematics;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
@@ -183,15 +177,15 @@ namespace Robust.Client.Graphics.Audio
|
||||
private void _updateAudio()
|
||||
{
|
||||
var eye = _eyeManager.CurrentEye;
|
||||
var (x, y) = eye.Position.Position;
|
||||
AL.Listener(ALListener3f.Position, x, y, -5);
|
||||
var vec = eye.Position.Position;
|
||||
AL.Listener(ALListener3f.Position, vec.X, vec.Y, -5);
|
||||
var rot2d = eye.Rotation.ToVec();
|
||||
AL.Listener(ALListenerfv.Orientation, new []{0, 0, -1, rot2d.X, rot2d.Y, 0});
|
||||
|
||||
// Default orientation: at: (0, 0, -1) up: (0, 1, 0)
|
||||
var (rotX, rotY) = eye.Rotation.ToVec();
|
||||
var rot = eye.Rotation.ToVec();
|
||||
var at = new Vector3(0f, 0f, -1f);
|
||||
var up = new Vector3(rotY, rotX, 0f);
|
||||
var up = new Vector3(rot.Y, rot.X, 0f);
|
||||
AL.Listener(ALListenerfv.Orientation, ref at, ref up);
|
||||
|
||||
_flushALDisposeQueues();
|
||||
@@ -267,6 +261,9 @@ namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
var source = AL.GenSource();
|
||||
|
||||
if (!AL.IsSource(source))
|
||||
throw new Exception("Failed to generate source. Too many simultaneous audio streams?");
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
|
||||
var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio);
|
||||
@@ -332,7 +329,7 @@ namespace Robust.Client.Graphics.Audio
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
|
||||
return new AudioStream(handle, length, (int) vorbis.Channels, name);
|
||||
return new AudioStream(handle, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioWav(Stream stream, string? name = null)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using JetBrains.Annotations;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -528,7 +529,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// So there are distortions from incorrect projection.
|
||||
_renderHandle.UseShader(_fovDebugShaderInstance);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
|
||||
var pos = UIBox2.FromDimensions(viewport.Size / 2 - (200, 200), (400, 400));
|
||||
var pos = UIBox2.FromDimensions(viewport.Size / 2 - new Vector2(200, 200), new Vector2(400, 400));
|
||||
_renderHandle.DrawingHandleScreen.DrawTextureRect(FovTexture, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Numerics;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
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
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Buffers;
|
||||
using System.Numerics;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
@@ -17,6 +18,7 @@ using Robust.Shared.Physics;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using static Robust.Shared.GameObjects.OccluderComponent;
|
||||
using Robust.Shared.Utility;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -558,7 +560,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (light.CastShadows)
|
||||
shadowCount++;
|
||||
|
||||
float distanceSquared = (state.worldAABB.Center - lightPos).LengthSquared;
|
||||
var distanceSquared = (state.worldAABB.Center - lightPos).LengthSquared();
|
||||
state.clyde._lightsToRenderList[count++] = (light, lightPos, distanceSquared, rot);
|
||||
|
||||
return true;
|
||||
@@ -576,9 +578,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var state = (this, count: 0, shadowCastingCount: 0, xformSystem, xforms, worldAABB);
|
||||
|
||||
foreach (var comp in lightTreeSys.GetIntersectingTrees(map, worldAABB))
|
||||
foreach (var (uid, comp) in lightTreeSys.GetIntersectingTrees(map, worldAABB))
|
||||
{
|
||||
var bounds = xformSystem.GetInvWorldMatrix(comp.Owner, xforms).TransformBox(worldBounds);
|
||||
var bounds = xformSystem.GetInvWorldMatrix(uid, xforms).TransformBox(worldBounds);
|
||||
comp.Tree.QueryAabb(ref state, LightQuery, bounds);
|
||||
}
|
||||
|
||||
@@ -926,12 +928,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var comp in occluderSystem.GetIntersectingTrees(map, expandedBounds))
|
||||
foreach (var (uid, comp) in occluderSystem.GetIntersectingTrees(map, expandedBounds))
|
||||
{
|
||||
if (ami >= amiMax)
|
||||
break;
|
||||
|
||||
var treeBounds = xforms.GetComponent(comp.Owner).InvWorldMatrix.TransformBox(expandedBounds);
|
||||
var treeBounds = xforms.GetComponent(uid).InvWorldMatrix.TransformBox(expandedBounds);
|
||||
|
||||
comp.Tree.QueryAabb((in ComponentTreeEntry<OccluderComponent> entry) =>
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -165,7 +166,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var oldModel = _clyde._currentMatrixModel;
|
||||
|
||||
var newModel = oldModel;
|
||||
position += (oldModel.R0C2, oldModel.R1C2);
|
||||
position += new Vector2(oldModel.R0C2, oldModel.R1C2);
|
||||
newModel.R0C2 = 0;
|
||||
newModel.R1C2 = 0;
|
||||
SetModelTransform(newModel);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
@@ -9,6 +10,8 @@ using Robust.Client.Utility;
|
||||
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
|
||||
{
|
||||
|
||||
@@ -2,12 +2,15 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.ResourceManagement;
|
||||
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
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
@@ -43,7 +44,7 @@ internal partial class Clyde
|
||||
private void ProcessSpriteEntities(MapId map, Viewport view, IEye eye, Box2Rotated worldBounds, RefList<SpriteData> list)
|
||||
{
|
||||
var query = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var viewScale = eye.Scale * view.RenderScale * (EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter);
|
||||
var viewScale = eye.Scale * view.RenderScale * new Vector2(EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter);
|
||||
var treeData = new BatchData()
|
||||
{
|
||||
Sys = _entityManager.EntitySysManager.GetEntitySystem<TransformSystem>(),
|
||||
@@ -59,11 +60,12 @@ internal partial class Clyde
|
||||
var index = 0;
|
||||
var added = 0;
|
||||
var opts = new ParallelOptions { MaxDegreeOfParallelism = _parMan.ParallelProcessCount };
|
||||
foreach (var comp in _entitySystemManager.GetEntitySystem<SpriteTreeSystem>().GetIntersectingTrees(map, worldBounds))
|
||||
var xformSystem = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
foreach (var (treeOwner, comp) in _entitySystemManager.GetEntitySystem<SpriteTreeSystem>().GetIntersectingTrees(map, worldBounds))
|
||||
{
|
||||
var treeOwner = comp.Owner;
|
||||
var treeXform = query.GetComponent(comp.Owner);
|
||||
var bounds = treeXform.InvWorldMatrix.TransformBox(worldBounds);
|
||||
var treeXform = query.GetComponent(treeOwner);
|
||||
var bounds = xformSystem.GetInvWorldMatrix(treeOwner).TransformBox(worldBounds);
|
||||
DebugTools.Assert(treeXform.MapUid == treeXform.ParentUid || !treeXform.ParentUid.IsValid());
|
||||
|
||||
treeData = treeData with
|
||||
@@ -88,7 +90,7 @@ internal partial class Clyde
|
||||
// Get bounding boxes & world positions
|
||||
added = list.Count - index;
|
||||
var batches = added/_spriteProcessingBatchSize;
|
||||
|
||||
|
||||
// TODO also do sorting here & use a merge sort later on for y-sorting?
|
||||
if (batches > 1)
|
||||
Parallel.For(0, batches, opts, (i) => ProcessSprites(list, index + i * _spriteProcessingBatchSize, _spriteProcessingBatchSize, treeData));
|
||||
@@ -121,7 +123,7 @@ internal partial class Clyde
|
||||
|
||||
// To help explain the remainder of this function, it should be functionally equivalent to the following
|
||||
// three lines of code, but has been expanded & simplified to speed up the calculation:
|
||||
//
|
||||
//
|
||||
// (data.WorldPos, data.WorldRot) = batch.Sys.GetWorldPositionRotation(data.Xform, batch.Query);
|
||||
// var spriteWorldBB = data.Sprite.CalculateRotatedBoundingBox(data.WorldPos, data.WorldRot, batch.ViewRotation);
|
||||
// data.SpriteScreenBB = Viewport.GetWorldToLocalMatrix().TransformBox(spriteWorldBB);
|
||||
@@ -160,7 +162,7 @@ internal partial class Clyde
|
||||
|
||||
/// <summary>
|
||||
/// This is effectively a specialized combination of a <see cref="Matrix3.TransformBox(in Box2Rotated)"/> and <see cref="Box2Rotated.CalcBoundingBox()"/>.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe Box2 TransformCenteredBox(in Box2 box, float angle, in Vector2 offset, in Vector2 scale)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
@@ -162,7 +163,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (Eye == null)
|
||||
return Matrix3.Identity;
|
||||
|
||||
Eye.GetViewMatrix(out var viewMatrix, RenderScale * (EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter));
|
||||
Eye.GetViewMatrix(out var viewMatrix, RenderScale * new Vector2(EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter));
|
||||
viewMatrix.R0C2 += Size.X / 2f;
|
||||
viewMatrix.R1C2 += Size.Y / 2f;
|
||||
return viewMatrix;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Input;
|
||||
|
||||
@@ -176,6 +176,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_entityManager.EventBus.SubscribeEvent<GridModifiedEvent>(EventSource.Local, this, _updateOnGridModified);
|
||||
}
|
||||
|
||||
public void ShutdownGridEcsEvents()
|
||||
{
|
||||
_entityManager.EventBus.UnsubscribeEvent<TileChangedEvent>(EventSource.Local, this);
|
||||
_entityManager.EventBus.UnsubscribeEvent<GridStartupEvent>(EventSource.Local, this);
|
||||
_entityManager.EventBus.UnsubscribeEvent<GridRemovalEvent>(EventSource.Local, this);
|
||||
_entityManager.EventBus.UnsubscribeEvent<GridModifiedEvent>(EventSource.Local, this);
|
||||
}
|
||||
|
||||
private void GLInitBindings(bool gles)
|
||||
{
|
||||
_glBindingsContext = _glContext!.BindingsContext;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
@@ -13,6 +14,8 @@ 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
|
||||
{
|
||||
@@ -26,7 +29,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public IClydeWindow MainWindow { get; }
|
||||
public Vector2i ScreenSize => (1280, 720);
|
||||
public IEnumerable<IClydeWindow> AllWindows => _windows;
|
||||
public Vector2 DefaultWindowScale => (1, 1);
|
||||
public Vector2 DefaultWindowScale => new Vector2(1, 1);
|
||||
public bool IsFocused => true;
|
||||
private readonly List<IClydeWindow> _windows = new();
|
||||
private int _nextWindowId = 2;
|
||||
@@ -85,6 +88,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void ShutdownGridEcsEvents()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void SetWindowTitle(string title)
|
||||
{
|
||||
// Nada.
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
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
|
||||
{
|
||||
|
||||
@@ -12,9 +12,9 @@ void main()
|
||||
|
||||
lowp vec4 COLOR;
|
||||
|
||||
// [SHADER_CODE]
|
||||
|
||||
lowp vec3 lightSample = texture2D(lightMap, Pos).rgb;
|
||||
|
||||
// [SHADER_CODE]
|
||||
|
||||
gl_FragColor = zAdjustResult(COLOR * VtxModulate * vec4(lightSample, 1.0));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using OpenToolkit.GraphicsLibraryFramework;
|
||||
using Robust.Client.Input;
|
||||
@@ -107,7 +108,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (windowReg == null)
|
||||
return;
|
||||
|
||||
var newPos = ((float) ev.XPos, (float) ev.YPos) * windowReg.PixelRatio;
|
||||
var newPos = new Vector2((float) ev.XPos, (float) ev.YPos) * windowReg.PixelRatio;
|
||||
var delta = newPos - windowReg.LastMousePos;
|
||||
windowReg.LastMousePos = newPos;
|
||||
|
||||
@@ -178,7 +179,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return;
|
||||
|
||||
var eventArgs = new MouseWheelEventArgs(
|
||||
((float) ev.XOffset, (float) ev.YOffset),
|
||||
new((float) ev.XOffset, (float) ev.YOffset),
|
||||
new ScreenCoordinates(windowReg.LastMousePos, windowReg.Id));
|
||||
_clyde.SendScroll(eventArgs);
|
||||
}
|
||||
@@ -235,7 +236,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (windowReg == null)
|
||||
return;
|
||||
|
||||
windowReg.WindowScale = (ev.XScale, ev.YScale);
|
||||
windowReg.WindowScale = new Vector2(ev.XScale, ev.YScale);
|
||||
_clyde.SendWindowContentScaleChanged(new WindowContentScaleEventArgs(windowReg.Handle));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using OpenToolkit.GraphicsLibraryFramework;
|
||||
@@ -559,7 +560,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
reg.FramebufferSize = (fbW, fbH);
|
||||
|
||||
GLFW.GetWindowContentScale(window, out var scaleX, out var scaleY);
|
||||
reg.WindowScale = (scaleX, scaleY);
|
||||
reg.WindowScale = new Vector2(scaleX, scaleY);
|
||||
|
||||
GLFW.GetWindowSize(window, out var w, out var h);
|
||||
reg.PrevWindowSize = reg.WindowSize = (w, h);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static SDL2.SDL;
|
||||
using static SDL2.SDL.SDL_EventType;
|
||||
@@ -141,7 +141,7 @@ internal partial class Clyde
|
||||
return;
|
||||
|
||||
var eventArgs = new MouseWheelEventArgs(
|
||||
(ev.XOffset, ev.YOffset),
|
||||
new Vector2(ev.XOffset, ev.YOffset),
|
||||
new ScreenCoordinates(windowReg.LastMousePos, windowReg.Id));
|
||||
|
||||
_clyde.SendScroll(eventArgs);
|
||||
@@ -165,7 +165,7 @@ internal partial class Clyde
|
||||
if (windowReg == null)
|
||||
return;
|
||||
|
||||
var newPos = (ev.X, ev.Y) * windowReg.PixelRatio;
|
||||
var newPos = new Vector2(ev.X, ev.Y) * windowReg.PixelRatio;
|
||||
// SDL2 does give us delta positions, but I'm worried about rounding errors thanks to DPI stuff.
|
||||
var delta = newPos - windowReg.LastMousePos;
|
||||
windowReg.LastMousePos = newPos;
|
||||
@@ -205,10 +205,11 @@ internal partial class Clyde
|
||||
return;
|
||||
|
||||
windowReg.PixelRatio = windowReg.FramebufferSize / (Vector2)windowReg.WindowSize;
|
||||
var newScale = new Vector2(ev.XScale, ev.YScale);
|
||||
|
||||
if (windowReg.WindowScale != (ev.XScale, ev.YScale))
|
||||
if (!windowReg.WindowScale.Equals(newScale))
|
||||
{
|
||||
windowReg.WindowScale = (ev.XScale, ev.YScale);
|
||||
windowReg.WindowScale = newScale;
|
||||
_clyde.SendWindowContentScaleChanged(new WindowContentScaleEventArgs(windowReg.Handle));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Maths;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static SDL2.SDL;
|
||||
using static SDL2.SDL.SDL_EventType;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Map;
|
||||
@@ -513,7 +514,7 @@ internal partial class Clyde
|
||||
cmd.Tcs.TrySetResult(SDL_GetClipboardText());
|
||||
}
|
||||
|
||||
private static (float h, float v) GetWindowScale(nint window)
|
||||
private static Vector2 GetWindowScale(nint window)
|
||||
{
|
||||
// Get scale by diving size in pixels with size in points.
|
||||
SDL_GetWindowSizeInPixels(window, out var pixW, out var pixH);
|
||||
@@ -521,7 +522,7 @@ internal partial class Clyde
|
||||
|
||||
// Avoiding degenerate cases, not sure if these can actually happen.
|
||||
if (pixW == 0 || pixH == 0 || pointW == 0 || pointH == 0)
|
||||
return (1, 1);
|
||||
return new Vector2(1, 1);
|
||||
|
||||
var scaleH = pixW / (float) pointW;
|
||||
var scaleV = pixH / (float) pointH;
|
||||
@@ -530,7 +531,7 @@ internal partial class Clyde
|
||||
scaleH = MathF.Round(scaleH * 20) / 20;
|
||||
scaleV = MathF.Round(scaleV * 20) / 20;
|
||||
|
||||
return (scaleH, scaleV);
|
||||
return new Vector2(scaleH, scaleV);
|
||||
}
|
||||
|
||||
private static void CheckWindowDisposed(WindowReg reg)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -99,7 +100,7 @@ namespace Robust.Client.Graphics
|
||||
Color colorLinear = Color.FromSrgb(color);
|
||||
for (var i = 0; i < output.Length; i++)
|
||||
{
|
||||
output[i] = new DrawVertexUV2DColor(input[i], (0.5f, 0.5f), colorLinear);
|
||||
output[i] = new DrawVertexUV2DColor(input[i], new Vector2(0.5f, 0.5f), colorLinear);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -37,10 +38,16 @@ namespace Robust.Client.Graphics
|
||||
_paddingTop = other._paddingTop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum size, in virtual pixels.
|
||||
/// </summary>
|
||||
public Vector2 MinimumSize =>
|
||||
new(GetContentMargin(Margin.Left) + GetContentMargin(Margin.Right),
|
||||
GetContentMargin(Margin.Top) + GetContentMargin(Margin.Bottom));
|
||||
|
||||
/// <summary>
|
||||
/// Left content margin, in virtual pixels.
|
||||
/// </summary>
|
||||
public float? ContentMarginLeftOverride
|
||||
{
|
||||
get => _contentMarginLeftOverride;
|
||||
@@ -55,6 +62,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Top content margin, in virtual pixels.
|
||||
/// </summary>
|
||||
public float? ContentMarginTopOverride
|
||||
{
|
||||
get => _contentMarginTopOverride;
|
||||
@@ -69,6 +79,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Right content margin, in virtual pixels.
|
||||
/// </summary>
|
||||
public float? ContentMarginRightOverride
|
||||
{
|
||||
get => _contentMarginRightOverride;
|
||||
@@ -83,6 +96,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bottom content margin, in virtual pixels.
|
||||
/// </summary>
|
||||
public float? ContentMarginBottomOverride
|
||||
{
|
||||
get => _contentMarginBottomOverride;
|
||||
@@ -97,6 +113,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left padding, in virtual pixels.
|
||||
/// </summary>
|
||||
public float PaddingLeft
|
||||
{
|
||||
get => _paddingLeft;
|
||||
@@ -111,6 +130,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bottom padding, in virtual pixels.
|
||||
/// </summary>
|
||||
public float PaddingBottom
|
||||
{
|
||||
get => _paddingBottom;
|
||||
@@ -125,6 +147,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Right padding, in virtual pixels.
|
||||
/// </summary>
|
||||
public float PaddingRight
|
||||
{
|
||||
get => _paddingRight;
|
||||
@@ -139,6 +164,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Top padding, in virtual pixels.
|
||||
/// </summary>
|
||||
public float PaddingTop
|
||||
{
|
||||
get => _paddingTop;
|
||||
@@ -153,6 +181,9 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Padding, in virtual pixels.
|
||||
/// </summary>
|
||||
public Thickness Padding
|
||||
{
|
||||
set
|
||||
@@ -165,11 +196,11 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw this style box to the screen at the specified coordinates.
|
||||
/// Draw this style box to the screen at the specified coordinates. This is using physical pixels, not virtual pixels.
|
||||
/// </summary>
|
||||
/// <param name="handle"></param>
|
||||
/// <param name="box"></param>
|
||||
public void Draw(DrawingHandleScreen handle, UIBox2 box)
|
||||
public void Draw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
|
||||
{
|
||||
box = new UIBox2(
|
||||
box.Left + PaddingLeft,
|
||||
@@ -178,11 +209,11 @@ namespace Robust.Client.Graphics
|
||||
box.Bottom - PaddingBottom
|
||||
);
|
||||
|
||||
DoDraw(handle, box);
|
||||
DoDraw(handle, box, uiScale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset from a margin of the box to where content should actually be drawn.
|
||||
/// Gets the offset from a margin of the box to where content should actually be drawn. This is in virtual pixels.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown if <paramref name="margin"/> is a compound is not a single margin flag.
|
||||
@@ -212,6 +243,9 @@ namespace Robust.Client.Graphics
|
||||
return contentMargin + GetPadding(margin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the padding. This is in virtual pixels.
|
||||
/// </summary>
|
||||
public float GetPadding(Margin margin)
|
||||
{
|
||||
switch (margin)
|
||||
@@ -230,26 +264,27 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the offsets of the content region of this box,
|
||||
/// if this box is drawn from the given position.
|
||||
/// Returns the offsets of the content region of this box when drawn from the given position. Input and
|
||||
/// output positions are in real pixels, though virtual pixels can also be used if the ui scale is set to 1.
|
||||
/// </summary>
|
||||
public Vector2 GetContentOffset(Vector2 basePosition)
|
||||
public Vector2 GetContentOffset(Vector2 basePosition, float uiScale)
|
||||
{
|
||||
return basePosition + (GetContentMargin(Margin.Left), GetContentMargin(Margin.Top));
|
||||
return basePosition + uiScale * new Vector2(GetContentMargin(Margin.Left), GetContentMargin(Margin.Top));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the box considered the "contents" of this style box, when drawn at a specific size.
|
||||
/// Gets the box considered the "contents" of this style box, when drawn at a specific size. Input and output
|
||||
/// boxes are in virtual pixels, though virtual pixels can also be used if the ui scale is set to 1.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="baseBox"/> is too small and the resultant box would have negative dimensions.
|
||||
/// </exception>
|
||||
public UIBox2 GetContentBox(UIBox2 baseBox)
|
||||
public UIBox2 GetContentBox(UIBox2 baseBox, float uiScale)
|
||||
{
|
||||
var left = baseBox.Left + GetContentMargin(Margin.Left);
|
||||
var top = baseBox.Top + GetContentMargin(Margin.Top);
|
||||
var right = baseBox.Right - GetContentMargin(Margin.Right);
|
||||
var bottom = baseBox.Bottom - GetContentMargin(Margin.Bottom);
|
||||
var left = baseBox.Left + GetContentMargin(Margin.Left) * uiScale;
|
||||
var top = baseBox.Top + GetContentMargin(Margin.Top) * uiScale;
|
||||
var right = baseBox.Right - GetContentMargin(Margin.Right) * uiScale;
|
||||
var bottom = baseBox.Bottom - GetContentMargin(Margin.Bottom) * uiScale;
|
||||
|
||||
return new UIBox2(left, top, right, bottom);
|
||||
}
|
||||
@@ -257,19 +292,22 @@ namespace Robust.Client.Graphics
|
||||
/// <summary>
|
||||
/// Gets the draw box, positioned at <paramref name="position"/>,
|
||||
/// that envelops a box with the given dimensions perfectly given this box's content margins.
|
||||
/// Input and output values are in physical pixels, though virtual pixels can also be used if the ui scale
|
||||
/// is set to 1.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It's basically a reverse <see cref="GetContentBox"/>.
|
||||
/// </remarks>
|
||||
/// <param name="position">The position at which the new box should be drawn.</param>
|
||||
/// <param name="dimensions">The dimensions of the content box inside this new box.</param>
|
||||
/// <param name="uiScale">Scales the content margin border size</param>
|
||||
/// <returns>
|
||||
/// A box that, when ran through <see cref="GetContentBox"/>,
|
||||
/// has a content box of size <paramref name="dimensions"/>
|
||||
/// </returns>
|
||||
public UIBox2 GetEnvelopBox(Vector2 position, Vector2 dimensions)
|
||||
public UIBox2 GetEnvelopBox(Vector2 position, Vector2 dimensions, float uiScale)
|
||||
{
|
||||
return UIBox2.FromDimensions(position, dimensions + MinimumSize);
|
||||
return UIBox2.FromDimensions(position, dimensions + MinimumSize * uiScale);
|
||||
}
|
||||
|
||||
public void SetContentMarginOverride(Margin margin, float value)
|
||||
@@ -318,7 +356,13 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void DoDraw(DrawingHandleScreen handle, UIBox2 box);
|
||||
/// <summary>
|
||||
/// Draw the style box in the given UI Box.
|
||||
/// </summary>
|
||||
/// <param name="handle"></param>
|
||||
/// <param name="box">The area to draw in, in physical pixels</param>
|
||||
/// <param name="uiScale">The UI scale to use.</param>
|
||||
protected abstract void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale);
|
||||
|
||||
protected virtual float GetDefaultContentMargin(Margin margin)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
public sealed class StyleBoxEmpty : StyleBox
|
||||
{
|
||||
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box)
|
||||
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
|
||||
{
|
||||
// It's empty what more do you want?
|
||||
}
|
||||
|
||||
@@ -7,11 +7,16 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
public Color BackgroundColor { get; set; }
|
||||
public Color BorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Thickness of the border, in virtual pixels.
|
||||
/// </summary>
|
||||
public Thickness BorderThickness { get; set; }
|
||||
|
||||
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box)
|
||||
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
|
||||
{
|
||||
var (btl, btt, btr, btb) = BorderThickness;
|
||||
var thickness = BorderThickness.Scale(uiScale);
|
||||
var (btl, btt, btr, btb) = thickness;
|
||||
if (btl > 0)
|
||||
handle.DrawRect(new UIBox2(box.Left, box.Top, box.Left + btl, box.Bottom), BorderColor);
|
||||
|
||||
@@ -24,7 +29,7 @@ namespace Robust.Client.Graphics
|
||||
if (btb > 0)
|
||||
handle.DrawRect(new UIBox2(box.Left, box.Bottom - btb, box.Right, box.Bottom), BorderColor);
|
||||
|
||||
handle.DrawRect(BorderThickness.Deflate(box), BackgroundColor);
|
||||
handle.DrawRect(thickness.Deflate(box), BackgroundColor);
|
||||
}
|
||||
|
||||
public StyleBoxFlat()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -37,21 +38,57 @@ namespace Robust.Client.Graphics
|
||||
|
||||
Texture = copy.Texture;
|
||||
Modulate = copy.Modulate;
|
||||
TextureScale = copy.TextureScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left expansion size, in virtual pixels.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
|
||||
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
|
||||
/// <see cref="TextureScale"/>.
|
||||
/// </remarks>
|
||||
public float ExpandMarginLeft { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Top expansion size, in virtual pixels.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
|
||||
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
|
||||
/// <see cref="TextureScale"/>.
|
||||
/// </remarks>
|
||||
public float ExpandMarginTop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bottom expansion size, in virtual pixels.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
|
||||
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
|
||||
/// <see cref="TextureScale"/>.
|
||||
/// </remarks>
|
||||
public float ExpandMarginBottom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Right expansion size, in virtual pixels.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This expands the size of the area where the patches get drawn. This will cause the drawn texture to
|
||||
/// extend beyond the box passed to the <see cref="StyleBox.Draw"/> function. This is not affected by
|
||||
/// <see cref="TextureScale"/>.
|
||||
/// </remarks>
|
||||
public float ExpandMarginRight { get; set; }
|
||||
|
||||
public StretchMode Mode { get; set; } = StretchMode.Stretch;
|
||||
|
||||
private float _patchMarginLeft;
|
||||
|
||||
// Distance of the left patch margin from the image. In texture space.
|
||||
/// <summary>
|
||||
/// Distance of the left patch margin from the image. In texture space.
|
||||
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
|
||||
/// </summary>
|
||||
public float PatchMarginLeft
|
||||
{
|
||||
get => _patchMarginLeft;
|
||||
@@ -68,7 +105,10 @@ namespace Robust.Client.Graphics
|
||||
|
||||
private float _patchMarginRight;
|
||||
|
||||
// Distance of the right patch margin from the image. In texture space.
|
||||
/// <summary>
|
||||
/// Distance of the right patch margin from the image. In texture space.
|
||||
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
|
||||
/// </summary>
|
||||
public float PatchMarginRight
|
||||
{
|
||||
get => _patchMarginRight;
|
||||
@@ -85,7 +125,10 @@ namespace Robust.Client.Graphics
|
||||
|
||||
private float _patchMarginTop;
|
||||
|
||||
// Distance of the top patch margin from the image. In texture space.
|
||||
/// <summary>
|
||||
/// Distance of the top patch margin from the image. In texture space.
|
||||
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
|
||||
/// </summary>
|
||||
public float PatchMarginTop
|
||||
{
|
||||
get => _patchMarginTop;
|
||||
@@ -102,7 +145,10 @@ namespace Robust.Client.Graphics
|
||||
|
||||
private float _patchMarginBottom;
|
||||
|
||||
// Distance of the bottom patch margin from the image. In texture space.
|
||||
/// <summary>
|
||||
/// Distance of the bottom patch margin from the image. In texture space.
|
||||
/// The size of this patch in virtual pixels can be obtained by scaling this with <see cref="TextureScale"/>.
|
||||
/// </summary>
|
||||
public float PatchMarginBottom
|
||||
{
|
||||
get => _patchMarginBottom;
|
||||
@@ -121,7 +167,9 @@ namespace Robust.Client.Graphics
|
||||
|
||||
public Texture? Texture { get; set; }
|
||||
|
||||
// Applies an additional x/y scale to the teture
|
||||
/// <summary>
|
||||
/// Additional scaling to use when drawing the texture.
|
||||
/// </summary>
|
||||
public Vector2 TextureScale { get; set; } = Vector2.One;
|
||||
|
||||
public void SetPatchMargin(Margin margin, float value)
|
||||
@@ -170,7 +218,7 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box)
|
||||
protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
|
||||
{
|
||||
if (Texture == null)
|
||||
{
|
||||
@@ -178,13 +226,13 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
box = new UIBox2(
|
||||
box.Left - ExpandMarginLeft,
|
||||
box.Top - ExpandMarginTop,
|
||||
box.Right + ExpandMarginRight,
|
||||
box.Bottom + ExpandMarginBottom);
|
||||
box.Left - ExpandMarginLeft * uiScale,
|
||||
box.Top - ExpandMarginTop * uiScale,
|
||||
box.Right + ExpandMarginRight * uiScale,
|
||||
box.Bottom + ExpandMarginBottom * uiScale);
|
||||
|
||||
var scaledMargin = new UIBox2(PatchMarginLeft * TextureScale.X, PatchMarginTop * TextureScale.Y,
|
||||
PatchMarginRight * TextureScale.X, PatchMarginBottom * TextureScale.Y);
|
||||
var scaledMargin = new UIBox2(PatchMarginLeft * TextureScale.X * uiScale, PatchMarginTop * TextureScale.Y * uiScale,
|
||||
PatchMarginRight * TextureScale.X * uiScale, PatchMarginBottom * TextureScale.Y * uiScale);
|
||||
|
||||
if (PatchMarginLeft > 0)
|
||||
{
|
||||
@@ -203,7 +251,7 @@ namespace Robust.Client.Graphics
|
||||
new UIBox2(0, scaledMargin.Top, scaledMargin.Left, box.Height - scaledMargin.Bottom)
|
||||
.Translated(box.TopLeft);
|
||||
DrawStretchingArea(handle, leftBox,
|
||||
new UIBox2(0, PatchMarginTop, PatchMarginLeft, Texture.Height - PatchMarginBottom));
|
||||
new UIBox2(0, PatchMarginTop, PatchMarginLeft, Texture.Height - PatchMarginBottom), uiScale);
|
||||
}
|
||||
|
||||
if (PatchMarginBottom > 0)
|
||||
@@ -238,7 +286,7 @@ namespace Robust.Client.Graphics
|
||||
DrawStretchingArea(handle, rightBox,
|
||||
new UIBox2(Texture.Width - PatchMarginRight, PatchMarginTop,
|
||||
Texture.Width,
|
||||
Texture.Height - PatchMarginBottom));
|
||||
Texture.Height - PatchMarginBottom), uiScale);
|
||||
}
|
||||
|
||||
if (PatchMarginBottom > 0)
|
||||
@@ -260,7 +308,7 @@ namespace Robust.Client.Graphics
|
||||
new UIBox2(scaledMargin.Left, 0, box.Width - scaledMargin.Right, scaledMargin.Top)
|
||||
.Translated(box.TopLeft);
|
||||
DrawStretchingArea(handle, topBox,
|
||||
new UIBox2(PatchMarginLeft, 0, Texture.Width - PatchMarginRight, PatchMarginTop));
|
||||
new UIBox2(PatchMarginLeft, 0, Texture.Width - PatchMarginRight, PatchMarginTop), uiScale);
|
||||
}
|
||||
|
||||
if (PatchMarginBottom > 0)
|
||||
@@ -274,7 +322,7 @@ namespace Robust.Client.Graphics
|
||||
DrawStretchingArea(handle, bottomBox,
|
||||
new UIBox2(PatchMarginLeft, Texture.Height - PatchMarginBottom,
|
||||
Texture.Width - PatchMarginRight,
|
||||
Texture.Height));
|
||||
Texture.Height), uiScale);
|
||||
}
|
||||
|
||||
// Draw center
|
||||
@@ -283,11 +331,11 @@ namespace Robust.Client.Graphics
|
||||
box.Height - scaledMargin.Bottom).Translated(box.TopLeft);
|
||||
|
||||
DrawStretchingArea(handle, centerBox, new UIBox2(PatchMarginLeft, PatchMarginTop, Texture.Width - PatchMarginRight,
|
||||
Texture.Height - PatchMarginBottom));
|
||||
Texture.Height - PatchMarginBottom), uiScale);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawStretchingArea(DrawingHandleScreen handle, UIBox2 area, UIBox2 texCoords)
|
||||
private void DrawStretchingArea(DrawingHandleScreen handle, UIBox2 area, UIBox2 texCoords, float uiScale)
|
||||
{
|
||||
if (Mode == StretchMode.Stretch)
|
||||
{
|
||||
@@ -300,8 +348,8 @@ namespace Robust.Client.Graphics
|
||||
// TODO: this is an insanely expensive way to do tiling, seriously.
|
||||
// This should 100% be implemented in a shader instead.
|
||||
|
||||
var sectionWidth = texCoords.Width * TextureScale.X;
|
||||
var sectionHeight = texCoords.Height * TextureScale.Y;
|
||||
var sectionWidth = texCoords.Width * TextureScale.X * uiScale;
|
||||
var sectionHeight = texCoords.Height * TextureScale.Y * uiScale;
|
||||
var invScale = Vector2.One / TextureScale;
|
||||
|
||||
for (var x = area.Left; area.Right - x > 0; x += sectionWidth)
|
||||
@@ -327,13 +375,13 @@ namespace Robust.Client.Graphics
|
||||
switch (margin)
|
||||
{
|
||||
case Margin.Top:
|
||||
return PatchMarginTop;
|
||||
return PatchMarginTop * TextureScale.Y;
|
||||
case Margin.Bottom:
|
||||
return PatchMarginBottom;
|
||||
return PatchMarginBottom * TextureScale.Y;
|
||||
case Margin.Right:
|
||||
return PatchMarginRight;
|
||||
return PatchMarginRight * TextureScale.X;
|
||||
case Margin.Left:
|
||||
return PatchMarginLeft;
|
||||
return PatchMarginLeft * TextureScale.X;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(margin), margin, null);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Maths;
|
||||
using SixLabors.ImageSharp;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user