mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
127 Commits
fix/bui-st
...
v226.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bb3d3c4bc | ||
|
|
a9f07dd63c | ||
|
|
2423c861c2 | ||
|
|
87ffd7b7af | ||
|
|
a3a8912f42 | ||
|
|
b40973157d | ||
|
|
1de8731465 | ||
|
|
3a479cb5f4 | ||
|
|
76eeebf439 | ||
|
|
2fa83181e2 | ||
|
|
36f02b4a18 | ||
|
|
e842142dd7 | ||
|
|
2eb740cea8 | ||
|
|
a044f04e3b | ||
|
|
a4723d1f62 | ||
|
|
627c1eb054 | ||
|
|
836aec0b87 | ||
|
|
9116e64291 | ||
|
|
a6bfb5f557 | ||
|
|
5c83678c78 | ||
|
|
eac94b1032 | ||
|
|
efd870d070 | ||
|
|
94f98073b0 | ||
|
|
5aa9378de0 | ||
|
|
850e9ab695 | ||
|
|
7319f3a241 | ||
|
|
b6252c9e4f | ||
|
|
fcd507d1f9 | ||
|
|
1eb874f4c3 | ||
|
|
a628d31c4b | ||
|
|
2b0ecd7166 | ||
|
|
bde650689b | ||
|
|
87d8d74d8c | ||
|
|
dddf13a19a | ||
|
|
75626a86a3 | ||
|
|
3e3cd0e257 | ||
|
|
a3a90154a4 | ||
|
|
9240c94e59 | ||
|
|
a95ba9f181 | ||
|
|
074a4faa92 | ||
|
|
6b4d74f46e | ||
|
|
f648218756 | ||
|
|
b497efb0c0 | ||
|
|
d8f2b917b4 | ||
|
|
6dd6b79db6 | ||
|
|
ff4548f108 | ||
|
|
8f41405b31 | ||
|
|
f285c62674 | ||
|
|
56c30edf04 | ||
|
|
783d529ec4 | ||
|
|
895bfb8ec0 | ||
|
|
6ef67cf513 | ||
|
|
15a2f6702c | ||
|
|
c5c2c2022a | ||
|
|
3c378640dd | ||
|
|
0a149fa91c | ||
|
|
fe8d1d9422 | ||
|
|
c89c529ba4 | ||
|
|
dd56de70b7 | ||
|
|
710408c613 | ||
|
|
2461cd94dd | ||
|
|
721408bb37 | ||
|
|
8b42c1dd46 | ||
|
|
688b0b0458 | ||
|
|
5fa49b5689 | ||
|
|
1f7a9bdf0a | ||
|
|
796abe1230 | ||
|
|
6c2cf26250 | ||
|
|
ce262d5ff8 | ||
|
|
c250010dad | ||
|
|
709c7bc808 | ||
|
|
37918da73c | ||
|
|
6cc2083b09 | ||
|
|
6a6bfe33ca | ||
|
|
9737a4249c | ||
|
|
a48a353939 | ||
|
|
f0b45d95cb | ||
|
|
d69c5500f2 | ||
|
|
cf133ca341 | ||
|
|
b0922b8e0e | ||
|
|
512ebd8422 | ||
|
|
85f74c3ba3 | ||
|
|
da7abc6580 | ||
|
|
b1329d30bf | ||
|
|
12808d073e | ||
|
|
ec794ce4e4 | ||
|
|
6b13475842 | ||
|
|
b48ee22800 | ||
|
|
0b95a4edeb | ||
|
|
ed359481b4 | ||
|
|
1189613908 | ||
|
|
30907d8415 | ||
|
|
7f2da4d4f3 | ||
|
|
e30e963623 | ||
|
|
b056caeed7 | ||
|
|
fbc8086335 | ||
|
|
799702b814 | ||
|
|
63df90f86f | ||
|
|
51f0c60bd3 | ||
|
|
a9ed53f47b | ||
|
|
41c40f1a94 | ||
|
|
6e61c35d35 | ||
|
|
aae0a8bc51 | ||
|
|
cb543240c6 | ||
|
|
1654ab06f5 | ||
|
|
211245215e | ||
|
|
10aaaa65c5 | ||
|
|
d2a2afe82e | ||
|
|
025d90d281 | ||
|
|
c229f2e312 | ||
|
|
fe051a3577 | ||
|
|
51a0ef1e60 | ||
|
|
702dfef5fc | ||
|
|
a0c1ad246f | ||
|
|
1153888bd1 | ||
|
|
ccbb6ddec7 | ||
|
|
970da5f717 | ||
|
|
4d528dd577 | ||
|
|
c83720b163 | ||
|
|
bd87a805d4 | ||
|
|
fff42fb2b4 | ||
|
|
4500669f65 | ||
|
|
7d19ea9338 | ||
|
|
2dc610907d | ||
|
|
beb1c4b1fb | ||
|
|
7e331eaa75 | ||
|
|
caf9e45ad9 |
@@ -7,6 +7,18 @@ indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
|
||||
max_line_length = 120
|
||||
|
||||
# ReSharper properties
|
||||
resharper_csharp_max_line_length = 120
|
||||
resharper_csharp_wrap_after_declaration_lpar = true
|
||||
resharper_csharp_wrap_arguments_style = chop_if_long
|
||||
resharper_csharp_wrap_parameters_style = chop_if_long
|
||||
resharper_keep_existing_attribute_arrangement = true
|
||||
resharper_place_field_attribute_on_same_line = if_owner_is_single_line
|
||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
|
||||
[*.{csproj,xml,yml,dll.config,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
|
||||
6
.github/workflows/publish-client.yml
vendored
6
.github/workflows/publish-client.yml
vendored
@@ -33,10 +33,10 @@ jobs:
|
||||
mkdir "release/${{ steps.parse_version.outputs.version }}"
|
||||
mv release/*.zip "release/${{ steps.parse_version.outputs.version }}"
|
||||
|
||||
- name: Upload files to centcomm
|
||||
- name: Upload files to Suns
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
host: suns.spacestation14.com
|
||||
username: robust-build-push
|
||||
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
|
||||
source: "release/${{ steps.parse_version.outputs.version }}"
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Update manifest JSON
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
host: suns.spacestation14.com
|
||||
username: robust-build-push
|
||||
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
|
||||
script: /home/robust-build-push/push.ps1 ${{ steps.parse_version.outputs.version }}
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
We actually set ManagePackageVersionsCentrally manually in another import file.
|
||||
Since .NET SDK 8.0.300, ManagePackageVersionsCentrally is automatically set if Directory.Packages.props exists.
|
||||
https://github.com/NuGet/NuGet.Client/pull/5572
|
||||
We actively negate this here, as we have some packages in tree we don't want such automatic behavior for.
|
||||
We use Directory.Build.props to get copy the state *after* our MSBuild config but before Nuget's config.
|
||||
-->
|
||||
<ManagePackageVersionsCentrally />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="ILReader.Core" Version="1.0.0.4" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.0" />
|
||||
<PackageVersion Include="Linguini.Bundle" Version="0.1.3" />
|
||||
<PackageVersion Include="Linguini.Bundle" Version="0.8.1" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
|
||||
@@ -45,7 +55,7 @@
|
||||
<PackageVersion Include="Serilog" Version="3.1.1" />
|
||||
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.3" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: 61a56c60bd...1d85b82e05
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
271
RELEASE-NOTES.md
271
RELEASE-NOTES.md
@@ -54,6 +54,277 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 226.0.2
|
||||
|
||||
|
||||
## 226.0.1
|
||||
|
||||
|
||||
## 226.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `IEventBus.RaiseComponentEvent` now requires an EntityUid argument.
|
||||
* The `AddedComponentEventArgs` and `RemovedComponentEventArgs` constructors are now internal
|
||||
|
||||
### New features
|
||||
|
||||
* Allow RequestScreenTexture to be set in overlays.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix AnimationCompletedEvent not always going out.
|
||||
|
||||
|
||||
## 225.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `NetEntity.Parse` and `TryParse` will now fail to parse empty strings.
|
||||
* Try to prevent EventBus looping. This also caps the amount of directed component subscriptions for a particular component to 256.
|
||||
|
||||
### New features
|
||||
|
||||
* `IPrototypeManager.TryIndex` will now default to logging errors if passed an invalid prototype id struct (i,e., `EntProtoId` or `ProtoId<T>`). There is a new optional bool argument to disable logging errors.
|
||||
* `Eye` now allows its `Position` to be set directly. Please only do this with the `FixedEye` child type constructed manually.
|
||||
* Engine now respects the hub's `can_skip_build` parameter on info query, fixing an issue where the first hub advertisement fails due to ACZ taking too long.
|
||||
* Add GetSession & TryGetSession to ActorSystem.
|
||||
* Raise an event when an entity's name is changed.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* The `ent` toolshed command now takes `NetEntity` values, fixing parsing in practical uses.
|
||||
* Fix ComponentFactory test mocks.
|
||||
* Fix LookupFlags missing from a couple of EntityLookupSystem methods.
|
||||
|
||||
### Other
|
||||
|
||||
* Improved engine's Happy Eyeballs implementation, should result in more usage of IPv6 for HTTP APIs when available.
|
||||
* Remove CompIdx locks to improve performance inside Pvs at higher player counts.
|
||||
* Avoid a read lock in GetEntityQuery to also improve performance.
|
||||
* Mark `EntityManager.System<T>` as Pure.
|
||||
|
||||
|
||||
## 224.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed UserInterfaceSystem sometimes throwing a key-not-found exception when trying to close UIs.
|
||||
|
||||
|
||||
## 224.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* `ServerIntegrationInstance` has new methods for adding dummy player sessions for tests that require multiple players.
|
||||
* Linguini has been updated to v0.8.1. Errors will now be logged when a duplicate localization key is found.
|
||||
* Added `UserInterfaceSystem.SetUi()` for modifying the `InterfaceData` associated with some BUI.
|
||||
* Added the `EntityPrototypeView` control for spawning & rendering an entity prototype.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix `UserInterfaceSystem` spamming client side errors when entities with UIs open are deleted while outside of PVS range.
|
||||
* Fix Toolshed's EnumTypeParse not working enum values with upercase characters.
|
||||
* Fixed `incmd` command not working due to an invalid cast.
|
||||
|
||||
### Other
|
||||
|
||||
* There have been various performance improvements to replay loading & playback.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added `DummySession` and `DummyChannel` classes for use in integration tests and benchmarks to fool the server into thinking that there are multiple players connected.
|
||||
* Added `ICommonSessionInternal` and updated `CommonSession` so that the internal setters now go through that interface.
|
||||
|
||||
## 224.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixes PVS throwing exceptions when invalid entities are passed to `ExpandPvsEvent`. Now it just logs an error.
|
||||
* Fixes BUIs not properly closing, resulting in invalid entities in `UserInterfaceUserComponent.OpenInterfaces`
|
||||
* Fixes an unknown/invalid prototype exception sometimes being thrown when running ``IPrototypeManager.ResolveResults()`
|
||||
|
||||
|
||||
## 224.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `Matrix3` has been replaced with `System.Numerics.Matrix3x2`. Various Matrix related methods have been turned into extension methods in the `Matrix3Helpers` class.
|
||||
* Engine `EntityCategory` prototype IDs have been changed to use CamelCase. I.e., `hideSpawnMenu` -> `HideSpawnMenu`
|
||||
* Prototypes can now be implicitly cast `ProtoId<T>` or `EntProtoId` ID structs. The new implicit cast might cause previous function calls to be ambiguous.
|
||||
|
||||
### New features
|
||||
|
||||
* `Array.Clear(Array)` is now available in the sandbox.
|
||||
* BUIs now use `ExpandPvsEvent`. I.e., if a player has a UI open, then the entity associated with that UI will always get sent to the player by the PVS system.
|
||||
* Added `cvar_subs` command for listing all subscribers to cvar changes
|
||||
* Entity categories have been reworked
|
||||
* Each category now has a `HideSpawnMenu` field. The old `HideSpawnMenu` category is now just a normal category with that field set to true.
|
||||
* Reworked category inheritance. Inheritance can now be disabled per category using a `Inheritable` field.
|
||||
* Entity prototypes can now be automatically added to categories based on the components that they have, either by specifying components when defining the category in yml, or by adding the EntityCategoryAttribute to the component class.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed client-side BUI error log spam if an unknown entity has a UI open.
|
||||
* Fixed placement manager spawning entities with incorrect rotations.
|
||||
|
||||
### Other
|
||||
|
||||
* Added a try-catch block to BUI constructors, to avoid clients getting stuck in error loops while applying states.
|
||||
* Attempting to play sounds on terminating entities no longer logs an error.
|
||||
|
||||
|
||||
## 223.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Better exception logging for IRobustJob.
|
||||
* Add SetGridAudio helper for SharedAudioSystem.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix placement manager not setting entity rotation correctly.
|
||||
* Fix grid-based audio not playing correctly.
|
||||
|
||||
|
||||
## 223.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added several new `FormattedMessage` methods for better exception tolerance when parsing markup. Several existing methods have been marked as obsolete, with new renamed methods taking their place.
|
||||
|
||||
|
||||
## 223.1.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* `MapGridComponent.LastTileModifiedTick` is now actually networked to clients.
|
||||
|
||||
|
||||
## 223.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed an exception caused by enum cvars using integer type values instead of enum values
|
||||
|
||||
|
||||
## 223.1.0
|
||||
|
||||
### Other
|
||||
|
||||
* Various `ContainerSystem` methods have been obsoleted in favour of overrides that take in an `Entity` struct instead of `EntityUid`
|
||||
* Various `EntityCoordinates` methods have been obsoleted with replacements added to `SharedTransformSystem`
|
||||
|
||||
|
||||
## 223.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The `ComponentState` class is now abstract. Networked components that don't have state information now just return a null state.
|
||||
* The way that delta component states work has changed. It now expects there to be two different state classes, only one of which should implement `IComponentDeltaState<TFullState>`
|
||||
|
||||
### New features
|
||||
|
||||
* A new `replay.checkpoint_min_interval` cvar has been added. It can be used to limit the frequency at which checkpoints are generated when loading a replay.
|
||||
* Added `IConfigurationManager.OnCVarValueChanged`. This is a c# event that gets invoked whenever any cvar value changes.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* `IEyeManager.GetWorldViewbounds()` and `IEyeManager.GetWorldViewbounds()` should now return the correct bounds if the main viewport does not take up the whole screen.
|
||||
|
||||
### Other
|
||||
|
||||
* The default values of various replay related cvars have been changed to try and reduce memory usage.
|
||||
|
||||
|
||||
## 222.4.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added the following types from `System.Numerics` to the sandbox: `Complex`, `Matrix3x2`, `Matrix4x4`, `Plane`, `Quaternion`, `Vector3`, `Vector4`.
|
||||
|
||||
|
||||
## 222.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* `ITileDefinition.EditorHidden` allows hiding a tile from the tile spawn panel.
|
||||
* Ordered event subscriptions now take child types into account, so ordering based on a shared type will work.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Cross-map BUI range checks now work.
|
||||
* Paused entities update on prototype reload.
|
||||
|
||||
### Other
|
||||
|
||||
* Fixed build compatibility with .NET 8.0.300 SDK, due to changes in how Central Package Management behaves.
|
||||
* Physics component has delta states to reduce network usage.
|
||||
|
||||
|
||||
## 222.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added `EntityQuery.Comp()` (abbreviation of `GetComponent()`)
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix `SerializationManager.TryGetVariableType` checking the wrong property.
|
||||
* Fixed GrammarSystem mispredicting a character's gender
|
||||
|
||||
### Other
|
||||
|
||||
* User interface system now performs range checks in parallel
|
||||
|
||||
|
||||
## 222.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed never setting BoundUserInterface.State.
|
||||
|
||||
### Other
|
||||
|
||||
* Add truncate for filesaving.
|
||||
* Add method for getting the type of a data field by name from ISerializationManager.
|
||||
|
||||
|
||||
## 222.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added `BoundKeyEventArgs.IsRepeat`.
|
||||
* Added `net.lidgren_log_warning` and `net.lidgren_log_error` CVars.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix assert trip when holding repeatable keybinds.
|
||||
|
||||
### Other
|
||||
|
||||
* Updated Lidgren to v0.3.1. This should provide performance improvements if warning/error logs are disabled.
|
||||
|
||||
|
||||
## 222.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Mark IComponentFactory argument in EntityPrototype as mandatory.
|
||||
|
||||
### New features
|
||||
|
||||
* Add `EntProtoId<T>` to check for components on the attached entity as well.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PVS iterating duplicate chunks for multiple viewsubscriptions.
|
||||
|
||||
### Other
|
||||
|
||||
* Defer clientside BUI opens if it's the first state that comes in.
|
||||
|
||||
|
||||
## 221.2.0
|
||||
|
||||
### New features
|
||||
|
||||
@@ -2,4 +2,7 @@
|
||||
id: Audio
|
||||
name: Audio
|
||||
description: Audio entity used by engine
|
||||
save: false
|
||||
save: false
|
||||
components:
|
||||
- type: Transform
|
||||
gridTraversal: false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
- type: entity
|
||||
id: debugRotation
|
||||
abstract: true
|
||||
categories: [ debug ]
|
||||
categories: [ Debug ]
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
# debug related entities
|
||||
- type: entityCategory
|
||||
id: debug
|
||||
id: Debug
|
||||
name: entity-category-name-debug
|
||||
description: entity-category-desc-debug
|
||||
suffix: entity-category-suffix-debug
|
||||
|
||||
# entities that spawn other entities
|
||||
- type: entityCategory
|
||||
id: spawner
|
||||
id: Spawner
|
||||
name: entity-category-name-spawner
|
||||
description: entity-category-desc-spawner
|
||||
|
||||
# entities that should be hidden from the spawn menu
|
||||
# simple category that just exists to hide prototypes in spawn menus
|
||||
- type: entityCategory
|
||||
id: hideSpawnMenu
|
||||
id: HideSpawnMenu
|
||||
name: entity-category-name-hide
|
||||
description: entity-category-desc-hide
|
||||
hideSpawnMenu: true
|
||||
inheritable: false
|
||||
|
||||
@@ -9,6 +9,7 @@ cmd-parse-failure-float = {$arg} is not a valid float.
|
||||
cmd-parse-failure-bool = {$arg} is not a valid bool.
|
||||
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
|
||||
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
|
||||
cmd-parse-failure-enum = {$arg} is not a {$enum} Enum.
|
||||
cmd-parse-failure-grid = {$arg} is not a valid grid.
|
||||
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
|
||||
cmd-parse-failure-session = There is no session with username: {$username}
|
||||
@@ -43,6 +44,13 @@ cmd-cvar-compl-list = List available CVars
|
||||
cmd-cvar-arg-name = <name | ?>
|
||||
cmd-cvar-value-hidden = <value hidden>
|
||||
|
||||
## 'cvar_subs' command
|
||||
cmd-cvar_subs-desc = Lists the OnValueChanged subscriptions for a CVar.
|
||||
cmd-cvar_subs-help = Usage: cvar_subs <name>
|
||||
|
||||
cmd-cvar_subs-invalid-args = Must provide exactly one argument.
|
||||
cmd-cvar_subs-arg-name = <name>
|
||||
|
||||
## 'list' command
|
||||
cmd-list-desc = Lists available commands, with optional search filter
|
||||
cmd-list-help = Usage: list [filter]
|
||||
@@ -245,9 +253,6 @@ cmd-bind-arg-command = <InputCommand>
|
||||
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net-draw-interp-help = Usage: net_draw_interp
|
||||
|
||||
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net-draw-interp-help = Usage: net_draw_interp
|
||||
|
||||
cmd-net-watch-ent-desc = Dumps all network updates for an EntityId to the console.
|
||||
cmd-net-watch-ent-help = Usage: net_watchent <0|EntityUid>
|
||||
|
||||
@@ -299,16 +304,9 @@ cmd-savegrid-help = savegrid <gridID> <Path>
|
||||
cmd-testbed-desc = Loads a physics testbed on the specified map.
|
||||
cmd-testbed-help = testbed <mapid> <test>
|
||||
|
||||
cmd-saveconfig-desc = Saves the client configuration to the config file.
|
||||
cmd-saveconfig-help = saveconfig
|
||||
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
cmd-flushcookies-desc = Flush CEF cookie storage to disk
|
||||
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
|
||||
Note that the actual operation is asynchronous.
|
||||
|
||||
## 'addcomp' command
|
||||
cmd-addcomp-desc = Adds a component to an entity.
|
||||
cmd-addcomp-help = addcomp <uid> <componentName>
|
||||
@@ -384,9 +382,9 @@ cmd-tp-desc = Teleports a player to any location in the round.
|
||||
cmd-tp-help = tp <x> <y> [<mapID>]
|
||||
|
||||
cmd-tpto-desc = Teleports the current player or the specified players/entities to the location of the first player/entity.
|
||||
cmd-tpto-help = tpto <username|uid> [username|uid]...
|
||||
cmd-tpto-destination-hint = destination (uid or username)
|
||||
cmd-tpto-victim-hint = entity to teleport (uid or username)
|
||||
cmd-tpto-help = tpto <username|uid> [username|NetEntity]...
|
||||
cmd-tpto-destination-hint = destination (NetEntity or username)
|
||||
cmd-tpto-victim-hint = entity to teleport (NetEntity or username)
|
||||
cmd-tpto-parse-error = Cant resolve entity or player: {$str}
|
||||
|
||||
cmd-listplayers-desc = Lists all players currently connected.
|
||||
@@ -446,9 +444,6 @@ cmd-showanchored-help = Usage: showanchored
|
||||
cmd-dmetamem-desc = Dumps a type's members in a format suitable for the sandbox configuration file.
|
||||
cmd-dmetamem-help = Usage: dmetamem <type>
|
||||
|
||||
cmd-dmetamem-desc = Displays chunk bounds for the purposes of rendering.
|
||||
cmd-dmetamem-help = Usage: showchunkbb <type>
|
||||
|
||||
cmd-launchauth-desc = Load authentication tokens from launcher data to aid in testing of live servers.
|
||||
cmd-launchauth-help = Usage: launchauth <account name>
|
||||
|
||||
@@ -515,9 +510,6 @@ cmd-profsnap-help = Usage: profsnap
|
||||
cmd-devwindow-desc = Dev Window
|
||||
cmd-devwindow-help = Usage: devwindow
|
||||
|
||||
cmd-devwindow-desc = Open file
|
||||
cmd-devwindow-help = Usage: testopenfile
|
||||
|
||||
cmd-scene-desc = Immediately changes the UI scene/state.
|
||||
cmd-scene-help = Usage: scene <className>
|
||||
|
||||
@@ -528,14 +520,11 @@ cmd-hwid-desc = Returns the current HWID (HardWare ID).
|
||||
cmd-hwid-help = Usage: hwid
|
||||
|
||||
cmd-vvread-desc = Retrieve a path's value using VV (View Variables).
|
||||
cmd-vvread-desc = Usage: vvread <path>
|
||||
cmd-vvread-help = Usage: vvread <path>
|
||||
|
||||
cmd-vvwrite-desc = Modify a path's value using VV (View Variables).
|
||||
cmd-vvwrite-help = Usage: vvwrite <path>
|
||||
|
||||
cmd-vv-desc = Opens View Variables (VV).
|
||||
cmd-vv-help = Usage: vv <path|entity ID|guihover>
|
||||
|
||||
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
|
||||
cmd-vvinvoke-help = Usage: vvinvoke <path> [arguments...]
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
entity-category-name-debug = Debug
|
||||
entity-category-desc-debug = Entity prototypes intended for debugging & testing.
|
||||
entity-category-suffix-debug = Debug
|
||||
|
||||
entity-category-name-spawner = Spawner
|
||||
entity-category-desc-spawner = Entity prototypes that spawn other entities.
|
||||
|
||||
entity-category-name-hide = Hidden
|
||||
entity-category-desc-hide = Entity prototypes that should be hidden from the spawn menu
|
||||
entity-category-desc-hide = Entity prototypes that should be hidden from entity spawn menus
|
||||
|
||||
57
Robust.Analyzers.Tests/NoUncachedRegexAnalyzerTest.cs
Normal file
57
Robust.Analyzers.Tests/NoUncachedRegexAnalyzerTest.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing.Verifiers;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.NoUncachedRegexAnalyzer>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
public sealed class NoUncachedRegexAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpAnalyzerTest<NoUncachedRegexAnalyzer, NUnitVerifier>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public static class Foo
|
||||
{
|
||||
public static void Bad()
|
||||
{
|
||||
Regex.Replace("foo", "bar", "baz");
|
||||
}
|
||||
|
||||
public static void Good()
|
||||
{
|
||||
var r = new Regex("bar");
|
||||
r.Replace("foo", "baz");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(7,9): warning RA0026: Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.
|
||||
VerifyCS.Diagnostic().WithSpan(7, 9, 7, 43)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
using Robust.Shared.Serialization.Manager.Definition;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
@@ -56,8 +57,18 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
"Make sure to add a setter."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldRedundantTagRule = new(
|
||||
Diagnostics.IdDataFieldRedundantTag,
|
||||
"Data field has redundant tag specified",
|
||||
"Data field {0} in data definition {1} has an explicitly set tag that matches autogenerated tag",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Info,
|
||||
true,
|
||||
"Make sure to remove the tag string from the data field attribute."
|
||||
);
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
|
||||
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule,
|
||||
DataFieldRedundantTagRule
|
||||
);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
@@ -125,6 +136,11 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
|
||||
}
|
||||
|
||||
if (HasRedundantTag(fieldSymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +165,11 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
|
||||
}
|
||||
|
||||
if (HasRedundantTag(propertySymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
|
||||
@@ -248,6 +269,29 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasRedundantTag(ISymbol symbol)
|
||||
{
|
||||
if (!IsDataField(symbol, out var _, out var attribute))
|
||||
return false;
|
||||
|
||||
// No args, no problem
|
||||
if (attribute.ConstructorArguments.Length == 0)
|
||||
return false;
|
||||
|
||||
// If a tag is explicitly specified, it will be the first argument...
|
||||
var tagArgument = attribute.ConstructorArguments[0];
|
||||
// ...but the first arg could also something else, since tag is optional
|
||||
// so we make sure that it's a string
|
||||
if (tagArgument.Value is not string explicitName)
|
||||
return false;
|
||||
|
||||
// Get the name that sourcegen would provide
|
||||
var automaticName = DataDefinitionUtility.AutoGenerateTag(symbol.Name);
|
||||
|
||||
// If the explicit name matches the sourcegen name, we have a redundancy
|
||||
return explicitName == automaticName;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
@@ -16,8 +13,11 @@ namespace Robust.Analyzers;
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public sealed class DefinitionFixer : CodeFixProvider
|
||||
{
|
||||
private const string DataFieldAttributeName = "DataField";
|
||||
|
||||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
|
||||
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
|
||||
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable,
|
||||
IdDataFieldRedundantTag
|
||||
);
|
||||
|
||||
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
@@ -34,6 +34,8 @@ public sealed class DefinitionFixer : CodeFixProvider
|
||||
return RegisterDataFieldFix(context, diagnostic);
|
||||
case IdDataFieldPropertyWritable:
|
||||
return RegisterDataFieldPropertyFix(context, diagnostic);
|
||||
case IdDataFieldRedundantTag:
|
||||
return RegisterRedundantTagFix(context, diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +74,68 @@ public sealed class DefinitionFixer : CodeFixProvider
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task RegisterRedundantTagFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<MemberDeclarationSyntax>().First();
|
||||
|
||||
if (token == null)
|
||||
return;
|
||||
|
||||
// Find the DataField attribute
|
||||
AttributeSyntax? dataFieldAttribute = null;
|
||||
foreach (var attributeList in token.AttributeLists)
|
||||
{
|
||||
foreach (var attribute in attributeList.Attributes)
|
||||
{
|
||||
if (attribute.Name.ToString() == DataFieldAttributeName)
|
||||
{
|
||||
dataFieldAttribute = attribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dataFieldAttribute != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (dataFieldAttribute == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Remove explicitly set tag",
|
||||
c => RemoveRedundantTag(context.Document, dataFieldAttribute, c),
|
||||
"Remove explicitly set tag"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> RemoveRedundantTag(Document document, AttributeSyntax syntax, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
|
||||
if (syntax.ArgumentList == null)
|
||||
return document;
|
||||
|
||||
AttributeSyntax? newSyntax;
|
||||
if (syntax.ArgumentList.Arguments.Count == 1)
|
||||
{
|
||||
// If this is the only argument, delete the ArgumentList so we don't leave empty parentheses
|
||||
newSyntax = syntax.RemoveNode(syntax.ArgumentList, SyntaxRemoveOptions.KeepNoTrivia);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove the first argument, which is the tag
|
||||
var newArgs = syntax.ArgumentList.Arguments.RemoveAt(0);
|
||||
var newArgList = syntax.ArgumentList.WithArguments(newArgs);
|
||||
// Construct a new attribute with the tag removed
|
||||
newSyntax = syntax.WithArgumentList(newArgList);
|
||||
}
|
||||
|
||||
root = root!.ReplaceNode(syntax, newSyntax!);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
|
||||
66
Robust.Analyzers/NoUncachedRegexAnalyzer.cs
Normal file
66
Robust.Analyzers/NoUncachedRegexAnalyzer.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class NoUncachedRegexAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string RegexTypeName = "Regex";
|
||||
private const string RegexType = $"System.Text.RegularExpressions.{RegexTypeName}";
|
||||
|
||||
private static readonly DiagnosticDescriptor Rule = new (
|
||||
Diagnostics.IdUncachedRegex,
|
||||
"Use of uncached static Regex function",
|
||||
"Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public static readonly HashSet<string> BadFunctions =
|
||||
[
|
||||
"Count",
|
||||
"EnumerateMatches",
|
||||
"IsMatch",
|
||||
"Match",
|
||||
"Matches",
|
||||
"Replace",
|
||||
"Split"
|
||||
];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.EnableConcurrentExecution();
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.RegisterOperationAction(CheckInvocation, OperationKind.Invocation);
|
||||
}
|
||||
|
||||
private static void CheckInvocation(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation invocation)
|
||||
return;
|
||||
|
||||
// All Regex functions we care about are static.
|
||||
var targetMethod = invocation.TargetMethod;
|
||||
if (!targetMethod.IsStatic)
|
||||
return;
|
||||
|
||||
// Bail early.
|
||||
if (targetMethod.ContainingType.Name != "Regex")
|
||||
return;
|
||||
|
||||
var regexType = context.Compilation.GetTypeByMetadataName(RegexType);
|
||||
if (!SymbolEqualityComparer.Default.Equals(regexType, targetMethod.ContainingType))
|
||||
return;
|
||||
|
||||
if (!BadFunctions.Contains(targetMethod.Name))
|
||||
return;
|
||||
|
||||
context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.Syntax.GetLocation()));
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,11 @@
|
||||
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for DataDefinitionAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Serialization\Manager\Definition\DataDefinitionUtility.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -239,6 +239,7 @@ namespace Robust.Build.Tasks
|
||||
engine.LogErrorEvent(new BuildErrorEventArgs("XAMLIL", "", res.FilePath, 0, 0, 0, 0,
|
||||
$"{res.FilePath}: {e.Message}", "", "CompileRobustXaml"));
|
||||
}
|
||||
res.Remove();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
[Dependency] private readonly IParallelManager _parMan = default!;
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
[Dependency] private readonly IAudioInternal _audio = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSys = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
|
||||
@@ -216,11 +216,6 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
}
|
||||
}
|
||||
|
||||
if ((component.Flags & AudioFlags.GridAudio) != 0x0)
|
||||
{
|
||||
_metadata.SetFlag(entity.Owner, MetaDataFlags.Undetachable, true);
|
||||
}
|
||||
|
||||
// Need to set all initial data for first frame.
|
||||
ApplyAudioParams(component.Params, component);
|
||||
component.Source.Global = component.Global;
|
||||
@@ -336,55 +331,22 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
}
|
||||
|
||||
Vector2 worldPos;
|
||||
var gridUid = xform.ParentUid;
|
||||
component.Volume = component.Params.Volume;
|
||||
Vector2 delta;
|
||||
|
||||
// Handle grid audio differently by using nearest-edge instead of entity centre.
|
||||
// Handle grid audio differently by using grid position.
|
||||
if ((component.Flags & AudioFlags.GridAudio) != 0x0)
|
||||
{
|
||||
// It's our grid so max volume.
|
||||
if (_listenerGrid == gridUid)
|
||||
{
|
||||
component.Volume = component.Params.Volume;
|
||||
component.Occlusion = 0f;
|
||||
component.Position = listener.Position;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Need a grid-optimised version because this is gonna be expensive.
|
||||
// Just to avoid clipping on and off grid or nearestPoint changing we'll
|
||||
// always set the sound to listener's pos, we'll just manually do gain ourselves.
|
||||
if (_physics.TryGetNearest(gridUid, listener, out _, out var gridDistance))
|
||||
{
|
||||
// Out of range
|
||||
if (gridDistance > component.MaxDistance)
|
||||
{
|
||||
component.Gain = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
var paramsGain = VolumeToGain(component.Params.Volume);
|
||||
|
||||
// Thought I'd never have to manually calculate gain again but this is the least
|
||||
// unpleasant audio I could get at the moment.
|
||||
component.Gain = paramsGain * _audio.GetAttenuationGain(
|
||||
gridDistance,
|
||||
component.Params.RolloffFactor,
|
||||
component.Params.ReferenceDistance,
|
||||
component.Params.MaxDistance);
|
||||
component.Position = listener.Position;
|
||||
return;
|
||||
}
|
||||
|
||||
// Can't get nearest point so don't play anymore.
|
||||
component.Gain = 0f;
|
||||
return;
|
||||
var parentUid = xform.ParentUid;
|
||||
worldPos = _maps.GetGridPosition(parentUid);
|
||||
}
|
||||
else
|
||||
{
|
||||
worldPos = _xformSys.GetWorldPosition(entity);
|
||||
}
|
||||
|
||||
worldPos = _xformSys.GetWorldPosition(entity);
|
||||
component.Volume = component.Params.Volume;
|
||||
|
||||
// Max distance check
|
||||
var delta = worldPos - listener.Position;
|
||||
delta = worldPos - listener.Position;
|
||||
var distance = delta.Length();
|
||||
|
||||
// Out of range so just clip it for us.
|
||||
@@ -403,8 +365,15 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
}
|
||||
|
||||
// Update audio occlusion
|
||||
var occlusion = GetOcclusion(listener, delta, distance, entity);
|
||||
component.Occlusion = occlusion;
|
||||
if ((component.Flags & AudioFlags.NoOcclusion) == AudioFlags.NoOcclusion)
|
||||
{
|
||||
component.Occlusion = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
var occlusion = GetOcclusion(listener, delta, distance, entity);
|
||||
component.Occlusion = occlusion;
|
||||
}
|
||||
|
||||
// Update audio positions.
|
||||
component.Position = worldPos;
|
||||
@@ -671,10 +640,10 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
private (EntityUid Entity, AudioComponent Component) CreateAndStartPlayingStream(AudioParams? audioParams, AudioStream stream)
|
||||
{
|
||||
var audioP = audioParams ?? AudioParams.Default;
|
||||
var entity = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace);
|
||||
var comp = SetupAudio(entity, null, audioP, stream.Length);
|
||||
LoadStream((entity, comp), stream);
|
||||
var entity = SetupAudio(null, audioP, initialize: false, length: stream.Length);
|
||||
LoadStream(entity, stream);
|
||||
EntityManager.InitializeAndStartEntity(entity);
|
||||
var comp = entity.Comp;
|
||||
var source = comp.Source;
|
||||
|
||||
// TODO clamp the offset inside of SetPlaybackPosition() itself.
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = Type.GetType(args[0]);
|
||||
var type = GetType(args[0]);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
@@ -25,6 +25,17 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteLine(sig);
|
||||
}
|
||||
}
|
||||
|
||||
private Type? GetType(string name)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.GetType(name) is { } type)
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
var viewBounds = args.WorldBounds;
|
||||
var viewAABB = args.WorldAABB;
|
||||
var mapId = _eyeManager.CurrentMap;
|
||||
var mapId = args.MapId;
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0)
|
||||
{
|
||||
@@ -368,12 +368,12 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
|
||||
worldHandle.UseShader(null);
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
|
||||
{
|
||||
var mapId = _eyeManager.CurrentMap;
|
||||
var mapId = args.MapId;
|
||||
var mousePos = _inputManager.MouseScreenPosition;
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.ShapeInfo) != 0x0)
|
||||
@@ -443,7 +443,7 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
|
||||
screenHandle.UseShader(null);
|
||||
screenHandle.SetTransform(Matrix3.Identity);
|
||||
screenHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
@@ -520,11 +520,11 @@ namespace Robust.Client.Debugging
|
||||
var matrix1 = xform1.WorldMatrix;
|
||||
var matrix2 = xform2.WorldMatrix;
|
||||
|
||||
var xf1 = new Vector2(matrix1.R0C2, matrix1.R1C2);
|
||||
var xf2 = new Vector2(matrix2.R0C2, matrix2.R1C2);
|
||||
var xf1 = new Vector2(matrix1.M31, matrix1.M32);
|
||||
var xf2 = new Vector2(matrix2.M31, matrix2.M32);
|
||||
|
||||
var p1 = matrix1.Transform(joint.LocalAnchorA);
|
||||
var p2 = matrix2.Transform(joint.LocalAnchorB);
|
||||
var p1 = Vector2.Transform(joint.LocalAnchorA, matrix1);
|
||||
var p2 = Vector2.Transform(joint.LocalAnchorB, matrix2);
|
||||
|
||||
var xfa = new Transform(xf1, xform1.WorldRotation);
|
||||
var xfb = new Transform(xf2, xform2.WorldRotation);
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Robust.Client.GameObjects
|
||||
[DataField("color")]
|
||||
private Color color = Color.White;
|
||||
|
||||
public Matrix3 LocalMatrix = Matrix3.Identity;
|
||||
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
|
||||
|
||||
[Animatable]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -389,10 +389,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
internal void UpdateLocalMatrix()
|
||||
{
|
||||
LocalMatrix = Matrix3.CreateTransform(in offset, in rotation, in scale);
|
||||
LocalMatrix = Matrix3Helpers.CreateTransform(in offset, in rotation, in scale);
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalMatrix()
|
||||
public Matrix3x2 GetLocalMatrix()
|
||||
{
|
||||
return LocalMatrix;
|
||||
}
|
||||
@@ -1304,22 +1304,21 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// worldRotation + eyeRotation should be the angle of the entity on-screen. If no-rot is enabled this is just set to zero.
|
||||
// However, at some point later the eye-matrix is applied separately, so we subtract -eye rotation for now:
|
||||
var entityMatrix = Matrix3.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
|
||||
var entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
|
||||
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSprite);
|
||||
var transformSprite = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
|
||||
if (GranularLayersRendering)
|
||||
{
|
||||
//Default rendering
|
||||
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation);
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformDefault);
|
||||
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation);
|
||||
var transformDefault = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
//Snap to cardinals
|
||||
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSnap);
|
||||
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
|
||||
var transformSnap = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
//No rotation
|
||||
entityMatrix = Matrix3.CreateTransform(worldPosition, -eyeRotation);
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformNoRot);
|
||||
|
||||
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, -eyeRotation);
|
||||
var transformNoRot = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
|
||||
foreach (var layer in Layers) {
|
||||
switch (layer.RenderingStrategy)
|
||||
@@ -1380,7 +1379,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// TODO whenever sprite comp gets ECS'd , just make this a direct method call.
|
||||
var ev = new QueueSpriteTreeUpdateEvent(entities.GetComponent<TransformComponent>(Owner));
|
||||
entities.EventBus.RaiseComponentEvent(this, ref ev);
|
||||
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
|
||||
}
|
||||
|
||||
private void QueueUpdateIsInert()
|
||||
@@ -1390,7 +1389,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// TODO whenever sprite comp gets ECS'd , just make this a direct method call.
|
||||
var ev = new SpriteUpdateInertEvent();
|
||||
entities.EventBus.RaiseComponentEvent(this, ref ev);
|
||||
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
|
||||
}
|
||||
|
||||
[Obsolete("Use SpriteSystem instead.")]
|
||||
@@ -1546,7 +1545,7 @@ namespace Robust.Client.GameObjects
|
||||
private RSI.State? _actualState;
|
||||
[ViewVariables] public RSI.State? ActualState => _actualState;
|
||||
|
||||
public Matrix3 LocalMatrix = Matrix3.Identity;
|
||||
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 Scale
|
||||
@@ -1692,7 +1691,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
internal void UpdateLocalMatrix()
|
||||
{
|
||||
LocalMatrix = Matrix3.CreateTransform(in _offset, in _rotation, in _scale);
|
||||
LocalMatrix = Matrix3Helpers.CreateTransform(in _offset, in _rotation, in _scale);
|
||||
}
|
||||
|
||||
RSI? ISpriteLayer.Rsi { get => RSI; set => SetRsi(value); }
|
||||
@@ -1964,27 +1963,27 @@ namespace Robust.Client.GameObjects
|
||||
/// Given the apparent rotation of an entity on screen (world + eye rotation), get layer's matrix for drawing &
|
||||
/// relevant RSI direction.
|
||||
/// </summary>
|
||||
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3 layerDrawMatrix)
|
||||
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3x2 layerDrawMatrix)
|
||||
{
|
||||
if (_parent.NoRotation || dir == RsiDirection.South)
|
||||
layerDrawMatrix = LocalMatrix;
|
||||
else
|
||||
{
|
||||
Matrix3.Multiply(in _rsiDirectionMatrices[(int)dir], in LocalMatrix, out layerDrawMatrix);
|
||||
layerDrawMatrix = Matrix3x2.Multiply(_rsiDirectionMatrices[(int)dir], LocalMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
private static Matrix3[] _rsiDirectionMatrices = new Matrix3[]
|
||||
private static Matrix3x2[] _rsiDirectionMatrices = new Matrix3x2[]
|
||||
{
|
||||
// array order chosen such that this array can be indexed by casing an RSI direction to an int
|
||||
Matrix3.Identity, // should probably just avoid matrix multiplication altogether if the direction is south.
|
||||
Matrix3.CreateRotation(-Direction.North.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.East.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.West.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.SouthEast.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.SouthWest.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.NorthEast.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.NorthWest.ToAngle())
|
||||
Matrix3x2.Identity, // should probably just avoid matrix multiplication altogether if the direction is south.
|
||||
Matrix3Helpers.CreateRotation(-Direction.North.ToAngle()),
|
||||
Matrix3Helpers.CreateRotation(-Direction.East.ToAngle()),
|
||||
Matrix3Helpers.CreateRotation(-Direction.West.ToAngle()),
|
||||
Matrix3Helpers.CreateRotation(-Direction.SouthEast.ToAngle()),
|
||||
Matrix3Helpers.CreateRotation(-Direction.SouthWest.ToAngle()),
|
||||
Matrix3Helpers.CreateRotation(-Direction.NorthEast.ToAngle()),
|
||||
Matrix3Helpers.CreateRotation(-Direction.NorthWest.ToAngle())
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -2018,7 +2017,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Render a layer. This assumes that the input angle is between 0 and 2pi.
|
||||
/// </summary>
|
||||
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3 spriteMatrix, Angle angle, Direction? overrideDirection)
|
||||
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatrix, Angle angle, Direction? overrideDirection)
|
||||
{
|
||||
if (!Visible || Blank)
|
||||
return;
|
||||
@@ -2040,7 +2039,7 @@ namespace Robust.Client.GameObjects
|
||||
if (CopyToShaderParameters == null)
|
||||
{
|
||||
// Set the drawing transform for this layer
|
||||
Matrix3.Multiply(in layerMatrix, in spriteMatrix, out var transformMatrix);
|
||||
var transformMatrix = Matrix3x2.Multiply(layerMatrix, spriteMatrix);
|
||||
drawingHandle.SetTransform(in transformMatrix);
|
||||
|
||||
RenderTexture(drawingHandle, texture);
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
private readonly List<Entity<AnimationPlayerComponent>> _activeAnimations = new();
|
||||
|
||||
private EntityQuery<AnimationPlayerComponent> _playerQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
@@ -18,6 +19,7 @@ namespace Robust.Client.GameObjects
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_playerQuery = GetEntityQuery<AnimationPlayerComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
}
|
||||
|
||||
@@ -171,28 +173,32 @@ namespace Robust.Client.GameObjects
|
||||
return component.PlayingAnimations.ContainsKey(key);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public void Stop(AnimationPlayerComponent component, string key)
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
Stop((component.Owner, component), key);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, string key)
|
||||
public void Stop(Entity<AnimationPlayerComponent?> entity, string key)
|
||||
{
|
||||
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
|
||||
if (!_playerQuery.Resolve(entity.Owner, ref entity.Comp, false) ||
|
||||
!entity.Comp.PlayingAnimations.Remove(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
player.PlayingAnimations.Remove(key);
|
||||
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, new AnimationCompletedEvent {Uid = entity.Owner, Key = key}, true);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
component.PlayingAnimations.Remove(key);
|
||||
Stop((uid, component), key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised whenever an animation stops, either due to running its course or being stopped manually.
|
||||
/// </summary>
|
||||
public sealed class AnimationCompletedEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Uid { get; init; }
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
var type = _serializer.FindSerializedType(typeof(BaseContainer), data.ContainerType);
|
||||
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type!, inject:false);
|
||||
InitContainer(container, (uid, component), id);
|
||||
container.Init(this, id, (uid, component));
|
||||
component.Containers.Add(id, container);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -118,7 +119,7 @@ public sealed class EntityLookupOverlay : Overlay
|
||||
//DebugTools.Assert(!ent.IsInContainer(_entityManager));
|
||||
var (entPos, entRot) = _transform.GetWorldPositionRotation(ent);
|
||||
|
||||
var lookupPos = invMatrix.Transform(entPos);
|
||||
var lookupPos = Vector2.Transform(entPos, invMatrix);
|
||||
var lookupRot = entRot - rotation;
|
||||
|
||||
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, _xformQuery);
|
||||
@@ -127,6 +128,6 @@ public sealed class EntityLookupOverlay : Overlay
|
||||
}
|
||||
});
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var map = _eyeManager.CurrentMap;
|
||||
var map = args.MapId;
|
||||
if (map == MapId.Nullspace) return;
|
||||
|
||||
foreach (var (_, treeComp) in _trees.GetIntersectingTrees(map, args.WorldBounds))
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var currentMap = _eyeManager.CurrentMap;
|
||||
var currentMap = args.MapId;
|
||||
var viewport = args.WorldBounds;
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public sealed class InputSystem : SharedInputSystem, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _stateManager = default!;
|
||||
@@ -82,18 +83,35 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
// send it off to the server
|
||||
var clientMsg = (ClientFullInputCmdMessage)message;
|
||||
var fullMsg = new FullInputCmdMessage(
|
||||
clientMsg.Tick,
|
||||
clientMsg.SubTick,
|
||||
(int)clientMsg.InputSequence,
|
||||
clientMsg.InputFunctionId,
|
||||
clientMsg.State,
|
||||
GetNetCoordinates(clientMsg.Coordinates),
|
||||
clientMsg.ScreenCoordinates)
|
||||
var clientMsg = message switch
|
||||
{
|
||||
Uid = GetNetEntity(clientMsg.Uid)
|
||||
ClientFullInputCmdMessage clientInput => clientInput,
|
||||
FullInputCmdMessage fullInput => new ClientFullInputCmdMessage(
|
||||
fullInput.Tick,
|
||||
fullInput.SubTick,
|
||||
fullInput.InputFunctionId,
|
||||
GetCoordinates(fullInput.Coordinates),
|
||||
fullInput.ScreenCoordinates,
|
||||
fullInput.State,
|
||||
GetEntity(fullInput.Uid)),
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
var fullMsg = message switch
|
||||
{
|
||||
FullInputCmdMessage fullInput => fullInput,
|
||||
ClientFullInputCmdMessage client => new FullInputCmdMessage(
|
||||
client.Tick,
|
||||
client.SubTick,
|
||||
client.InputFunctionId,
|
||||
clientMsg.State,
|
||||
GetNetCoordinates(client.Coordinates),
|
||||
clientMsg.ScreenCoordinates,
|
||||
GetNetEntity(clientMsg.Uid)
|
||||
),
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
DispatchInputCommand(clientMsg, fullMsg);
|
||||
@@ -131,7 +149,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
_conHost.RegisterCommand("incmd",
|
||||
"Inserts an input command into the simulation",
|
||||
"incmd <KeyFunction> <d|u KeyState> <wxPos> <wyPos>",
|
||||
"incmd <KeyFunction> <KeyState> [wxPos] [wyPos]",
|
||||
GenerateInputCommand);
|
||||
}
|
||||
|
||||
@@ -147,17 +165,47 @@ namespace Robust.Client.GameObjects
|
||||
if (_playerManager.LocalEntity is not { } pent)
|
||||
return;
|
||||
|
||||
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
|
||||
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
|
||||
if (args.Length is not (2 or 4))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString($"cmd-invalid-arg-number-error"));
|
||||
return;
|
||||
}
|
||||
|
||||
var pxform = Transform(pent);
|
||||
var wPos = pxform.WorldPosition + new Vector2(float.Parse(args[2]), float.Parse(args[3]));
|
||||
var coords = EntityCoordinates.FromMap(pent, new MapCoordinates(wPos, pxform.MapID), _transform, EntityManager);
|
||||
var keyFunction = new BoundKeyFunction(args[0]);
|
||||
if (!Enum.TryParse<BoundKeyState>(args[1], out var state))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-parse-failure-enum", ("arg", args[1]), ("enum", nameof(BoundKeyState))));
|
||||
return;
|
||||
}
|
||||
|
||||
var wOffset = Vector2.Zero;
|
||||
if (args.Length == 4)
|
||||
{
|
||||
if (!float.TryParse(args[2], out var wX))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[2])));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!float.TryParse(args[3], out var wY))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[3])));
|
||||
return;
|
||||
}
|
||||
|
||||
wOffset = new Vector2(wX, wY);
|
||||
}
|
||||
|
||||
var coords = EntityCoordinates.FromMap(pent, _transform.GetMapCoordinates(pent).Offset(wOffset), _transform, EntityManager);
|
||||
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
|
||||
|
||||
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
|
||||
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
|
||||
var message = new ClientFullInputCmdMessage(_timing.CurTick,
|
||||
_timing.TickFraction,
|
||||
funcId,
|
||||
coords,
|
||||
new ScreenCoordinates(0, 0, default),
|
||||
state,
|
||||
EntityUid.Invalid);
|
||||
|
||||
HandleInputCommand(_playerManager.LocalSession, keyFunction, message);
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ namespace Robust.Client.GameStates
|
||||
private readonly HashSet<NetEntity> _stateEnts = new();
|
||||
private readonly List<EntityUid> _toDelete = new();
|
||||
private readonly List<IComponent> _toRemove = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _outputData = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> _outputData = new();
|
||||
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
|
||||
|
||||
private readonly ObjectPool<Dictionary<ushort, IComponentState>> _compDataPool =
|
||||
new DefaultObjectPool<Dictionary<ushort, IComponentState>>(new DictPolicy<ushort, IComponentState>(), 256);
|
||||
private readonly ObjectPool<Dictionary<ushort, IComponentState?>> _compDataPool =
|
||||
new DefaultObjectPool<Dictionary<ushort, IComponentState?>>(new DictPolicy<ushort, IComponentState?>(), 256);
|
||||
|
||||
private uint _metaCompNetId;
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace Robust.Client.GameStates
|
||||
public void UpdateFullRep(GameState state, bool cloneDelta = false)
|
||||
=> _processor.UpdateFullRep(state, cloneDelta);
|
||||
|
||||
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep()
|
||||
=> _processor.GetFullRep();
|
||||
|
||||
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
|
||||
@@ -600,8 +600,12 @@ namespace Robust.Client.GameStates
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A component was dirtied: {comp.GetType()}");
|
||||
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
if (compState != null)
|
||||
{
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
_entities.EventBus.RaiseComponentEvent(entity, comp, ref handleState);
|
||||
}
|
||||
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
}
|
||||
@@ -633,8 +637,12 @@ namespace Robust.Client.GameStates
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A component was removed: {comp.GetType()}");
|
||||
|
||||
var stateEv = new ComponentHandleState(state, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
|
||||
if (state != null)
|
||||
{
|
||||
var stateEv = new ComponentHandleState(state, null);
|
||||
_entities.EventBus.RaiseComponentEvent(entity, comp, ref stateEv);
|
||||
}
|
||||
|
||||
comp.ClearCreationTick(); // don't undo the re-adding.
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
@@ -687,7 +695,7 @@ namespace Robust.Client.GameStates
|
||||
DebugTools.Assert(component.NetSyncEnabled);
|
||||
|
||||
var state = _entityManager.GetComponentState(bus, component, null, GameTick.Zero);
|
||||
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
|
||||
DebugTools.Assert(state is not IComponentDeltaState);
|
||||
compData.Add(netId, state);
|
||||
}
|
||||
}
|
||||
@@ -1349,8 +1357,11 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (comp, cur, next) in _compStateWork.Values)
|
||||
{
|
||||
if (cur == null && next == null)
|
||||
continue;
|
||||
|
||||
var handleState = new ComponentHandleState(cur, next);
|
||||
bus.RaiseComponentEvent(comp, ref handleState);
|
||||
bus.RaiseComponentEvent(uid, comp, ref handleState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1501,8 +1512,11 @@ namespace Robust.Client.GameStates
|
||||
_entityManager.AddComponent(uid, comp, true, meta);
|
||||
}
|
||||
|
||||
if (state == null)
|
||||
continue;
|
||||
|
||||
var handleState = new ComponentHandleState(state, null);
|
||||
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
_entityManager.EventBus.RaiseComponentEvent(uid, comp, ref handleState);
|
||||
}
|
||||
|
||||
// ensure we don't have any extra components
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
|
||||
/// </summary>
|
||||
internal readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _lastStateFullRep
|
||||
internal readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> _lastStateFullRep
|
||||
= new();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -212,7 +212,7 @@ Had full state: {LastFullState != null}"
|
||||
{
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
|
||||
{
|
||||
compData = new Dictionary<ushort, IComponentState>();
|
||||
compData = new();
|
||||
_lastStateFullRep.Add(entityState.NetEntity, compData);
|
||||
}
|
||||
|
||||
@@ -221,21 +221,20 @@ Had full state: {LastFullState != null}"
|
||||
var compState = change.State;
|
||||
|
||||
if (compState is IComponentDeltaState delta
|
||||
&& !delta.FullState
|
||||
&& compData.TryGetValue(change.NetID, out var old)) // May fail if relying on implicit data
|
||||
{
|
||||
DebugTools.Assert(old is IComponentDeltaState oldDelta && oldDelta.FullState, "last state is not a full state");
|
||||
DebugTools.Assert(old is not IComponentDeltaState, "last state is not a full state");
|
||||
|
||||
if (cloneDelta)
|
||||
{
|
||||
compState = delta.CreateNewFullState(old);
|
||||
compState = delta.CreateNewFullState(old!);
|
||||
}
|
||||
else
|
||||
{
|
||||
delta.ApplyToFullState(old);
|
||||
delta.ApplyToFullState(old!);
|
||||
compState = old;
|
||||
}
|
||||
DebugTools.Assert(compState is IComponentDeltaState newState && newState.FullState, "newly constructed state is not a full state");
|
||||
DebugTools.Assert(compState is not IComponentDeltaState, "newly constructed state is not a full state");
|
||||
}
|
||||
|
||||
compData[change.NetID] = compState;
|
||||
@@ -391,7 +390,7 @@ Had full state: {LastFullState != null}"
|
||||
LastFullStateRequested = null;
|
||||
}
|
||||
|
||||
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> implicitData)
|
||||
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> implicitData)
|
||||
{
|
||||
foreach (var (netEntity, implicitEntState) in implicitData)
|
||||
{
|
||||
@@ -399,6 +398,7 @@ Had full state: {LastFullState != null}"
|
||||
|
||||
foreach (var (netId, implicitCompState) in implicitEntState)
|
||||
{
|
||||
DebugTools.Assert(implicitCompState is not IComponentDeltaState);
|
||||
ref var serverState = ref CollectionsMarshal.GetValueRefOrAddDefault(fullRep, netId, out var exists);
|
||||
|
||||
if (!exists)
|
||||
@@ -407,36 +407,32 @@ Had full state: {LastFullState != null}"
|
||||
continue;
|
||||
}
|
||||
|
||||
if (serverState is not IComponentDeltaState serverDelta || serverDelta.FullState)
|
||||
if (serverState is not IComponentDeltaState serverDelta)
|
||||
continue;
|
||||
|
||||
DebugTools.AssertNotNull(implicitCompState);
|
||||
|
||||
// Server sent an initial delta state. This is fine as long as the client can infer an initial full
|
||||
// state from the entity prototype.
|
||||
if (implicitCompState is not IComponentDeltaState implicitDelta || !implicitDelta.FullState)
|
||||
{
|
||||
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {netEntity}");
|
||||
continue;
|
||||
}
|
||||
|
||||
serverDelta.ApplyToFullState(implicitCompState);
|
||||
serverDelta.ApplyToFullState(implicitCompState!);
|
||||
serverState = implicitCompState;
|
||||
DebugTools.Assert(implicitCompState is IComponentDeltaState d && d.FullState);
|
||||
DebugTools.Assert(serverState is not IComponentDeltaState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity netEntity)
|
||||
public Dictionary<ushort, IComponentState?> GetLastServerStates(NetEntity netEntity)
|
||||
{
|
||||
return _lastStateFullRep[netEntity];
|
||||
}
|
||||
|
||||
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep()
|
||||
{
|
||||
return _lastStateFullRep;
|
||||
}
|
||||
|
||||
public bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary)
|
||||
[NotNullWhen(true)] out Dictionary<ushort, IComponentState?>? dictionary)
|
||||
{
|
||||
return _lastStateFullRep.TryGetValue(entity, out dictionary);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Returns the full collection of cached game states that are used to reset predicted entities.
|
||||
/// </summary>
|
||||
Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep();
|
||||
Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep();
|
||||
|
||||
/// <summary>
|
||||
/// This will perform some setup in order to reset the game to an earlier state. To fully reset the state
|
||||
|
||||
@@ -83,13 +83,13 @@ namespace Robust.Client.GameStates
|
||||
/// The data to merge.
|
||||
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
|
||||
/// </param>
|
||||
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> data);
|
||||
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> data);
|
||||
|
||||
/// <summary>
|
||||
/// Get the last state data from the server for an entity.
|
||||
/// </summary>
|
||||
/// <returns>Dictionary (net ID -> ComponentState)</returns>
|
||||
Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity entity);
|
||||
Dictionary<ushort, IComponentState?> GetLastServerStates(NetEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the number of applicable states in the game state buffer from a given tick.
|
||||
@@ -99,6 +99,6 @@ namespace Robust.Client.GameStates
|
||||
int GetApplicableStateCount(GameTick? fromTick);
|
||||
|
||||
bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary);
|
||||
[NotNullWhen(true)] out Dictionary<ushort, IComponentState?>? dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Robust.Client.GameStates
|
||||
while (query.MoveNext(out var uid, out var transform))
|
||||
{
|
||||
// if not on the same map, continue
|
||||
if (transform.MapID != _eyeManager.CurrentMap || _container.IsEntityInContainer(uid))
|
||||
if (transform.MapID != args.MapId || _container.IsEntityInContainer(uid))
|
||||
continue;
|
||||
|
||||
if (transform.GridUid == uid)
|
||||
|
||||
@@ -64,29 +64,22 @@ namespace Robust.Client.Graphics
|
||||
/// <inheritdoc />
|
||||
public Box2 GetWorldViewport()
|
||||
{
|
||||
var vpSize = _displayManager.ScreenSize;
|
||||
|
||||
var topLeft = ScreenToMap(Vector2.Zero);
|
||||
var topRight = ScreenToMap(new Vector2(vpSize.X, 0));
|
||||
var bottomRight = ScreenToMap(vpSize);
|
||||
var bottomLeft = ScreenToMap(new Vector2(0, vpSize.Y));
|
||||
|
||||
var left = MathHelper.Min(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
|
||||
var bottom = MathHelper.Min(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
|
||||
var right = MathHelper.Max(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
|
||||
var top = MathHelper.Max(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
|
||||
|
||||
return new Box2(left, bottom, right, top);
|
||||
return GetWorldViewbounds().CalcBoundingBox();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box2Rotated GetWorldViewbounds()
|
||||
{
|
||||
var vpSize = _displayManager.ScreenSize;
|
||||
// This is an inefficient and roundabout way of geting the viewport.
|
||||
// But its a method that shouldn't get used much.
|
||||
|
||||
var vp = MainViewport as Control;
|
||||
var vpSize = vp?.PixelSize ?? _displayManager.ScreenSize;
|
||||
|
||||
var topRight = ScreenToMap(new Vector2(vpSize.X, 0)).Position;
|
||||
var bottomLeft = ScreenToMap(new Vector2(0, vpSize.Y)).Position;
|
||||
|
||||
// This assumes the main viewports eye and the main eye are the same.
|
||||
var rotation = new Angle(CurrentEye.Rotation);
|
||||
var center = (bottomLeft + topRight) / 2;
|
||||
|
||||
@@ -108,18 +101,16 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void GetScreenProjectionMatrix(out Matrix3 projMatrix)
|
||||
public void GetScreenProjectionMatrix(out Matrix3x2 projMatrix)
|
||||
{
|
||||
Matrix3 result = default;
|
||||
Matrix3x2 result = default;
|
||||
|
||||
result.R0C0 = PixelsPerMeter;
|
||||
result.R1C1 = -PixelsPerMeter;
|
||||
result.M11 = PixelsPerMeter;
|
||||
result.M22 = -PixelsPerMeter;
|
||||
|
||||
var screenSize = _displayManager.ScreenSize;
|
||||
result.R0C2 = screenSize.X / 2f;
|
||||
result.R1C2 = screenSize.Y / 2f;
|
||||
|
||||
result.R2C2 = 1;
|
||||
result.M31 = screenSize.X / 2f;
|
||||
result.M32 = screenSize.Y / 2f;
|
||||
|
||||
/* column major
|
||||
Sx 0 Tx
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
@@ -13,26 +14,29 @@ namespace Robust.Client.Graphics
|
||||
public interface IEyeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The current eye that is being used to render the game.
|
||||
/// The primary eye, which is usually the eye associated with the main viewport.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally, you should avoid using this whenever possible. E.g., when rendering overlays should use the
|
||||
/// eye & viewbounds that gets passed to the draw method.
|
||||
/// Setting this property to null will use the default eye.
|
||||
/// </remarks>
|
||||
IEye CurrentEye { get; set; }
|
||||
|
||||
IViewportControl MainViewport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the map on which the current eye is "placed".
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
MapId CurrentMap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A world-space box that is at LEAST the area covered by the viewport.
|
||||
/// A world-space box that is at LEAST the area covered by the main viewport.
|
||||
/// May be larger due to say rotation.
|
||||
/// </summary>
|
||||
Box2 GetWorldViewport();
|
||||
|
||||
/// <summary>
|
||||
/// A world-space box of the area visible in the main viewport.
|
||||
/// </summary>
|
||||
Box2Rotated GetWorldViewbounds();
|
||||
|
||||
/// <summary>
|
||||
@@ -40,10 +44,10 @@ namespace Robust.Client.Graphics
|
||||
/// to UI screen space.
|
||||
/// </summary>
|
||||
/// <param name="projMatrix"></param>
|
||||
void GetScreenProjectionMatrix(out Matrix3 projMatrix);
|
||||
void GetScreenProjectionMatrix(out Matrix3x2 projMatrix);
|
||||
|
||||
/// <summary>
|
||||
/// Projects a point from world space to UI screen space using the current camera.
|
||||
/// Projects a point from world space to UI screen space using the main viewport.
|
||||
/// </summary>
|
||||
/// <param name="point">Point in world to transform.</param>
|
||||
/// <returns>Corresponding point in UI screen space.</returns>
|
||||
|
||||
@@ -367,7 +367,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle.UseShader(entry.Sprite.PostShader);
|
||||
CalcScreenMatrices(viewport.Size, out var proj, out var view);
|
||||
_renderHandle.SetProjView(proj, view);
|
||||
_renderHandle.SetModelTransform(Matrix3.Identity);
|
||||
_renderHandle.SetModelTransform(Matrix3x2.Identity);
|
||||
|
||||
var rounded = roundedPos - entityPostRenderTarget.Size / 2;
|
||||
|
||||
@@ -480,7 +480,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var worldBounds = CalcWorldBounds(viewport);
|
||||
var worldAABB = worldBounds.CalcBoundingBox();
|
||||
|
||||
if (_eyeManager.CurrentMap != MapId.Nullspace)
|
||||
if (eye.Position.MapId != MapId.Nullspace)
|
||||
{
|
||||
using (DebugGroup("Lights"))
|
||||
using (_prof.Group("Lights"))
|
||||
@@ -530,7 +530,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Because the math is wrong.
|
||||
// So there are distortions from incorrect projection.
|
||||
_renderHandle.UseShader(_fovDebugShaderInstance);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
|
||||
var pos = UIBox2.FromDimensions(viewport.Size / 2 - new Vector2(200, 200), new Vector2(400, 400));
|
||||
_renderHandle.DrawingHandleScreen.DrawTextureRect(FovTexture, pos);
|
||||
}
|
||||
@@ -538,15 +538,18 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (DebugLayers == ClydeDebugLayers.Light)
|
||||
{
|
||||
_renderHandle.UseShader(null);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
|
||||
_renderHandle.DrawingHandleScreen.DrawTextureRect(
|
||||
viewport.WallBleedIntermediateRenderTarget2.Texture,
|
||||
UIBox2.FromDimensions(Vector2.Zero, viewport.Size), new Color(1, 1, 1, 0.5f));
|
||||
}
|
||||
|
||||
using (_prof.Group("Overlays WS"))
|
||||
if (eye.Position.MapId != MapId.Nullspace)
|
||||
{
|
||||
RenderOverlays(viewport, OverlaySpace.WorldSpace, worldAABB, worldBounds);
|
||||
using (_prof.Group("Overlays WS"))
|
||||
{
|
||||
RenderOverlays(viewport, OverlaySpace.WorldSpace, worldAABB, worldBounds);
|
||||
}
|
||||
}
|
||||
|
||||
_currentViewport = oldVp;
|
||||
|
||||
@@ -98,28 +98,34 @@ namespace Robust.Client.Graphics.Clyde
|
||||
[FieldOffset(20 * sizeof(float))] public Vector3 ViewMatrixC2;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ProjViewMatrices(in Matrix3 projMatrix, in Matrix3 viewMatrix)
|
||||
public ProjViewMatrices(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix)
|
||||
{
|
||||
ProjMatrixC0 = new Vector3(projMatrix.R0C0, projMatrix.R1C0, projMatrix.R2C0);
|
||||
ProjMatrixC1 = new Vector3(projMatrix.R0C1, projMatrix.R1C1, projMatrix.R2C1);
|
||||
ProjMatrixC2 = new Vector3(projMatrix.R0C2, projMatrix.R1C2, projMatrix.R2C2);
|
||||
// We put the rows of the input matrix into the columns of our GPU matrices
|
||||
// this transpose is required, as in C#, we premultiply vectors with matrices
|
||||
// (vM) while GL postmultiplies vectors with matrices (Mv); however, since
|
||||
// the Matrix3x2 data is stored row-major, and GL uses column-major, the
|
||||
// memory layout is the same (or would be, if Matrix3x2 didn't have an
|
||||
// implicit column)
|
||||
ProjMatrixC0 = new Vector3(projMatrix.M11, projMatrix.M12, 0);
|
||||
ProjMatrixC1 = new Vector3(projMatrix.M21, projMatrix.M22, 0);
|
||||
ProjMatrixC2 = new Vector3(projMatrix.M31, projMatrix.M32, 1);
|
||||
|
||||
ViewMatrixC0 = new Vector3(viewMatrix.R0C0, viewMatrix.R1C0, viewMatrix.R2C0);
|
||||
ViewMatrixC1 = new Vector3(viewMatrix.R0C1, viewMatrix.R1C1, viewMatrix.R2C1);
|
||||
ViewMatrixC2 = new Vector3(viewMatrix.R0C2, viewMatrix.R1C2, viewMatrix.R2C2);
|
||||
ViewMatrixC0 = new Vector3(viewMatrix.M11, viewMatrix.M12, 0);
|
||||
ViewMatrixC1 = new Vector3(viewMatrix.M21, viewMatrix.M22, 0);
|
||||
ViewMatrixC2 = new Vector3(viewMatrix.M31, viewMatrix.M32, 1);
|
||||
}
|
||||
|
||||
public void Apply(Clyde clyde, GLShaderProgram program)
|
||||
{
|
||||
program.SetUniformMaybe("projectionMatrix", new Matrix3(
|
||||
ProjMatrixC0.X, ProjMatrixC1.X, ProjMatrixC2.X,
|
||||
ProjMatrixC0.Y, ProjMatrixC1.Y, ProjMatrixC2.Y,
|
||||
ProjMatrixC0.Z, ProjMatrixC1.Z, ProjMatrixC2.Z
|
||||
program.SetUniformMaybe("projectionMatrix", new Matrix3x2(
|
||||
ProjMatrixC0.X, ProjMatrixC0.Y, // Implicit 0
|
||||
ProjMatrixC1.X, ProjMatrixC1.Y, // Implicit 0
|
||||
ProjMatrixC2.X, ProjMatrixC2.Y // Implicit 1
|
||||
));
|
||||
program.SetUniformMaybe("viewMatrix", new Matrix3(
|
||||
ViewMatrixC0.X, ViewMatrixC1.X, ViewMatrixC2.X,
|
||||
ViewMatrixC0.Y, ViewMatrixC1.Y, ViewMatrixC2.Y,
|
||||
ViewMatrixC0.Z, ViewMatrixC1.Z, ViewMatrixC2.Z
|
||||
program.SetUniformMaybe("viewMatrix", new Matrix3x2(
|
||||
ViewMatrixC0.X, ViewMatrixC0.Y, // Implicit 0
|
||||
ViewMatrixC1.X, ViewMatrixC1.Y, // Implicit 0
|
||||
ViewMatrixC2.X, ViewMatrixC2.Y // Implicit 1
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,18 +492,18 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
var offset = new Vector2(component.Radius, component.Radius);
|
||||
|
||||
Matrix3 matrix;
|
||||
Matrix3x2 matrix;
|
||||
if (mask == null)
|
||||
{
|
||||
matrix = Matrix3.Identity;
|
||||
matrix = Matrix3x2.Identity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only apply rotation if a mask is said, because else it doesn't matter.
|
||||
matrix = Matrix3.CreateRotation(rotation);
|
||||
matrix = Matrix3Helpers.CreateRotation(rotation);
|
||||
}
|
||||
|
||||
(matrix.R0C2, matrix.R1C2) = lightPos;
|
||||
(matrix.M31, matrix.M32) = lightPos;
|
||||
|
||||
_drawQuad(-offset, offset, matrix, lightShader);
|
||||
}
|
||||
@@ -692,7 +692,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// Blur horizontally to _wallBleedIntermediateRenderTarget1.
|
||||
shader.SetUniformMaybe("direction", Vector2.UnitX);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
|
||||
|
||||
SetTexture(TextureUnit.Texture0, viewport.LightBlurTarget.Texture);
|
||||
|
||||
@@ -700,7 +700,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// Blur vertically to _wallBleedIntermediateRenderTarget2.
|
||||
shader.SetUniformMaybe("direction", Vector2.UnitY);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
|
||||
|
||||
SetTexture(TextureUnit.Texture0, viewport.LightRenderTarget.Texture);
|
||||
}
|
||||
@@ -754,14 +754,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// Blur horizontally to _wallBleedIntermediateRenderTarget1.
|
||||
shader.SetUniformMaybe("direction", Vector2.UnitX);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
|
||||
|
||||
SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget1.Texture);
|
||||
BindRenderTargetFull(viewport.WallBleedIntermediateRenderTarget2);
|
||||
|
||||
// Blur vertically to _wallBleedIntermediateRenderTarget2.
|
||||
shader.SetUniformMaybe("direction", Vector2.UnitY);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
|
||||
|
||||
SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget2.Texture);
|
||||
}
|
||||
@@ -909,13 +909,13 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Second modification is that output must be fov-centred (difference-space)
|
||||
uZero -= fovCentre;
|
||||
|
||||
var clipToDiff = new Matrix3(in uX, in uY, in uZero);
|
||||
var clipToDiff = new Matrix3x2(uX.X, uX.Y, uY.X, uY.Y, uZero.X, uZero.Y);
|
||||
|
||||
fovShader.SetUniformMaybe("clipToDiff", clipToDiff);
|
||||
_drawQuad(Vector2.Zero, Vector2.One, Matrix3.Identity, fovShader);
|
||||
_drawQuad(Vector2.Zero, Vector2.One, Matrix3x2.Identity, fovShader);
|
||||
}
|
||||
|
||||
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3 eyeTransform)
|
||||
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3x2 eyeTransform)
|
||||
{
|
||||
using var _ = _prof.Group("UpdateOcclusionGeometry");
|
||||
using var _p = DebugGroup(nameof(UpdateOcclusionGeometry));
|
||||
@@ -968,9 +968,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var worldTransform = xformSystem.GetWorldMatrix(transform, xforms);
|
||||
var box = occluder.BoundingBox;
|
||||
|
||||
var tl = worldTransform.Transform(box.TopLeft);
|
||||
var tr = worldTransform.Transform(box.TopRight);
|
||||
var br = worldTransform.Transform(box.BottomRight);
|
||||
var tl = Vector2.Transform(box.TopLeft, worldTransform);
|
||||
var tr = Vector2.Transform(box.TopRight, worldTransform);
|
||||
var br = Vector2.Transform(box.BottomRight, worldTransform);
|
||||
var bl = tl + br - tr;
|
||||
|
||||
// Faces.
|
||||
@@ -1010,9 +1010,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
//
|
||||
|
||||
// Calculate delta positions from camera.
|
||||
var dTl = eyeTransform.Transform(tl);
|
||||
var dTr = eyeTransform.Transform(tr);
|
||||
var dBl = eyeTransform.Transform(bl);
|
||||
var dTl = Vector2.Transform(tl, eyeTransform);
|
||||
var dTr = Vector2.Transform(tr, eyeTransform);
|
||||
var dBl = Vector2.Transform(bl, eyeTransform);
|
||||
var dBr = dBl + dTr - dTl;
|
||||
|
||||
// Get which neighbors are occluding.
|
||||
|
||||
@@ -33,17 +33,17 @@ namespace Robust.Client.Graphics.Clyde
|
||||
DrawingHandleWorld = new DrawingHandleWorldImpl(white, this);
|
||||
}
|
||||
|
||||
public void SetModelTransform(in Matrix3 matrix)
|
||||
public void SetModelTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_clyde.DrawSetModelTransform(matrix);
|
||||
}
|
||||
|
||||
public Matrix3 GetModelTransform()
|
||||
public Matrix3x2 GetModelTransform()
|
||||
{
|
||||
return _clyde.DrawGetModelTransform();
|
||||
}
|
||||
|
||||
public void SetProjView(in Matrix3 proj, in Matrix3 view)
|
||||
public void SetProjView(in Matrix3x2 proj, in Matrix3x2 view)
|
||||
{
|
||||
_clyde.DrawSetProjViewTransform(proj, view);
|
||||
}
|
||||
@@ -177,9 +177,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var oldModel = _clyde._currentMatrixModel;
|
||||
|
||||
var newModel = oldModel;
|
||||
position += new Vector2(oldModel.R0C2, oldModel.R1C2);
|
||||
newModel.R0C2 = 0;
|
||||
newModel.R1C2 = 0;
|
||||
position += new Vector2(oldModel.M31, oldModel.M32);
|
||||
newModel.M31 = 0;
|
||||
newModel.M32 = 0;
|
||||
SetModelTransform(newModel);
|
||||
|
||||
// Switch rendering to pseudo-world space.
|
||||
@@ -194,7 +194,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Maaaaybe this is meant to have a minus sign.
|
||||
var rot = -(float) eyeRot.Theta;
|
||||
|
||||
var view = Matrix3.CreateTransform(ofsX, ofsY, rot, scale.X, scale.Y);
|
||||
var view = Matrix3Helpers.CreateTransform(ofsX, ofsY, rot, scale.X, scale.Y);
|
||||
SetProjView(proj, view);
|
||||
}
|
||||
|
||||
@@ -297,12 +297,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle = renderHandle;
|
||||
}
|
||||
|
||||
public override void SetTransform(in Matrix3 matrix)
|
||||
public override void SetTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_renderHandle.SetModelTransform(matrix);
|
||||
}
|
||||
|
||||
public override Matrix3 GetTransform()
|
||||
public override Matrix3x2 GetTransform()
|
||||
{
|
||||
return _renderHandle.GetModelTransform();
|
||||
}
|
||||
@@ -402,12 +402,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle = renderHandle;
|
||||
}
|
||||
|
||||
public override void SetTransform(in Matrix3 matrix)
|
||||
public override void SetTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_renderHandle.SetModelTransform(matrix);
|
||||
}
|
||||
|
||||
public override Matrix3 GetTransform()
|
||||
public override Matrix3x2 GetTransform()
|
||||
{
|
||||
return _renderHandle.GetModelTransform();
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// This matrix is applied to most normal geometry coming in.
|
||||
// Some is applied while the batch is being created (e.g. simple texture draw calls).
|
||||
// For DrawPrimitives OTOH the model matrix is passed along with the render command so is applied in the shader.
|
||||
private Matrix3 _currentMatrixModel = Matrix3.Identity;
|
||||
private Matrix3x2 _currentMatrixModel = Matrix3x2.Identity;
|
||||
|
||||
// Buffers and data for the batching system. Written into during (queue) and processed during (submit).
|
||||
private readonly Vertex2D[] BatchVertexData = new Vertex2D[MaxBatchQuads * 4];
|
||||
@@ -84,8 +84,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// Current projection & view matrices that are being used ot render.
|
||||
// This gets updated to keep track during (queue) and (misc), but not during (submit).
|
||||
private Matrix3 _currentMatrixProj;
|
||||
private Matrix3 _currentMatrixView;
|
||||
private Matrix3x2 _currentMatrixProj;
|
||||
private Matrix3x2 _currentMatrixView;
|
||||
|
||||
// (queue) and (misc), current state of the scissor test. Null if disabled.
|
||||
private UIBox2i? _currentScissorState;
|
||||
@@ -110,25 +110,25 @@ namespace Robust.Client.Graphics.Clyde
|
||||
UniformConstantsUBO.Reallocate(constants);
|
||||
}
|
||||
|
||||
private void CalcScreenMatrices(in Vector2i screenSize, out Matrix3 proj, out Matrix3 view)
|
||||
private void CalcScreenMatrices(in Vector2i screenSize, out Matrix3x2 proj, out Matrix3x2 view)
|
||||
{
|
||||
proj = Matrix3.Identity;
|
||||
proj.R0C0 = 2f / screenSize.X;
|
||||
proj.R1C1 = -2f / screenSize.Y;
|
||||
proj.R0C2 = -1;
|
||||
proj.R1C2 = 1;
|
||||
proj = Matrix3x2.Identity;
|
||||
proj.M11 = 2f / screenSize.X;
|
||||
proj.M22 = -2f / screenSize.Y;
|
||||
proj.M31 = -1;
|
||||
proj.M32 = 1;
|
||||
|
||||
if (_currentRenderTarget.FlipY)
|
||||
{
|
||||
proj.R1C1 *= -1;
|
||||
proj.R1C2 *= -1;
|
||||
proj.M22 *= -1;
|
||||
proj.M32 *= -1;
|
||||
}
|
||||
|
||||
view = Matrix3.Identity;
|
||||
view = Matrix3x2.Identity;
|
||||
}
|
||||
|
||||
private void CalcWorldMatrices(in Vector2i screenSize, in Vector2 renderScale, IEye eye,
|
||||
out Matrix3 proj, out Matrix3 view)
|
||||
out Matrix3x2 proj, out Matrix3x2 view)
|
||||
{
|
||||
eye.GetViewMatrix(out view, renderScale);
|
||||
|
||||
@@ -136,20 +136,20 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void CalcWorldProjMatrix(in Vector2i screenSize, out Matrix3 proj)
|
||||
private void CalcWorldProjMatrix(in Vector2i screenSize, out Matrix3x2 proj)
|
||||
{
|
||||
proj = Matrix3.Identity;
|
||||
proj.R0C0 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
|
||||
proj.R1C1 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
|
||||
proj = Matrix3x2.Identity;
|
||||
proj.M11 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
|
||||
proj.M22 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
|
||||
|
||||
if (_currentRenderTarget.FlipY)
|
||||
{
|
||||
proj.R1C1 *= -1;
|
||||
proj.R1C2 *= -1;
|
||||
proj.M22 *= -1;
|
||||
proj.M32 *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetProjViewBuffer(in Matrix3 proj, in Matrix3 view)
|
||||
private void SetProjViewBuffer(in Matrix3x2 proj, in Matrix3x2 view)
|
||||
{
|
||||
// TODO: Fix perf here.
|
||||
// This immediately causes a glBufferData() call every time this is changed.
|
||||
@@ -160,7 +160,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
ProjViewUBO.Reallocate(combined);
|
||||
}
|
||||
|
||||
private void SetProjViewFull(in Matrix3 proj, in Matrix3 view)
|
||||
private void SetProjViewFull(in Matrix3x2 proj, in Matrix3x2 view)
|
||||
{
|
||||
_currentMatrixProj = proj;
|
||||
_currentMatrixView = view;
|
||||
@@ -285,21 +285,21 @@ namespace Robust.Client.Graphics.Clyde
|
||||
};
|
||||
}
|
||||
|
||||
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3 modelMatrix, GLShaderProgram program)
|
||||
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3x2 modelMatrix, GLShaderProgram program)
|
||||
{
|
||||
DrawQuadWithVao(QuadVAO, a, b, modelMatrix, program);
|
||||
}
|
||||
|
||||
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3 modelMatrix,
|
||||
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3x2 modelMatrix,
|
||||
GLShaderProgram program)
|
||||
{
|
||||
BindVertexArray(vao.Handle);
|
||||
CheckGlError();
|
||||
|
||||
var rectTransform = Matrix3.Identity;
|
||||
(rectTransform.R0C0, rectTransform.R1C1) = b - a;
|
||||
(rectTransform.R0C2, rectTransform.R1C2) = a;
|
||||
rectTransform.Multiply(modelMatrix);
|
||||
var rectTransform = Matrix3x2.Identity;
|
||||
(rectTransform.M11, rectTransform.M22) = b - a;
|
||||
(rectTransform.M31, rectTransform.M32) = a;
|
||||
rectTransform = rectTransform * modelMatrix;
|
||||
program.SetUniformMaybe(UniIModelMatrix, rectTransform);
|
||||
|
||||
_debugStats.LastGLDrawCalls += 1;
|
||||
@@ -315,7 +315,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
FlushBatchQueue();
|
||||
|
||||
// Reset renderer state.
|
||||
_currentMatrixModel = Matrix3.Identity;
|
||||
_currentMatrixModel = Matrix3x2.Identity;
|
||||
_queuedShaderInstance = _defaultShader;
|
||||
SetScissorFull(null);
|
||||
}
|
||||
@@ -496,7 +496,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case bool b:
|
||||
program.SetUniform(name, b ? 1 : 0);
|
||||
break;
|
||||
case Matrix3 matrix3:
|
||||
case Matrix3x2 matrix3:
|
||||
program.SetUniform(name, matrix3);
|
||||
break;
|
||||
case Matrix4 matrix4:
|
||||
@@ -532,17 +532,17 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return ref command;
|
||||
}
|
||||
|
||||
private void DrawSetModelTransform(in Matrix3 matrix)
|
||||
private void DrawSetModelTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_currentMatrixModel = matrix;
|
||||
}
|
||||
|
||||
private Matrix3 DrawGetModelTransform()
|
||||
private Matrix3x2 DrawGetModelTransform()
|
||||
{
|
||||
return _currentMatrixModel;
|
||||
}
|
||||
|
||||
private void DrawSetProjViewTransform(in Matrix3 proj, in Matrix3 view)
|
||||
private void DrawSetProjViewTransform(in Matrix3x2 proj, in Matrix3x2 view)
|
||||
{
|
||||
BreakBatch();
|
||||
|
||||
@@ -571,9 +571,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
EnsureBatchSpaceAvailable(4, GetQuadBatchIndexCount());
|
||||
EnsureBatchState(texture, true, GetQuadBatchPrimitiveType(), _queuedShader);
|
||||
|
||||
bl = _currentMatrixModel.Transform(bl);
|
||||
br = _currentMatrixModel.Transform(br);
|
||||
tr = _currentMatrixModel.Transform(tr);
|
||||
bl = Vector2.Transform(bl, _currentMatrixModel);
|
||||
br = Vector2.Transform(br, _currentMatrixModel);
|
||||
tr = Vector2.Transform(tr, _currentMatrixModel);
|
||||
tl = tr + bl - br;
|
||||
|
||||
// TODO: split batch if necessary.
|
||||
@@ -676,8 +676,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
EnsureBatchSpaceAvailable(2, 0);
|
||||
EnsureBatchState(_stockTextureWhite.TextureId, false, BatchPrimitiveType.LineList, _queuedShader);
|
||||
|
||||
a = _currentMatrixModel.Transform(a);
|
||||
b = _currentMatrixModel.Transform(b);
|
||||
a = Vector2.Transform(a, _currentMatrixModel);
|
||||
b = Vector2.Transform(b, _currentMatrixModel);
|
||||
|
||||
// TODO: split batch if necessary.
|
||||
var vIdx = BatchVertexIndex;
|
||||
@@ -807,7 +807,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
command.DrawBatch.ShaderInstance = metaData.ShaderInstance;
|
||||
|
||||
command.DrawBatch.Count = currentIndex - metaData.StartIndex;
|
||||
command.DrawBatch.ModelMatrix = Matrix3.Identity;
|
||||
command.DrawBatch.ModelMatrix = Matrix3x2.Identity;
|
||||
|
||||
_debugStats.LastBatches += 1;
|
||||
}
|
||||
@@ -882,7 +882,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_queuedRenderCommands.Clear();
|
||||
_currentViewport = null;
|
||||
_lightingReady = false;
|
||||
_currentMatrixModel = Matrix3.Identity;
|
||||
_currentMatrixModel = Matrix3x2.Identity;
|
||||
SetScissorFull(null);
|
||||
BindRenderTargetFull(_mainWindow!.RenderTarget);
|
||||
_batchMetaData = null;
|
||||
@@ -961,13 +961,13 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public BatchPrimitiveType PrimitiveType;
|
||||
|
||||
// TODO: this makes the render commands so much more large please remove.
|
||||
public Matrix3 ModelMatrix;
|
||||
public Matrix3x2 ModelMatrix;
|
||||
}
|
||||
|
||||
private struct RenderCommandProjViewMatrix
|
||||
{
|
||||
public Matrix3 ProjMatrix;
|
||||
public Matrix3 ViewMatrix;
|
||||
public Matrix3x2 ProjMatrix;
|
||||
public Matrix3x2 ViewMatrix;
|
||||
}
|
||||
|
||||
private struct RenderCommandScissor
|
||||
@@ -1056,11 +1056,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private readonly struct FullStoredRendererState
|
||||
{
|
||||
public readonly Matrix3 ProjMatrix;
|
||||
public readonly Matrix3 ViewMatrix;
|
||||
public readonly Matrix3x2 ProjMatrix;
|
||||
public readonly Matrix3x2 ViewMatrix;
|
||||
public readonly LoadedRenderTarget RenderTarget;
|
||||
|
||||
public FullStoredRendererState(in Matrix3 projMatrix, in Matrix3 viewMatrix,
|
||||
public FullStoredRendererState(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix,
|
||||
LoadedRenderTarget renderTarget)
|
||||
{
|
||||
ProjMatrix = projMatrix;
|
||||
|
||||
@@ -506,7 +506,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix3 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
|
||||
{
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
data.ParametersDirty = true;
|
||||
|
||||
@@ -664,7 +664,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
fixed (byte* p = buffer)
|
||||
{
|
||||
GL.GetnTexImage(TextureTarget.Texture2D, 0, PF.Rgba, PT.UnsignedByte, bufSize, (IntPtr) p);
|
||||
GL.GetTexImage(TextureTarget.Texture2D, 0, PF.Rgba, PT.UnsignedByte, (IntPtr) p);
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, curTexture2D);
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// view matrix
|
||||
vp.Eye.GetViewMatrixInv(out var viewMatrixInv, vp.RenderScale);
|
||||
point = viewMatrixInv * point;
|
||||
point = Vector2.Transform(point, viewMatrixInv);
|
||||
|
||||
return point;
|
||||
}
|
||||
@@ -136,7 +136,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// view matrix
|
||||
Eye.GetViewMatrixInv(out var viewMatrixInv, RenderScale);
|
||||
newPoint = viewMatrixInv * newPoint;
|
||||
newPoint = Vector2.Transform(newPoint, viewMatrixInv);
|
||||
|
||||
return new MapCoordinates(newPoint, Eye.Position.MapId);
|
||||
}
|
||||
@@ -150,7 +150,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var newPoint = point;
|
||||
|
||||
eye.GetViewMatrix(out var viewMatrix, RenderScale);
|
||||
newPoint = viewMatrix * newPoint;
|
||||
newPoint = Vector2.Transform(newPoint, viewMatrix);
|
||||
|
||||
// (inlined version of UiProjMatrix)
|
||||
newPoint *= new Vector2(1, -1) * EyeManager.PixelsPerMeter;
|
||||
@@ -159,14 +159,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
public Matrix3 GetWorldToLocalMatrix()
|
||||
public Matrix3x2 GetWorldToLocalMatrix()
|
||||
{
|
||||
if (Eye == null)
|
||||
return Matrix3.Identity;
|
||||
return Matrix3x2.Identity;
|
||||
|
||||
Eye.GetViewMatrix(out var viewMatrix, RenderScale * new Vector2(EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter));
|
||||
viewMatrix.R0C2 += Size.X / 2f;
|
||||
viewMatrix.R1C2 += Size.Y / 2f;
|
||||
viewMatrix.M31 += Size.X / 2f;
|
||||
viewMatrix.M32 += Size.Y / 2f;
|
||||
return viewMatrix;
|
||||
}
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix3 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return default;
|
||||
}
|
||||
|
||||
public Matrix3 GetWorldToLocalMatrix() => default;
|
||||
public Matrix3x2 GetWorldToLocalMatrix() => default;
|
||||
|
||||
public Vector2 WorldToLocal(Vector2 point)
|
||||
{
|
||||
|
||||
@@ -247,28 +247,33 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GL.Uniform1(uniformId, singles.Length, singles);
|
||||
}
|
||||
|
||||
public void SetUniform(string uniformName, in Matrix3 matrix)
|
||||
public void SetUniform(string uniformName, in Matrix3x2 matrix)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
SetUniformDirect(uniformId, matrix);
|
||||
}
|
||||
|
||||
public void SetUniform(int uniformName, in Matrix3 matrix)
|
||||
public void SetUniform(int uniformName, in Matrix3x2 matrix)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
SetUniformDirect(uniformId, matrix);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe void SetUniformDirect(int slot, in Matrix3 value, bool transpose=true)
|
||||
private unsafe void SetUniformDirect(int slot, in Matrix3x2 value)
|
||||
{
|
||||
Matrix3 tmpTranspose = value;
|
||||
if (transpose)
|
||||
{
|
||||
// transposition not supported on GLES2, & no access to _hasGLES
|
||||
tmpTranspose.Transpose();
|
||||
}
|
||||
GL.UniformMatrix3(slot, 1, false, (float*) &tmpTranspose);
|
||||
// We put the rows of the input matrix into the columns of our GPU matrices
|
||||
// this transpose is required, as in C#, we premultiply vectors with matrices
|
||||
// (vM) while GL postmultiplies vectors with matrices (Mv); however, since
|
||||
// the Matrix3x2 data is stored row-major, and GL uses column-major, the
|
||||
// memory layout is the same (or would be, if Matrix3x2 didn't have an
|
||||
// implicit column)
|
||||
var buf = stackalloc float[9]{
|
||||
value.M11, value.M12, 0,
|
||||
value.M21, value.M22, 0,
|
||||
value.M31, value.M32, 1
|
||||
};
|
||||
GL.UniformMatrix3(slot, 1, false, (float*)buf);
|
||||
_clyde.CheckGlError();
|
||||
}
|
||||
|
||||
@@ -474,7 +479,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformMaybe(string uniformName, in Matrix3 value)
|
||||
public void SetUniformMaybe(string uniformName, in Matrix3x2 value)
|
||||
{
|
||||
if (TryGetUniform(uniformName, out var slot))
|
||||
{
|
||||
@@ -490,7 +495,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformMaybe(int uniformName, in Matrix3 value)
|
||||
public void SetUniformMaybe(int uniformName, in Matrix3x2 value)
|
||||
{
|
||||
if (TryGetUniform(uniformName, out var slot))
|
||||
{
|
||||
|
||||
@@ -44,19 +44,19 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
var matrix = Matrix3.CreateTransform(in position, in rotation, in scale);
|
||||
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation, in scale);
|
||||
SetTransform(in matrix);
|
||||
}
|
||||
|
||||
public void SetTransform(in Vector2 position, in Angle rotation)
|
||||
{
|
||||
var matrix = Matrix3.CreateTransform(in position, in rotation);
|
||||
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation);
|
||||
SetTransform(in matrix);
|
||||
}
|
||||
|
||||
public abstract void SetTransform(in Matrix3 matrix);
|
||||
public abstract void SetTransform(in Matrix3x2 matrix);
|
||||
|
||||
public abstract Matrix3 GetTransform();
|
||||
public abstract Matrix3x2 GetTransform();
|
||||
|
||||
public abstract void UseShader(ShaderInstance? shader);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Robust.Client.Graphics
|
||||
/// <summary>
|
||||
/// Matrix equivalent of <see cref="LocalToWorld(Vector2)"/>.
|
||||
/// </summary>
|
||||
Matrix3 GetWorldToLocalMatrix();
|
||||
Matrix3x2 GetWorldToLocalMatrix();
|
||||
|
||||
/// <summary>
|
||||
/// Converts a point in world-space to the viewport's screen coordinates.
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Robust.Client.Graphics
|
||||
/// If set to true, <see cref="ScreenTexture"/> will be set to the current frame (at the moment before the overlay is rendered). This can be costly to performance, but
|
||||
/// some shaders will require it as a passed in uniform to operate.
|
||||
/// </summary>
|
||||
public virtual bool RequestScreenTexture => false;
|
||||
public virtual bool RequestScreenTexture { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="RequestScreenTexture"> is true, then this will be set to the texture corresponding to the current frame. If false, it will always be null.
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Robust.Client.Graphics
|
||||
SetParameterImpl(name, value);
|
||||
}
|
||||
|
||||
public void SetParameter(string name, in Matrix3 value)
|
||||
public void SetParameter(string name, in Matrix3x2 value)
|
||||
{
|
||||
EnsureAlive();
|
||||
EnsureMutable();
|
||||
@@ -219,7 +219,7 @@ namespace Robust.Client.Graphics
|
||||
private protected abstract void SetParameterImpl(string name, int value);
|
||||
private protected abstract void SetParameterImpl(string name, Vector2i value);
|
||||
private protected abstract void SetParameterImpl(string name, bool value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix3 value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix3x2 value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix4 value);
|
||||
private protected abstract void SetParameterImpl(string name, Texture value);
|
||||
private protected abstract void SetStencilImpl(StencilParameters value);
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Robust.Client.Graphics
|
||||
return node.AsVector4();
|
||||
}
|
||||
case ShaderDataType.Mat3:
|
||||
return node.AsMatrix3();
|
||||
return node.AsMatrix3x2();
|
||||
case ShaderDataType.Mat4:
|
||||
return node.AsMatrix4();
|
||||
default:
|
||||
@@ -219,7 +219,7 @@ namespace Robust.Client.Graphics
|
||||
case bool i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
case Matrix3 i:
|
||||
case Matrix3x2 i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
case Matrix4 i:
|
||||
|
||||
@@ -346,7 +346,7 @@ namespace Robust.Client.Input
|
||||
{
|
||||
if (binding.CanRepeat)
|
||||
{
|
||||
return SetBindState(binding, BoundKeyState.Down, uiOnly);
|
||||
return SetBindState(binding, BoundKeyState.Down, uiOnly, isRepeat);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -375,7 +375,7 @@ namespace Robust.Client.Input
|
||||
SetBindState(binding, BoundKeyState.Up);
|
||||
}
|
||||
|
||||
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false)
|
||||
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false, bool isRepeat = false)
|
||||
{
|
||||
if (binding.BindingType == KeyBindingType.Command && state == BoundKeyState.Down)
|
||||
{
|
||||
@@ -387,6 +387,7 @@ namespace Robust.Client.Input
|
||||
// I honestly have no idea what the best solution here is.
|
||||
// note from the future: context switches won't cause re-entrancy anymore because InputContextContainer defers context switches
|
||||
DebugTools.Assert(!_currentlyFindingViewport, "Re-entrant key events??");
|
||||
DebugTools.Assert(!isRepeat || binding.CanRepeat);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -399,7 +400,7 @@ namespace Robust.Client.Input
|
||||
binding.State = state;
|
||||
|
||||
var eventArgs = new BoundKeyEventArgs(binding.Function, binding.State,
|
||||
MouseScreenPosition, binding.CanFocus);
|
||||
MouseScreenPosition, binding.CanFocus, isRepeat);
|
||||
|
||||
// UI returns true here into blockPass if it wants to prevent us from giving input events
|
||||
// to the viewport, but doesn't want it hard-handled so we keep processing possible key actions.
|
||||
|
||||
@@ -120,6 +120,6 @@ public sealed class TileEdgeOverlay : GridOverlay
|
||||
}
|
||||
}
|
||||
|
||||
args.WorldHandle.SetTransform(Matrix3.Identity);
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Robust.Client.Physics
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Client.Placement.Modes
|
||||
|
||||
public SnapgridCenter(PlacementManager pMan) : base(pMan) { }
|
||||
|
||||
public override void Render(DrawingHandleWorld handle)
|
||||
public override void Render(in OverlayDrawArgs args)
|
||||
{
|
||||
if (Grid != null)
|
||||
{
|
||||
@@ -34,18 +34,18 @@ namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
var from = ScreenToWorld(new Vector2(a, 0));
|
||||
var to = ScreenToWorld(new Vector2(a, viewportSize.Y));
|
||||
handle.DrawLine(from, to, new Color(0, 0, 1f));
|
||||
args.WorldHandle.DrawLine(from, to, new Color(0, 0, 1f));
|
||||
}
|
||||
for (var a = gridstart.Y; a < viewportSize.Y; a += SnapSize * EyeManager.PixelsPerMeter)
|
||||
{
|
||||
var from = ScreenToWorld(new Vector2(0, a));
|
||||
var to = ScreenToWorld(new Vector2(viewportSize.X, a));
|
||||
handle.DrawLine(from, to, new Color(0, 0, 1f));
|
||||
args.WorldHandle.DrawLine(from, to, new Color(0, 0, 1f));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw grid BELOW the ghost thing.
|
||||
base.Render(handle);
|
||||
base.Render(args);
|
||||
}
|
||||
|
||||
public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
|
||||
|
||||
@@ -628,20 +628,20 @@ namespace Robust.Client.Placement
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Render(DrawingHandleWorld handle)
|
||||
private void Render(in OverlayDrawArgs args)
|
||||
{
|
||||
if (CurrentMode == null || !IsActive)
|
||||
{
|
||||
if (EraserRect.HasValue)
|
||||
{
|
||||
handle.UseShader(_drawingShader);
|
||||
handle.DrawRect(EraserRect.Value, new Color(255, 0, 0, 50));
|
||||
handle.UseShader(null);
|
||||
args.WorldHandle.UseShader(_drawingShader);
|
||||
args.WorldHandle.DrawRect(EraserRect.Value, new Color(255, 0, 0, 50));
|
||||
args.WorldHandle.UseShader(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentMode.Render(handle);
|
||||
CurrentMode.Render(args);
|
||||
|
||||
if (CurrentPermission is not {Range: > 0} ||
|
||||
!CurrentMode.RangeRequired ||
|
||||
@@ -650,7 +650,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
var worldPos = EntityManager.GetComponent<TransformComponent>(controlled).WorldPosition;
|
||||
|
||||
handle.DrawCircle(worldPos, CurrentPermission.Range, new Color(1, 1, 1, 0.25f));
|
||||
args.WorldHandle.DrawCircle(worldPos, CurrentPermission.Range, new Color(1, 1, 1, 0.25f));
|
||||
}
|
||||
|
||||
private void HandleStartPlacement(MsgPlacement msg)
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Robust.Client.Placement
|
||||
/// <returns></returns>
|
||||
public abstract bool IsValidPosition(EntityCoordinates position);
|
||||
|
||||
public virtual void Render(DrawingHandleWorld handle)
|
||||
public virtual void Render(in OverlayDrawArgs args)
|
||||
{
|
||||
var uid = pManager.CurrentPlacementOverlayEntity;
|
||||
if (!pManager.EntityManager.TryGetComponent(uid, out SpriteComponent? sprite) || !sprite.Visible)
|
||||
@@ -125,7 +125,8 @@ namespace Robust.Client.Placement
|
||||
var worldRot = pManager.EntityManager.GetComponent<TransformComponent>(coordinate.EntityId).WorldRotation + dirAng;
|
||||
|
||||
sprite.Color = IsValidPosition(coordinate) ? ValidPlaceColor : InvalidPlaceColor;
|
||||
spriteSys.Render(uid.Value, sprite, handle, pManager.EyeManager.CurrentEye.Rotation, worldRot, worldPos);
|
||||
var rot = args.Viewport.Eye?.Rotation ?? default;
|
||||
spriteSys.Render(uid.Value, sprite, args.WorldHandle, rot, worldRot, worldPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
_manager.Render(args.WorldHandle);
|
||||
_manager.Render(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,8 +261,8 @@ namespace Robust.Client.Player
|
||||
{
|
||||
// This is a new userid, so we create a new session.
|
||||
DebugTools.Assert(state.UserId != LocalPlayer?.UserId);
|
||||
var newSession = (CommonSession) CreateAndAddSession(state.UserId, state.Name);
|
||||
newSession.Ping = state.Ping;
|
||||
var newSession = (ICommonSessionInternal)CreateAndAddSession(state.UserId, state.Name);
|
||||
newSession.SetPing(state.Ping);
|
||||
SetStatus(newSession, state.Status);
|
||||
SetAttachedEntity(newSession, controlled, out _, true);
|
||||
dirty = true;
|
||||
@@ -279,9 +279,9 @@ namespace Robust.Client.Player
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
var local = (CommonSession) session;
|
||||
local.Name = state.Name;
|
||||
local.Ping = state.Ping;
|
||||
var local = (ICommonSessionInternal)session;
|
||||
local.SetName(state.Name);
|
||||
local.SetPing(state.Ping);
|
||||
SetStatus(local, state.Status);
|
||||
SetAttachedEntity(local, controlled, out _, true);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,31 @@ namespace Robust.Client.Replays.Loading;
|
||||
// so that when jumping to tick 1001 the client only has to apply states for tick 1000 and 1001, instead of 0, 1, 2, ...
|
||||
public sealed partial class ReplayLoadManager
|
||||
{
|
||||
// Scratch data used by UpdateEntityStates.
|
||||
// Avoids copying changes for every change to an entity between checkpoints, instead copies once per checkpoint on
|
||||
// first change. We can also use this to avoid building a dictionary of ComponentChange inside the inner loop.
|
||||
private class UpdateScratchData
|
||||
{
|
||||
public Dictionary<ushort, ComponentChange> Changes;
|
||||
public EntityState lastChange;
|
||||
public HashSet<ushort>? netComps;
|
||||
|
||||
public UpdateScratchData(EntityState oldEntState)
|
||||
{
|
||||
Changes = oldEntState.ComponentChanges.Value.ToDictionary(x => x.NetID);
|
||||
lastChange = oldEntState;
|
||||
netComps = oldEntState.NetComponents;
|
||||
}
|
||||
|
||||
public EntityState BakeChanges()
|
||||
{
|
||||
return new EntityState(lastChange.NetEntity,
|
||||
Changes.Values.ToList(),
|
||||
lastChange.EntityLastModified,
|
||||
netComps);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(CheckpointState[], TimeSpan[])> GenerateCheckpointsAsync(
|
||||
ReplayMessage? initMessages,
|
||||
HashSet<string> initialCvars,
|
||||
@@ -133,6 +158,12 @@ public sealed partial class ReplayLoadManager
|
||||
var spawnedTracker = 0;
|
||||
var stateTracker = 0;
|
||||
var curState = state0;
|
||||
|
||||
var stats_due_ticks = 0;
|
||||
var stats_due_spawned = 0;
|
||||
var stats_due_state = 0;
|
||||
|
||||
var modifiedEntities = new Dictionary<NetEntity, UpdateScratchData>();
|
||||
for (var i = 1; i < states.Count; i++)
|
||||
{
|
||||
if (i % 10 == 0)
|
||||
@@ -143,23 +174,42 @@ public sealed partial class ReplayLoadManager
|
||||
DebugTools.Assert(curState.FromSequence <= lastState.ToSequence);
|
||||
|
||||
UpdatePlayerStates(curState.PlayerStates.Span, playerStates);
|
||||
UpdateEntityStates(curState.EntityStates.Span, entStates, ref spawnedTracker, ref stateTracker, detached);
|
||||
UpdateEntityStates(curState.EntityStates.Span, entStates, modifiedEntities, ref spawnedTracker, ref stateTracker, detached);
|
||||
UpdateMessages(messages[i], uploadedFiles, prototypes, cvars, detachQueue, ref timeBase);
|
||||
ProcessQueue(curState.ToSequence, detachQueue, detached, entStates);
|
||||
UpdateDeletions(curState.EntityDeletions, entStates, detached);
|
||||
UpdateDeletions(curState.EntityDeletions, entStates, detached, modifiedEntities);
|
||||
serverTime[i] = GetTime(curState.ToSequence) - initialTime;
|
||||
ticksSinceLastCheckpoint++;
|
||||
|
||||
// Don't create checkpoints too frequently no matter the circumstance
|
||||
if (ticksSinceLastCheckpoint < _checkpointMinInterval)
|
||||
continue;
|
||||
|
||||
// Check if enough time, spawned entities or changed states have occurred.
|
||||
if (ticksSinceLastCheckpoint < _checkpointInterval
|
||||
&& spawnedTracker < _checkpointEntitySpawnThreshold
|
||||
&& stateTracker < _checkpointEntityStateThreshold)
|
||||
{
|
||||
continue;
|
||||
|
||||
// Track and update statistics about why checkpoints are getting created:
|
||||
if (ticksSinceLastCheckpoint >= _checkpointInterval)
|
||||
{
|
||||
stats_due_ticks += 1;
|
||||
}
|
||||
else if (spawnedTracker >= _checkpointEntitySpawnThreshold)
|
||||
{
|
||||
stats_due_spawned += 1;
|
||||
}
|
||||
else if (stateTracker >= _checkpointEntityStateThreshold)
|
||||
{
|
||||
stats_due_state += 1;
|
||||
}
|
||||
|
||||
ticksSinceLastCheckpoint = 0;
|
||||
spawnedTracker = 0;
|
||||
stateTracker = 0;
|
||||
ApplyModifiedEntities(entStates, modifiedEntities);
|
||||
|
||||
var newState = new GameState(GameTick.Zero,
|
||||
curState.ToSequence,
|
||||
default,
|
||||
@@ -169,7 +219,8 @@ public sealed partial class ReplayLoadManager
|
||||
checkPoints.Add(new CheckpointState(newState, timeBase, cvars, i, detached));
|
||||
}
|
||||
|
||||
_sawmill.Info($"Finished generating checkpoints. Elapsed time: {st.Elapsed}");
|
||||
_sawmill.Info($"Finished generating {checkPoints.Count} checkpoints. Elapsed time: {st.Elapsed}. Checkpoint every {(float)states.Count / checkPoints.Count} ticks on average");
|
||||
_sawmill.Info($"Checkpoint stats - Spawning: {stats_due_spawned} StateChanges: {stats_due_state} Ticks: {stats_due_ticks}. ");
|
||||
await callback(states.Count, states.Count, LoadingState.ProcessingFiles, false);
|
||||
return (checkPoints.ToArray(), serverTime);
|
||||
}
|
||||
@@ -316,16 +367,18 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
private void UpdateDeletions(NetListAsArray<NetEntity> entityDeletions,
|
||||
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached)
|
||||
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached, Dictionary<NetEntity, UpdateScratchData> modifiedEntities)
|
||||
{
|
||||
foreach (var ent in entityDeletions.Span)
|
||||
{
|
||||
entStates.Remove(ent);
|
||||
detached.Remove(ent);
|
||||
modifiedEntities.Remove(ent);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<NetEntity, EntityState> entStates,
|
||||
Dictionary<NetEntity, UpdateScratchData> modified,
|
||||
ref int spawnedTracker, ref int stateTracker, HashSet<NetEntity> detached)
|
||||
{
|
||||
foreach (var entState in span)
|
||||
@@ -340,25 +393,85 @@ public sealed partial class ReplayLoadManager
|
||||
#if DEBUG
|
||||
foreach (var state in modifiedState.ComponentChanges.Value)
|
||||
{
|
||||
DebugTools.Assert(state.State is not IComponentDeltaState delta || delta.FullState);
|
||||
DebugTools.Assert(state.State is not IComponentDeltaState delta);
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get scratch versions (with write access) for entities modified since last checkpoint
|
||||
UpdateScratchData? scratch;
|
||||
if (!modified.TryGetValue(entState.NetEntity, out scratch))
|
||||
{
|
||||
scratch = new UpdateScratchData(oldEntState);
|
||||
modified[entState.NetEntity] = scratch;
|
||||
}
|
||||
|
||||
stateTracker++;
|
||||
DebugTools.Assert(oldEntState.NetEntity == entState.NetEntity);
|
||||
entStates[entState.NetEntity] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
|
||||
// Note this does not change entStates, that change occurs later in ApplyModifiedEntities (to avoid early copies)
|
||||
UpdateScratch(entState, scratch.Changes);
|
||||
if (entState.NetComponents != null)
|
||||
scratch.netComps = entState.NetComponents;
|
||||
scratch.lastChange = entState;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
foreach (var state in entStates[entState.NetEntity].ComponentChanges.Span)
|
||||
{
|
||||
DebugTools.Assert(state.State is not IComponentDeltaState delta || delta.FullState);
|
||||
DebugTools.Assert(state.State is not IComponentDeltaState delta);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyModifiedEntities(Dictionary<NetEntity, EntityState> entStates, Dictionary<NetEntity, UpdateScratchData> modifiedEntities)
|
||||
{
|
||||
foreach (var modified in modifiedEntities)
|
||||
{
|
||||
entStates[modified.Key] = modified.Value.BakeChanges();
|
||||
}
|
||||
|
||||
modifiedEntities.Clear();
|
||||
}
|
||||
|
||||
private void UpdateScratch(
|
||||
EntityState newState,
|
||||
Dictionary<ushort, ComponentChange> oldState)
|
||||
{
|
||||
// remove any deleted components
|
||||
if (newState.NetComponents != null)
|
||||
{
|
||||
foreach (var change in oldState.Values)
|
||||
{
|
||||
if (!newState.NetComponents.Contains(change.NetID))
|
||||
oldState.Remove(change.NetID);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var newCompState in newState.ComponentChanges.Value)
|
||||
{
|
||||
if (!oldState.TryGetValue(newCompState.NetID, out var existing))
|
||||
{
|
||||
// This is a new component
|
||||
// I'm not 100% sure about this, but I think delta states should always be full states here?
|
||||
DebugTools.Assert(newCompState.State is not IComponentDeltaState newDelta);
|
||||
oldState[newCompState.NetID] = newCompState;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Modify or replace existing component
|
||||
if (newCompState.State is not IComponentDeltaState delta)
|
||||
{
|
||||
oldState[newCompState.NetID] = newCompState;
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(existing.State != null && existing.State is not IComponentDeltaState);
|
||||
oldState[newCompState.NetID] = new ComponentChange(existing.NetID, delta.CreateNewFullState(existing.State!), newCompState.LastModifiedTick);
|
||||
}
|
||||
}
|
||||
|
||||
private EntityState MergeStates(
|
||||
EntityState newState,
|
||||
IReadOnlyCollection<ComponentChange> oldState,
|
||||
@@ -384,20 +497,20 @@ public sealed partial class ReplayLoadManager
|
||||
if (!newCompStates.Remove(existing.NetID, out var newCompState))
|
||||
continue;
|
||||
|
||||
if (newCompState.State is not IComponentDeltaState delta || delta.FullState)
|
||||
if (newCompState.State is not IComponentDeltaState delta)
|
||||
{
|
||||
combined[index] = newCompState;
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(existing.State is IComponentDeltaState fullDelta && fullDelta.FullState);
|
||||
combined[index] = new ComponentChange(existing.NetID, delta.CreateNewFullState(existing.State), newCompState.LastModifiedTick);
|
||||
DebugTools.Assert(existing.State != null && existing.State is not IComponentDeltaState);
|
||||
combined[index] = new ComponentChange(existing.NetID, delta.CreateNewFullState(existing.State!), newCompState.LastModifiedTick);
|
||||
}
|
||||
|
||||
foreach (var compChange in newCompStates.Values)
|
||||
{
|
||||
// I'm not 100% sure about this, but I think delta states should always be full states here?
|
||||
DebugTools.Assert(compChange.State is not IComponentDeltaState delta || delta.FullState);
|
||||
DebugTools.Assert(compChange.State is not IComponentDeltaState);
|
||||
combined.Add(compChange);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed partial class ReplayLoadManager
|
||||
continue;
|
||||
|
||||
var state = _entMan.GetComponentState(_entMan.EventBus, component, null, GameTick.Zero);
|
||||
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
|
||||
DebugTools.Assert(state is not IComponentDeltaState);
|
||||
list.Add(new ComponentChange(netId, state, GameTick.Zero));
|
||||
set.Add(netId);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ public sealed partial class ReplayLoadManager
|
||||
{
|
||||
if (comp.NetID == _metaId)
|
||||
{
|
||||
var state = (MetaDataComponentState) comp.State;
|
||||
var state = (MetaDataComponentState) comp.State!;
|
||||
return state.PrototypeId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public sealed partial class ReplayLoadManager : IReplayLoadManager
|
||||
private ushort _metaId;
|
||||
private bool _initialized;
|
||||
private int _checkpointInterval;
|
||||
private int _checkpointMinInterval;
|
||||
private int _checkpointEntitySpawnThreshold;
|
||||
private int _checkpointEntityStateThreshold;
|
||||
private ISawmill _sawmill = default!;
|
||||
@@ -45,6 +46,7 @@ public sealed partial class ReplayLoadManager : IReplayLoadManager
|
||||
|
||||
_initialized = true;
|
||||
_confMan.OnValueChanged(CVars.CheckpointInterval, value => _checkpointInterval = value, true);
|
||||
_confMan.OnValueChanged(CVars.CheckpointMinInterval, value => _checkpointMinInterval = value, true);
|
||||
_confMan.OnValueChanged(CVars.CheckpointEntitySpawnThreshold, value => _checkpointEntitySpawnThreshold = value,
|
||||
true);
|
||||
_confMan.OnValueChanged(CVars.CheckpointEntityStateThreshold, value => _checkpointEntityStateThreshold = value,
|
||||
|
||||
@@ -17,7 +17,7 @@ internal sealed partial class ReplayPlaybackManager
|
||||
/// </summary>
|
||||
/// <param name="index">The target tick/index. The actual checkpoint will have an index less than or equal to this.</param>
|
||||
/// <param name="flushEntities">Whether to delete all entities</param>
|
||||
public void ResetToNearestCheckpoint(int index, bool flushEntities)
|
||||
public void ResetToNearestCheckpoint(int index, bool flushEntities, CheckpointState? checkpoint = null)
|
||||
{
|
||||
if (Replay == null)
|
||||
throw new Exception("Not currently playing a replay");
|
||||
@@ -25,7 +25,8 @@ internal sealed partial class ReplayPlaybackManager
|
||||
if (flushEntities)
|
||||
_entMan.FlushEntities();
|
||||
|
||||
var checkpoint = GetLastCheckpoint(Replay, index);
|
||||
// Look up the desired checkpoint, unless our caller kindly provided one to us.
|
||||
checkpoint ??= GetLastCheckpoint(Replay, index);
|
||||
|
||||
_sawmill.Info($"Resetting to checkpoint. From {Replay.CurrentIndex} to {checkpoint.Index}");
|
||||
var st = new Stopwatch();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Replays.Playback;
|
||||
@@ -27,27 +28,35 @@ internal sealed partial class ReplayPlaybackManager
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Playing &= !pausePlayback;
|
||||
value = Math.Clamp(value, 0, Replay.States.Count - 1);
|
||||
if (value == Replay.CurrentIndex)
|
||||
{
|
||||
ScrubbingTarget = null;
|
||||
return;
|
||||
}
|
||||
|
||||
BeforeSetTick?.Invoke();
|
||||
|
||||
// Begin timing replay processing so we can abort when we run out of time (_replayMaxScrubTime)
|
||||
var st = RStopwatch.StartNew();
|
||||
|
||||
bool skipEffectEvents = value > Replay.CurrentIndex + _visualEventThreshold;
|
||||
if (value < Replay.CurrentIndex)
|
||||
{
|
||||
skipEffectEvents = true;
|
||||
ResetToNearestCheckpoint(value, false);
|
||||
}
|
||||
else if (value > Replay.CurrentIndex + _checkpointInterval)
|
||||
else if (value > Replay.CurrentIndex + _checkpointMinInterval)
|
||||
{
|
||||
// If we are skipping many ticks into the future, we try to skip directly to a checkpoint instead of
|
||||
// applying every tick.
|
||||
var nextCheckpoint = GetNextCheckpoint(Replay, Replay.CurrentIndex);
|
||||
if (nextCheckpoint.Index < value)
|
||||
ResetToNearestCheckpoint(value, false);
|
||||
|
||||
var nextCheckpoint = GetLastCheckpoint(Replay, value);
|
||||
// Sanity-Check that the checkpoint is actually BEFORE the desired position.
|
||||
// Also check that it gets us closer to goal position than we already are.
|
||||
if (nextCheckpoint.Index <= value && nextCheckpoint.Index > Replay.CurrentIndex)
|
||||
ResetToNearestCheckpoint(value, false, nextCheckpoint);
|
||||
}
|
||||
|
||||
_entMan.EntitySysManager.GetEntitySystem<ClientDirtySystem>().Reset();
|
||||
@@ -75,6 +84,23 @@ internal sealed partial class ReplayPlaybackManager
|
||||
DebugTools.Assert(Replay.LastApplied >= state.FromSequence);
|
||||
DebugTools.Assert(Replay.LastApplied + 1 <= state.ToSequence);
|
||||
Replay.LastApplied = state.ToSequence;
|
||||
|
||||
if (st.Elapsed.TotalMilliseconds > _replayMaxScrubTime)
|
||||
{
|
||||
// Out of time to advance replay this tick
|
||||
// Note: We check at end of loop so we always advance at least 1 tick.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Use ScrubbingTarget to force a later invocation to continue moving towards the target tick
|
||||
if (Replay.CurrentIndex < value)
|
||||
{
|
||||
ScrubbingTarget = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScrubbingTarget = null;
|
||||
}
|
||||
|
||||
AfterSetTick?.Invoke();
|
||||
|
||||
@@ -51,7 +51,8 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
|
||||
|
||||
public ReplayData? Replay { get; private set; }
|
||||
public NetUserId? Recorder => Replay?.Recorder;
|
||||
private int _checkpointInterval;
|
||||
private int _checkpointMinInterval;
|
||||
private int _replayMaxScrubTime;
|
||||
private int _visualEventThreshold;
|
||||
public uint? AutoPauseCountdown { get; set; }
|
||||
public int? ScrubbingTarget { get; set; }
|
||||
@@ -93,7 +94,8 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
|
||||
_initialized = true;
|
||||
_sawmill = _logMan.GetSawmill("replay");
|
||||
_metaId = _factory.GetRegistration(typeof(MetaDataComponent)).NetID!.Value;
|
||||
_confMan.OnValueChanged(CVars.CheckpointInterval, (value) => _checkpointInterval = value, true);
|
||||
_confMan.OnValueChanged(CVars.CheckpointMinInterval, (value) => _checkpointMinInterval = value, true);
|
||||
_confMan.OnValueChanged(CVars.ReplayMaxScrubTime, (value) => _replayMaxScrubTime = value, true);
|
||||
_confMan.OnValueChanged(CVars.ReplaySkipThreshold, (value) => _visualEventThreshold = value, true);
|
||||
_client.RunLevelChanged += OnRunLevelChanged;
|
||||
}
|
||||
|
||||
@@ -567,7 +567,7 @@ namespace Robust.Client.UserInterface
|
||||
public Vector2i Position;
|
||||
public Color Modulate;
|
||||
public UIBox2i? ScissorBox;
|
||||
public ref Matrix3 CoordinateTransform;
|
||||
public ref Matrix3x2 CoordinateTransform;
|
||||
}
|
||||
|
||||
protected void RenderControl(ref ControlRenderArguments args, int childIndex, Vector2i position)
|
||||
|
||||
@@ -130,7 +130,7 @@ public sealed class TileSpawningUIController : UIController
|
||||
|
||||
_window.TileList.Clear();
|
||||
|
||||
IEnumerable<ITileDefinition> tileDefs = _tiles;
|
||||
IEnumerable<ITileDefinition> tileDefs = _tiles.Where(def => !def.EditorHidden);
|
||||
|
||||
if (!string.IsNullOrEmpty(searchStr))
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -20,19 +21,19 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
/// <summary>
|
||||
/// Base background colour.
|
||||
/// </summary>
|
||||
public Vector4 BaseColor;
|
||||
public Robust.Shared.Maths.Vector4 BaseColor;
|
||||
|
||||
/// <summary>
|
||||
/// Colour to add to the background colour along the X-axis.
|
||||
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
|
||||
/// </summary>
|
||||
public Vector4 XAxis;
|
||||
public Robust.Shared.Maths.Vector4 XAxis;
|
||||
|
||||
/// <summary>
|
||||
/// Colour to add to the background colour along the y-axis.
|
||||
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
|
||||
/// </summary>
|
||||
public Vector4 YAxis;
|
||||
public Robust.Shared.Maths.Vector4 YAxis;
|
||||
|
||||
/// <summary>
|
||||
/// If true, then <see cref="BaseColor"/>, <see cref="XAxis"/>, and <see cref="YAxis"/> will be interpreted as HSVa
|
||||
@@ -53,7 +54,7 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
var old = handle.GetShader();
|
||||
handle.UseShader(_shader);
|
||||
|
||||
var globalPixelPos = handle.GetTransform().Transform(default);
|
||||
var globalPixelPos = Vector2.Transform(default, handle.GetTransform());
|
||||
_shader.SetParameter("size", box.Size);
|
||||
_shader.SetParameter("offset", globalPixelPos);
|
||||
_shader.SetParameter("xAxis", XAxis);
|
||||
@@ -92,14 +93,14 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
{
|
||||
var colorData = Hsv
|
||||
? Color.ToHsv(color)
|
||||
: new Vector4(color.R, color.G, color.B, color.A);
|
||||
: new Robust.Shared.Maths.Vector4(color.R, color.G, color.B, color.A);
|
||||
SetBaseColor(colorData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that sets the base color by taking in some color and removing the components that are controlled by the x and y axes.
|
||||
/// </summary>
|
||||
public void SetBaseColor(Vector4 colorData)
|
||||
public void SetBaseColor(Robust.Shared.Maths.Vector4 colorData)
|
||||
{
|
||||
BaseColor = colorData - colorData * XAxis - colorData * YAxis;
|
||||
}
|
||||
|
||||
64
Robust.Client/UserInterface/Controls/EntityPrototypeView.cs
Normal file
64
Robust.Client/UserInterface/Controls/EntityPrototypeView.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
[Virtual]
|
||||
public class EntityPrototypeView : SpriteView
|
||||
{
|
||||
private string? _currentPrototype;
|
||||
private EntityUid? _ourEntity;
|
||||
|
||||
public EntityPrototypeView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EntityPrototypeView(EntProtoId? entProto, IEntityManager entMan) : base(entMan)
|
||||
{
|
||||
SetPrototype(entProto);
|
||||
}
|
||||
|
||||
public void SetPrototype(EntProtoId? entProto)
|
||||
{
|
||||
SpriteSystem ??= EntMan.System<SpriteSystem>();
|
||||
|
||||
if (entProto == _currentPrototype
|
||||
&& EntMan.TryGetComponent(Entity?.Owner, out MetaDataComponent? meta)
|
||||
&& meta.EntityPrototype?.ID == _currentPrototype)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentPrototype = entProto;
|
||||
SetEntity(null);
|
||||
if (_ourEntity != null)
|
||||
{
|
||||
EntMan.DeleteEntity(_ourEntity);
|
||||
}
|
||||
|
||||
if (_currentPrototype != null)
|
||||
{
|
||||
_ourEntity = EntMan.Spawn(_currentPrototype);
|
||||
SpriteSystem.ForceUpdate(_ourEntity.Value);
|
||||
SetEntity(_ourEntity);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
if (_currentPrototype != null)
|
||||
SetPrototype(_currentPrototype);
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
if (!EntMan.Deleted(_ourEntity))
|
||||
EntMan.QueueDeleteEntity(_ourEntity);
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
private TimeSpan? _lastClickTime;
|
||||
private Vector2? _lastClickPosition;
|
||||
|
||||
private bool IsPlaceHolderVisible => string.IsNullOrEmpty(_text) && _placeHolder != null;
|
||||
private bool IsPlaceHolderVisible => !(HidePlaceHolderOnFocus && HasKeyboardFocus()) && string.IsNullOrEmpty(_text) && _placeHolder != null;
|
||||
|
||||
public event Action<LineEditEventArgs>? OnTextChanged;
|
||||
public event Action<LineEditEventArgs>? OnTextEntered;
|
||||
@@ -186,6 +186,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public int SelectionLower => Math.Min(_selectionStart, _cursorPosition);
|
||||
public int SelectionUpper => Math.Max(_selectionStart, _cursorPosition);
|
||||
|
||||
public bool HidePlaceHolderOnFocus { get; set; }
|
||||
|
||||
public bool IgnoreNext { get; set; }
|
||||
|
||||
private (int start, int length)? _imeData;
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
[Virtual]
|
||||
public class SpriteView : Control
|
||||
{
|
||||
private SpriteSystem? _sprite;
|
||||
protected SpriteSystem? SpriteSystem;
|
||||
private SharedTransformSystem? _transform;
|
||||
private readonly IEntityManager _entMan;
|
||||
protected readonly IEntityManager EntMan;
|
||||
|
||||
[ViewVariables]
|
||||
public SpriteComponent? Sprite => Entity?.Comp1;
|
||||
@@ -120,20 +120,26 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
public SpriteView()
|
||||
{
|
||||
IoCManager.Resolve(ref _entMan);
|
||||
IoCManager.Resolve(ref EntMan);
|
||||
RectClipContent = true;
|
||||
}
|
||||
|
||||
public SpriteView(IEntityManager entMan)
|
||||
{
|
||||
EntMan = entMan;
|
||||
RectClipContent = true;
|
||||
}
|
||||
|
||||
public SpriteView(EntityUid? uid, IEntityManager entMan)
|
||||
{
|
||||
_entMan = entMan;
|
||||
EntMan = entMan;
|
||||
RectClipContent = true;
|
||||
SetEntity(uid);
|
||||
}
|
||||
|
||||
public SpriteView(NetEntity uid, IEntityManager entMan)
|
||||
{
|
||||
_entMan = entMan;
|
||||
EntMan = entMan;
|
||||
RectClipContent = true;
|
||||
SetEntity(uid);
|
||||
}
|
||||
@@ -154,8 +160,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
if (Entity?.Owner == uid)
|
||||
return;
|
||||
|
||||
if (!_entMan.TryGetComponent(uid, out SpriteComponent? sprite)
|
||||
|| !_entMan.TryGetComponent(uid, out TransformComponent? xform))
|
||||
if (!EntMan.TryGetComponent(uid, out SpriteComponent? sprite)
|
||||
|| !EntMan.TryGetComponent(uid, out TransformComponent? xform))
|
||||
{
|
||||
Entity = null;
|
||||
NetEnt = null;
|
||||
@@ -163,7 +169,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
|
||||
Entity = new(uid.Value, sprite, xform);
|
||||
NetEnt = _entMan.GetNetEntity(uid);
|
||||
NetEnt = EntMan.GetNetEntity(uid);
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
@@ -223,11 +229,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
if (!ResolveEntity(out var uid, out var sprite, out var xform))
|
||||
return;
|
||||
|
||||
_sprite ??= _entMan.System<SpriteSystem>();
|
||||
_transform ??= _entMan.System<TransformSystem>();
|
||||
SpriteSystem ??= EntMan.System<SpriteSystem>();
|
||||
_transform ??= EntMan.System<TransformSystem>();
|
||||
|
||||
// Ensure the sprite is animated despite possible not being visible in any viewport.
|
||||
_sprite.ForceUpdate(uid);
|
||||
SpriteSystem.ForceUpdate(uid);
|
||||
|
||||
var stretchVec = Stretch switch
|
||||
{
|
||||
@@ -258,13 +264,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
[NotNullWhen(true)] out SpriteComponent? sprite,
|
||||
[NotNullWhen(true)] out TransformComponent? xform)
|
||||
{
|
||||
if (NetEnt != null && Entity == null && _entMan.TryGetEntity(NetEnt, out var ent))
|
||||
if (NetEnt != null && Entity == null && EntMan.TryGetEntity(NetEnt, out var ent))
|
||||
SetEntity(ent);
|
||||
|
||||
if (Entity != null)
|
||||
{
|
||||
(uid, sprite, xform) = Entity.Value;
|
||||
return !_entMan.Deleted(uid);
|
||||
return !EntMan.Deleted(uid);
|
||||
}
|
||||
|
||||
sprite = null;
|
||||
|
||||
@@ -43,11 +43,11 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
/// <remarks>
|
||||
/// This is generally just be a combination of <see cref="IClydeViewport.GetWorldToLocalMatrix"/> and <see cref="GetLocalToScreenMatrix"/>
|
||||
/// </remarks>
|
||||
Matrix3 GetWorldToScreenMatrix();
|
||||
Matrix3x2 GetWorldToScreenMatrix();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a matrix that can be used to transform from view-port local to screen coordinates.
|
||||
/// </summary>
|
||||
Matrix3 GetLocalToScreenMatrix();
|
||||
Matrix3x2 GetLocalToScreenMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,17 +155,17 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
return WorldToLocalPixel(point) + GlobalPixelPosition;
|
||||
}
|
||||
|
||||
public Matrix3 GetWorldToScreenMatrix()
|
||||
public Matrix3x2 GetWorldToScreenMatrix()
|
||||
{
|
||||
if (Viewport == null)
|
||||
return Matrix3.Identity;
|
||||
return Matrix3x2.Identity;
|
||||
|
||||
return Viewport.GetWorldToLocalMatrix() * GetLocalToScreenMatrix();
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalToScreenMatrix()
|
||||
public Matrix3x2 GetLocalToScreenMatrix()
|
||||
{
|
||||
return Matrix3.CreateTransform(GlobalPixelPosition, 0, Vector2.One / _viewportResolution);
|
||||
return Matrix3Helpers.CreateTransform(GlobalPixelPosition, 0, Vector2.One / _viewportResolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Client.UserInterface
|
||||
return Task.FromResult<Stream?>(null);
|
||||
}
|
||||
|
||||
public Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null)
|
||||
public Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null, bool truncate = true)
|
||||
{
|
||||
return Task.FromResult<(Stream fileStream, bool alreadyExisted)?>(null);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Robust.Client.UserInterface
|
||||
return await OpenFileNfd(filters);
|
||||
}
|
||||
|
||||
public async Task<(Stream, bool)?> SaveFile(FileDialogFilters? filters)
|
||||
public async Task<(Stream, bool)?> SaveFile(FileDialogFilters? filters, bool truncate = true)
|
||||
{
|
||||
var name = await GetSaveFileName(filters);
|
||||
if (name == null)
|
||||
@@ -61,7 +61,7 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
try
|
||||
{
|
||||
return (File.Open(name, FileMode.Open), true);
|
||||
return (File.Open(name, truncate ? FileMode.Truncate : FileMode.Open), true);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Robust.Client.UserInterface
|
||||
/// The file stream the user chose to save to, and whether the file already existed.
|
||||
/// Null if the user cancelled the action.
|
||||
/// </returns>
|
||||
Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null);
|
||||
/// <param name="truncate">Should we truncate an existing file to 0-size then write or append.</param>
|
||||
Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null, bool truncate = true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
@@ -63,7 +63,7 @@ namespace Robust.Client.UserInterface
|
||||
Color GetMainClearColor();
|
||||
|
||||
void RenderControl(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
|
||||
UIBox2i? scissorBox, Matrix3 coordinateTransform);
|
||||
UIBox2i? scissorBox, Matrix3x2 coordinateTransform);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,8 +114,10 @@ internal partial class UserInterfaceManager
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
// Attempt to ensure that keybind-up events only get raised after a single keybind-down.
|
||||
DebugTools.Assert(!_focusedControls.ContainsKey(args.Function));
|
||||
// Attempt to ensure that keybind-up events get raised after a keybind-down.
|
||||
DebugTools.Assert(!_focusedControls.TryGetValue(args.Function, out var existing)
|
||||
|| !existing.VisibleInTree
|
||||
|| args.IsRepeat && existing == control);
|
||||
_focusedControls[args.Function] = control;
|
||||
|
||||
OnKeyBindDown?.Invoke(control);
|
||||
@@ -124,7 +126,7 @@ internal partial class UserInterfaceManager
|
||||
public void KeyBindUp(BoundKeyEventArgs args)
|
||||
{
|
||||
// Only raise keybind-up for the control on which we previously raised keybind-down
|
||||
if (!_focusedControls.Remove(args.Function, out var control) || control.Disposed)
|
||||
if (!_focusedControls.Remove(args.Function, out var control) || !control.VisibleInTree)
|
||||
return;
|
||||
|
||||
var guiArgs = new GUIBoundKeyEventArgs(args.Function, args.State, args.PointerLocation, args.CanFocus,
|
||||
|
||||
@@ -130,9 +130,9 @@ internal sealed partial class UserInterfaceManager
|
||||
{
|
||||
var total = 0;
|
||||
var drawingHandle = renderHandle.DrawingHandleScreen;
|
||||
drawingHandle.SetTransform(Matrix3.Identity);
|
||||
RenderControl(renderHandle, ref total, root, Vector2i.Zero, Color.White, null, Matrix3.Identity);
|
||||
drawingHandle.SetTransform(Matrix3.Identity);
|
||||
drawingHandle.SetTransform(Matrix3x2.Identity);
|
||||
RenderControl(renderHandle, ref total, root, Vector2i.Zero, Color.White, null, Matrix3x2.Identity);
|
||||
drawingHandle.SetTransform(Matrix3x2.Identity);
|
||||
OnPostDrawUIRoot?.Invoke(new PostDrawUIRootEventArgs(root, drawingHandle));
|
||||
|
||||
_prof.WriteValue("Controls rendered", ProfData.Int32(total));
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
|
||||
public void RenderControl(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
|
||||
UIBox2i? scissorBox, Matrix3 coordinateTransform)
|
||||
UIBox2i? scissorBox, Matrix3x2 coordinateTransform)
|
||||
{
|
||||
if (!control.Visible)
|
||||
{
|
||||
@@ -378,8 +378,7 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
var handle = renderHandle.DrawingHandleScreen;
|
||||
var oldXform = handle.GetTransform();
|
||||
var xform = oldXform;
|
||||
xform.Multiply(Matrix3.CreateTransform(position, Angle.Zero, Vector2.One));
|
||||
var xform = oldXform * Matrix3Helpers.CreateTransform(position, Angle.Zero, Vector2.One);
|
||||
handle.SetTransform(xform);
|
||||
modulate *= control.Modulate;
|
||||
|
||||
@@ -406,7 +405,7 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
foreach (var child in control.Children)
|
||||
{
|
||||
var pos = position + (Vector2i) coordinateTransform.Transform(child.PixelPosition);
|
||||
var pos = position + (Vector2i)Vector2.Transform(child.PixelPosition, coordinateTransform);
|
||||
control.RenderChildOverride(ref args, child.GetPositionInParent(), pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,22 +4,6 @@ namespace Robust.Client.Utility
|
||||
{
|
||||
internal static class OpenTKConversions
|
||||
{
|
||||
public static OpenToolkit.Mathematics.Matrix3 ConvertOpenTK(this Matrix3 matrix)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
M11 = matrix.R0C0,
|
||||
M12 = matrix.R0C1,
|
||||
M13 = matrix.R0C2,
|
||||
M21 = matrix.R1C0,
|
||||
M22 = matrix.R1C1,
|
||||
M23 = matrix.R1C2,
|
||||
M31 = matrix.R2C0,
|
||||
M32 = matrix.R2C1,
|
||||
M33 = matrix.R2C2
|
||||
};
|
||||
}
|
||||
|
||||
public static OpenToolkit.Mathematics.Color4 ConvertOpenTK(this Color color)
|
||||
{
|
||||
return new(color.R, color.G, color.B, color.A);
|
||||
|
||||
@@ -29,6 +29,8 @@ public static class Diagnostics
|
||||
public const string IdComponentPauseNoParentAttribute = "RA0023";
|
||||
public const string IdComponentPauseWrongTypeAttribute = "RA0024";
|
||||
public const string IdDependencyFieldAssigned = "RA0025";
|
||||
public const string IdUncachedRegex = "RA0026";
|
||||
public const string IdDataFieldRedundantTag = "RA0027";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
@@ -36,6 +34,7 @@ public class Generator : IIncrementalGenerator
|
||||
var (compilation, declarations) = source;
|
||||
var builder = new StringBuilder();
|
||||
var containingTypes = new Stack<INamedTypeSymbol>();
|
||||
var declarationsGenerated = new HashSet<string>();
|
||||
|
||||
foreach (var declaration in declarations)
|
||||
{
|
||||
@@ -44,6 +43,14 @@ public class Generator : IIncrementalGenerator
|
||||
|
||||
var type = compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)!;
|
||||
|
||||
var symbolName = type
|
||||
.ToDisplayString()
|
||||
.Replace('<', '{')
|
||||
.Replace('>', '}');
|
||||
|
||||
if (!declarationsGenerated.Add(symbolName))
|
||||
continue;
|
||||
|
||||
var nonPartial = !IsPartial(declaration);
|
||||
|
||||
var namespaceString = type.ContainingNamespace.IsGlobalNamespace
|
||||
@@ -107,11 +114,6 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
{{containingTypesEnd}}
|
||||
""");
|
||||
|
||||
var symbolName = type
|
||||
.ToDisplayString()
|
||||
.Replace('<', '{')
|
||||
.Replace('>', '}');
|
||||
|
||||
var sourceText = CSharpSyntaxTree
|
||||
.ParseText(builder.ToString())
|
||||
.GetRoot()
|
||||
|
||||
@@ -42,6 +42,17 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
component.Source = new DummyAudioSource();
|
||||
}
|
||||
|
||||
public override void SetGridAudio(Entity<AudioComponent>? entity)
|
||||
{
|
||||
if (entity == null)
|
||||
return;
|
||||
|
||||
base.SetGridAudio(entity);
|
||||
|
||||
// Need to global override so everyone can hear it.
|
||||
_pvs.AddGlobalOverride(entity.Value.Owner);
|
||||
}
|
||||
|
||||
public override void SetMapAudio(Entity<AudioComponent>? audio)
|
||||
{
|
||||
if (audio == null)
|
||||
@@ -81,11 +92,10 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null)
|
||||
{
|
||||
var entity = Spawn("Audio", MapCoordinates.Nullspace);
|
||||
var audio = SetupAudio(entity, filename, audioParams);
|
||||
AddAudioFilter(entity, audio, playerFilter);
|
||||
audio.Global = true;
|
||||
return (entity, audio);
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
AddAudioFilter(entity, entity.Comp, playerFilter);
|
||||
entity.Comp.Global = true;
|
||||
return (entity, entity.Comp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -95,16 +105,14 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(uid))
|
||||
{
|
||||
Log.Error($"Tried to play audio on a terminating / deleted entity {ToPrettyString(uid)}. Trace: {Environment.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var entity = Spawn("Audio", new EntityCoordinates(uid, Vector2.Zero));
|
||||
var audio = SetupAudio(entity, filename, audioParams);
|
||||
AddAudioFilter(entity, audio, playerFilter);
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
// Move it after setting it up
|
||||
XformSystem.SetCoordinates(entity, new EntityCoordinates(uid, Vector2.Zero));
|
||||
AddAudioFilter(entity, entity.Comp, playerFilter);
|
||||
|
||||
return (entity, audio);
|
||||
return (entity, entity.Comp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -114,15 +122,12 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(uid))
|
||||
{
|
||||
Log.Error($"Tried to play audio on a terminating / deleted entity {ToPrettyString(uid)}. Trace: {Environment.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var entity = Spawn("Audio", new EntityCoordinates(uid, Vector2.Zero));
|
||||
var audio = SetupAudio(entity, filename, audioParams);
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
XformSystem.SetCoordinates(entity, new EntityCoordinates(uid, Vector2.Zero));
|
||||
|
||||
return (entity, audio);
|
||||
return (entity, entity.Comp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -140,11 +145,11 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return null;
|
||||
|
||||
var entity = Spawn("Audio", coordinates);
|
||||
var audio = SetupAudio(entity, filename, audioParams);
|
||||
AddAudioFilter(entity, audio, playerFilter);
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
XformSystem.SetCoordinates(entity, coordinates);
|
||||
AddAudioFilter(entity, entity.Comp, playerFilter);
|
||||
|
||||
return (entity, audio);
|
||||
return (entity, entity.Comp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -163,10 +168,11 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return null;
|
||||
|
||||
var entity = Spawn("Audio", coordinates);
|
||||
var audio = SetupAudio(entity, filename, audioParams);
|
||||
// TODO: Transform TryFindGridAt mess + optimisation required.
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
XformSystem.SetCoordinates(entity, coordinates);
|
||||
|
||||
return (entity, audio);
|
||||
return (entity, entity.Comp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -844,7 +844,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
if (xformQuery.TryGetComponent(rootEntity, out var xform) && IsRoot(xform, mapQuery) && !HasComp<MapComponent>(rootEntity))
|
||||
{
|
||||
_transform.SetLocalPosition(xform, data.Options.TransformMatrix.Transform(xform.LocalPosition));
|
||||
_transform.SetLocalPosition(xform, Vector2.Transform(xform.LocalPosition, data.Options.TransformMatrix));
|
||||
xform.LocalRotation += data.Options.Rotation;
|
||||
}
|
||||
}
|
||||
@@ -856,7 +856,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
if (xformQuery.TryGetComponent(entity, out var xform) && IsRoot(xform, mapQuery))
|
||||
{
|
||||
// Don't want to trigger events
|
||||
xform._localPosition = data.Options.TransformMatrix.Transform(xform.LocalPosition);
|
||||
xform._localPosition = Vector2.Transform(xform.LocalPosition, data.Options.TransformMatrix);
|
||||
if (!xform.NoLocalRotation)
|
||||
xform._localRotation += data.Options.Rotation;
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
public sealed class ServerMetaDataSystem : MetaDataSystem
|
||||
{
|
||||
[Dependency] private readonly PvsSystem _pvsSystem = default!;
|
||||
private EntityQuery<MetaDataComponent> _mQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -18,7 +13,6 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
|
||||
|
||||
EntityManager.ComponentAdded += OnComponentAdded;
|
||||
EntityManager.ComponentRemoved += OnComponentRemoved;
|
||||
_mQuery = GetEntityQuery<MetaDataComponent>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<UserInterfaceUserComponent, ExpandPvsEvent>(OnBuiUserExpand);
|
||||
}
|
||||
|
||||
private void OnBuiUserExpand(Entity<UserInterfaceUserComponent> ent, ref ExpandPvsEvent args)
|
||||
{
|
||||
var buis = ent.Comp.OpenInterfaces.Keys;
|
||||
|
||||
if (buis.Count == 0)
|
||||
return;
|
||||
|
||||
args.Entities ??= new List<EntityUid>(buis.Count);
|
||||
|
||||
foreach (var ui in buis)
|
||||
{
|
||||
DebugTools.Assert(ent.Comp.OpenInterfaces[ui].Count > 0);
|
||||
DebugTools.Assert(HasComp<UserInterfaceComponent>(ui));
|
||||
args.Entities.Add(ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ internal sealed class PvsChunk
|
||||
/// <summary>
|
||||
/// The <see cref="Root"/>'s inverse world matrix.
|
||||
/// </summary>
|
||||
public Matrix3 InvWorldMatrix { get; set; }
|
||||
public Matrix3x2 InvWorldMatrix { get; set; }
|
||||
|
||||
// These are only used while populating the chunk. They aren't local variables because the chunks are pooled, so
|
||||
// the same chunk can be repopulated more than once.
|
||||
|
||||
@@ -51,10 +51,15 @@ internal sealed class PvsSession(ICommonSession session, ResizableMemoryRegion<P
|
||||
public (GameTick ToTick, List<PvsIndex> PreviouslySent)? LastSent;
|
||||
|
||||
/// <summary>
|
||||
/// Visible chunks, sorted by proximity to the clients's viewers;
|
||||
/// Visible chunks, sorted by proximity to the client's viewers.
|
||||
/// </summary>
|
||||
public readonly List<(PvsChunk Chunk, float ChebyshevDistance)> Chunks = new();
|
||||
|
||||
/// <summary>
|
||||
/// Unsorted set of visible chunks. Used to construct the <see cref="Chunks"/> list.
|
||||
/// </summary>
|
||||
public readonly HashSet<PvsChunk> ChunkSet = new();
|
||||
|
||||
/// <summary>
|
||||
/// Squared distance ta all of the visible chunks.
|
||||
/// </summary>
|
||||
@@ -117,6 +122,7 @@ internal sealed class PvsSession(ICommonSession session, ResizableMemoryRegion<P
|
||||
{
|
||||
PlayerStates.Clear();
|
||||
Chunks.Clear();
|
||||
ChunkSet.Clear();
|
||||
States.Clear();
|
||||
State = null;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ internal sealed partial class PvsSystem
|
||||
var xform = Transform(chunk.Root);
|
||||
DebugTools.AssertEqual(chunk.Map.Owner, xform.MapUid);
|
||||
chunk.InvWorldMatrix = xform.InvLocalMatrix;
|
||||
var worldPos = xform.LocalMatrix.Transform(chunk.Centre);
|
||||
var worldPos = Vector2.Transform(chunk.Centre, xform.LocalMatrix);
|
||||
chunk.Position = new(worldPos, xform.MapID);
|
||||
chunk.UpdateQueued = false;
|
||||
}
|
||||
@@ -90,15 +90,13 @@ internal sealed partial class PvsSystem
|
||||
foreach (var session in _sessions)
|
||||
{
|
||||
session.Chunks.Clear();
|
||||
session.ChunkSet.Clear();
|
||||
GetSessionViewers(session);
|
||||
|
||||
foreach (var eye in session.Viewers)
|
||||
{
|
||||
GetVisibleChunks(eye, session.Chunks);
|
||||
GetVisibleChunks(eye, session.ChunkSet);
|
||||
}
|
||||
|
||||
// The list of visible chunks should be unique.
|
||||
DebugTools.Assert(session.Chunks.Select(x => x.Chunk).ToHashSet().Count == session.Chunks.Count);
|
||||
}
|
||||
DebugTools.Assert(_dirtyChunks.ToHashSet().Count == _dirtyChunks.Count);
|
||||
DebugTools.Assert(_cleanChunks.ToHashSet().Count == _cleanChunks.Count);
|
||||
@@ -108,7 +106,7 @@ internal sealed partial class PvsSystem
|
||||
/// Get the chunks visible to a single entity and add them to a player's set of visible chunks.
|
||||
/// </summary>
|
||||
private void GetVisibleChunks(Entity<TransformComponent, EyeComponent?> eye,
|
||||
List<(PvsChunk Chunk, float ChebyshevDistance)> playerChunks)
|
||||
HashSet<PvsChunk> chunks)
|
||||
{
|
||||
var (viewPos, range, mapUid) = CalcViewBounds(eye);
|
||||
if (mapUid is not {} map)
|
||||
@@ -121,7 +119,7 @@ internal sealed partial class PvsSystem
|
||||
if (!_chunks.TryGetValue(loc, out var chunk))
|
||||
continue;
|
||||
|
||||
playerChunks.Add((chunk, default));
|
||||
chunks.Add(chunk);
|
||||
if (chunk.UpdateQueued)
|
||||
continue;
|
||||
|
||||
@@ -139,7 +137,7 @@ internal sealed partial class PvsSystem
|
||||
|
||||
foreach (var (grid, _) in _grids)
|
||||
{
|
||||
var localPos = _transform.GetInvWorldMatrix(grid).Transform(viewPos);
|
||||
var localPos = Vector2.Transform(viewPos, _transform.GetInvWorldMatrix(grid));
|
||||
var gridChunkEnumerator = new ChunkIndicesEnumerator(localPos, range, ChunkSize);
|
||||
while (gridChunkEnumerator.MoveNext(out var gridChunkIndices))
|
||||
{
|
||||
@@ -147,7 +145,7 @@ internal sealed partial class PvsSystem
|
||||
if (!_chunks.TryGetValue(loc, out var chunk))
|
||||
continue;
|
||||
|
||||
playerChunks.Add((chunk, default));
|
||||
chunks.Add(chunk);
|
||||
if (chunk.UpdateQueued)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -50,9 +50,11 @@ internal sealed partial class PvsSystem
|
||||
continue;
|
||||
|
||||
var state = EntityManager.GetComponentState(bus, component, player, fromTick);
|
||||
DebugTools.Assert(fromTick > component.CreationTick || state is not IComponentDeltaState delta || delta.FullState);
|
||||
changed.Add(new ComponentChange(netId, state, component.LastModifiedTick));
|
||||
|
||||
if (state != null)
|
||||
DebugTools.Assert(fromTick > component.CreationTick || state is not IComponentDeltaState);
|
||||
|
||||
if (sendCompList)
|
||||
netComps!.Add(netId);
|
||||
}
|
||||
@@ -85,7 +87,7 @@ internal sealed partial class PvsSystem
|
||||
continue;
|
||||
|
||||
var state = EntityManager.GetComponentState(bus, component, player, GameTick.Zero);
|
||||
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
|
||||
DebugTools.Assert(state is not IComponentDeltaState);
|
||||
changed.Add(new ComponentChange(netId, state, component.LastModifiedTick));
|
||||
netComps.Add(netId);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,13 @@ internal sealed partial class PvsSystem
|
||||
/// </summary>
|
||||
private bool RecursivelyAddOverride(PvsSession session, EntityUid uid, GameTick fromTick, bool addChildren)
|
||||
{
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
if (!_xformQuery.TryGetComponent(uid, out var xform))
|
||||
{
|
||||
// Can happen if systems add deleted entities to PVS move event.
|
||||
Log.Error($"Attempted to add non-existent entity {uid} to PVS override for session {session.Session}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var parent = xform.ParentUid;
|
||||
|
||||
// First we process all parents. This is because while this entity may already have been added
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -42,7 +43,7 @@ internal sealed partial class PvsSystem
|
||||
|
||||
// PVS benchmarks use dummy sessions.
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (session.Channel != null)
|
||||
if (session.Channel is not DummyChannel)
|
||||
{
|
||||
_netMan.ServerSendMessage(msg, session.Channel);
|
||||
if (msg.ShouldSendReliably())
|
||||
@@ -137,15 +138,19 @@ internal sealed partial class PvsSystem
|
||||
if (!CullingEnabled || session.DisableCulling)
|
||||
return;
|
||||
|
||||
var chunkSet = session.ChunkSet;
|
||||
var chunks = session.Chunks;
|
||||
var distances = session.ChunkDistanceSq;
|
||||
|
||||
DebugTools.AssertEqual(chunks.Count, 0);
|
||||
|
||||
distances.Clear();
|
||||
distances.EnsureCapacity(chunks.Count);
|
||||
distances.EnsureCapacity(chunkSet.Count);
|
||||
chunks.EnsureCapacity(chunkSet.Count);
|
||||
|
||||
// Assemble list of chunks and their distances to the nearest eye.
|
||||
foreach (ref var tuple in CollectionsMarshal.AsSpan(chunks))
|
||||
foreach(var chunk in chunkSet)
|
||||
{
|
||||
var chunk = tuple.Chunk;
|
||||
var dist = float.MaxValue;
|
||||
var chebDist = float.MaxValue;
|
||||
|
||||
@@ -159,13 +164,13 @@ internal sealed partial class PvsSystem
|
||||
|
||||
dist = Math.Min(dist, (pos.Position - chunk.Position.Position).LengthSquared());
|
||||
|
||||
var relative = chunk.InvWorldMatrix.Transform(pos.Position) - chunk.Centre;
|
||||
var relative = Vector2.Transform(pos.Position, chunk.InvWorldMatrix) - chunk.Centre;
|
||||
relative = Vector2.Abs(relative);
|
||||
chebDist = Math.Min(chebDist, Math.Max(relative.X, relative.Y));
|
||||
}
|
||||
|
||||
distances.Add(dist);
|
||||
tuple.ChebyshevDistance = chebDist;
|
||||
chunks.Add((chunk, chebDist));
|
||||
}
|
||||
|
||||
// Sort chunks based on distances
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Server.Maps
|
||||
get => _offset;
|
||||
set
|
||||
{
|
||||
TransformMatrix = Matrix3.CreateTransform(value, Rotation);
|
||||
TransformMatrix = Matrix3Helpers.CreateTransform(value, Rotation);
|
||||
_offset = value;
|
||||
}
|
||||
}
|
||||
@@ -37,14 +37,14 @@ namespace Robust.Server.Maps
|
||||
get => _rotation;
|
||||
set
|
||||
{
|
||||
TransformMatrix = Matrix3.CreateTransform(Offset, value);
|
||||
TransformMatrix = Matrix3Helpers.CreateTransform(Offset, value);
|
||||
_rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Angle _rotation = Angle.Zero;
|
||||
|
||||
public Matrix3 TransformMatrix { get; set; } = Matrix3.Identity;
|
||||
public Matrix3x2 TransformMatrix { get; set; } = Matrix3x2.Identity;
|
||||
|
||||
/// <summary>
|
||||
/// If there is a map entity serialized should we also load it.
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed partial class GridFixtureSystem
|
||||
TransformComponent? xformA = null,
|
||||
TransformComponent? xformB = null)
|
||||
{
|
||||
var matrix = Matrix3.CreateTransform(offset, rotation);
|
||||
var matrix = Matrix3Helpers.CreateTransform(offset, rotation);
|
||||
Merge(gridAUid, gridBUid, matrix, gridA, gridB, xformA, xformB);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed partial class GridFixtureSystem
|
||||
public void Merge(
|
||||
EntityUid gridAUid,
|
||||
EntityUid gridBUid,
|
||||
Matrix3 matrix,
|
||||
Matrix3x2 matrix,
|
||||
MapGridComponent? gridA = null,
|
||||
MapGridComponent? gridB = null,
|
||||
TransformComponent? xformA = null,
|
||||
@@ -63,7 +63,7 @@ public sealed partial class GridFixtureSystem
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
var offsetTile = matrix.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector);
|
||||
var offsetTile = Vector2.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector, matrix);
|
||||
tiles.Add((offsetTile.Floored(), tileRef.Value.Tile));
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public sealed partial class GridFixtureSystem
|
||||
if (snapgrid == null || snapgrid.Count == 0)
|
||||
continue;
|
||||
|
||||
var offsetTile = matrix.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector);
|
||||
var offsetTile = Vector2.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector, matrix);
|
||||
var tileIndex = offsetTile.Floored();
|
||||
|
||||
for (var j = snapgrid.Count - 1; j >= 0; j--)
|
||||
@@ -124,7 +124,7 @@ public sealed partial class GridFixtureSystem
|
||||
if (entXform.ParentUid != gridBUid ||
|
||||
!bounds.Contains(entXform.LocalPosition)) continue;
|
||||
|
||||
var newPos = matrix.Transform(entXform.LocalPosition);
|
||||
var newPos = Vector2.Transform(entXform.LocalPosition, matrix);
|
||||
|
||||
_xformSystem.SetCoordinates(ent, entXform, new EntityCoordinates(gridAUid, newPos), entXform.LocalRotation + rotationDiff, oldParent: xformB, newParent: xformA);
|
||||
}
|
||||
|
||||
@@ -172,16 +172,10 @@ namespace Robust.Server.Placement
|
||||
}
|
||||
}
|
||||
|
||||
var created = _entityManager.SpawnEntity(entityTemplateName, coordinates);
|
||||
var created = _entityManager.SpawnAttachedTo(entityTemplateName, coordinates, rotation: dirRcv.ToAngle());
|
||||
|
||||
var placementCreateEvent = new PlacementEntityEvent(created, coordinates, PlacementEventAction.Create, msg.MsgChannel.UserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementCreateEvent);
|
||||
|
||||
// Some entities immediately delete themselves
|
||||
if (_entityManager.EntityExists(created))
|
||||
{
|
||||
_entityManager.GetComponent<TransformComponent>(created).LocalRotation = dirRcv.ToAngle();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -90,18 +90,18 @@ namespace Robust.Server.Player
|
||||
_cfg.SyncConnectingClient(args.Channel);
|
||||
}
|
||||
|
||||
private void EndSession(object? sender, NetChannelArgs args)
|
||||
{
|
||||
EndSession(args.Channel.UserId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends a clients session, and disconnects them.
|
||||
/// </summary>
|
||||
private void EndSession(object? sender, NetChannelArgs args)
|
||||
internal void EndSession(NetUserId user)
|
||||
{
|
||||
if (!TryGetSessionByChannel(args.Channel, out var session))
|
||||
{
|
||||
if (!TryGetSessionById(user, out var session))
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure nothing got messed up during the life of the session
|
||||
DebugTools.Assert(session.Channel == args.Channel);
|
||||
|
||||
SetStatus(session, SessionStatus.Disconnected);
|
||||
SetAttachedEntity(session, null, out _, true);
|
||||
@@ -159,5 +159,32 @@ namespace Robust.Server.Player
|
||||
session = actor.PlayerSession;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal ICommonSession AddDummySession(NetUserId user, string name)
|
||||
{
|
||||
#if FULL_RELEASE
|
||||
// Lets not make it completely trivial to fake player counts.
|
||||
throw new NotSupportedException();
|
||||
#endif
|
||||
Lock.EnterWriteLock();
|
||||
DummySession session;
|
||||
try
|
||||
{
|
||||
UserIdMap[name] = user;
|
||||
if (!PlayerData.TryGetValue(user, out var data))
|
||||
PlayerData[user] = data = new(user, name);
|
||||
|
||||
session = new DummySession(user, name, data);
|
||||
InternalSessions.Add(user, session);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Lock.ExitWriteLock();
|
||||
}
|
||||
|
||||
UpdateState(session);
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<Content Include="server_config.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="run_server.bat">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<EmbeddedResource Include="ExtraMappedSerializerStrings.txt">
|
||||
<LogicalName>Robust.Server.ExtraMappedSerializerStrings.txt</LogicalName>
|
||||
</EmbeddedResource>
|
||||
|
||||
@@ -312,14 +312,14 @@ namespace Robust.Server.ServerStatus
|
||||
}
|
||||
|
||||
// Only call this if the download URL is not available!
|
||||
private async Task<AczManifestInfo?> PrepareAcz()
|
||||
private async Task<AczManifestInfo?> PrepareAcz(bool optional = false)
|
||||
{
|
||||
// Take the ACZ lock asynchronously
|
||||
await _aczLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
// Setting this now ensures that it won't fail repeatedly on exceptions/etc.
|
||||
if (_aczPrepareAttempted)
|
||||
if (_aczPrepareAttempted || optional)
|
||||
return _aczPrepared;
|
||||
|
||||
_aczPrepareAttempted = true;
|
||||
|
||||
@@ -80,8 +80,7 @@ namespace Robust.Server.ServerStatus
|
||||
if (string.IsNullOrEmpty(downloadUrl))
|
||||
{
|
||||
var query = HttpUtility.ParseQueryString(context.Url.Query);
|
||||
var optional = query.Keys;
|
||||
buildInfo = await PrepareACZBuildInfo();
|
||||
buildInfo = await PrepareACZBuildInfo(optional: query.Get("can_skip_build") == "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -129,9 +128,9 @@ namespace Robust.Server.ServerStatus
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<JsonObject?> PrepareACZBuildInfo()
|
||||
private async Task<JsonObject?> PrepareACZBuildInfo(bool optional)
|
||||
{
|
||||
var acm = await PrepareAcz();
|
||||
var acm = await PrepareAcz(optional);
|
||||
if (acm == null) return null;
|
||||
|
||||
// Fork ID is an interesting case, we don't want to cause too many redownloads but we also don't want to pollute disk.
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Robust.Server.ServerStatus
|
||||
if (auth != _watchdogToken)
|
||||
{
|
||||
// Holy shit nobody read these logs please.
|
||||
_sawmill.Info(@"Failed auth: ""{0}"" vs ""{1}""", auth, _watchdogToken);
|
||||
_sawmill.Verbose(@"Failed auth: ""{0}"" vs ""{1}""", auth, _watchdogToken);
|
||||
await context.RespondErrorAsync(HttpStatusCode.Unauthorized);
|
||||
return true;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
if (auth != _watchdogToken)
|
||||
{
|
||||
_sawmill.Warning(
|
||||
_sawmill.Verbose(
|
||||
"received POST /shutdown with invalid authentication token. Ignoring {0}, {1}", auth,
|
||||
_watchdogToken);
|
||||
await context.RespondErrorAsync(HttpStatusCode.Unauthorized);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user