mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a654a6cf43 | ||
|
|
6211cf2e03 | ||
|
|
a522b4cf86 | ||
|
|
6d33be8c0f | ||
|
|
cb1d4ae843 | ||
|
|
039b70f502 | ||
|
|
7892cc895f | ||
|
|
77108284b8 | ||
|
|
5e21dbdd7f | ||
|
|
8274623edb | ||
|
|
e923d69083 | ||
|
|
6e8ab5ce78 | ||
|
|
f905ea631b | ||
|
|
be54c41891 | ||
|
|
33184ecfa5 | ||
|
|
11cf0c1703 | ||
|
|
528544b7a2 | ||
|
|
8571d7e7b5 | ||
|
|
0f06423b7a | ||
|
|
eb9e0ffefc | ||
|
|
903619ecef | ||
|
|
879c6ea538 | ||
|
|
5478545aeb | ||
|
|
650929dcbb | ||
|
|
a289659b49 | ||
|
|
85d15c21e1 | ||
|
|
bcd1566440 | ||
|
|
749ac2c364 | ||
|
|
5eed3bc281 | ||
|
|
d78f378493 | ||
|
|
eef44c15cf | ||
|
|
3d1b2418f9 | ||
|
|
6b49a86ee5 | ||
|
|
cd13cd3cd8 | ||
|
|
2b8d8d6636 | ||
|
|
409fe1a125 | ||
|
|
ab5db4641c | ||
|
|
064e8ee365 | ||
|
|
02dcff7eae | ||
|
|
e1e5f8de54 | ||
|
|
d5ba822a79 | ||
|
|
f448c6b8fa | ||
|
|
5e1d80be35 | ||
|
|
01546f32da | ||
|
|
aeeaaaefc5 | ||
|
|
b6c8060af1 | ||
|
|
99685838da | ||
|
|
8917b29255 | ||
|
|
f0c4d7c5eb | ||
|
|
6a00c62d3c | ||
|
|
fc3116fca5 | ||
|
|
98c1397b3a | ||
|
|
2464bb6c2f | ||
|
|
709142acee | ||
|
|
af4e3e5e1c | ||
|
|
d51a18c6ea | ||
|
|
d5c3d4c0c9 | ||
|
|
a4474d8df8 | ||
|
|
d66f7c7c06 | ||
|
|
b6879869d6 | ||
|
|
815b8e0c48 | ||
|
|
ef4e3baa7f | ||
|
|
270ddb5a53 | ||
|
|
6133fe0808 | ||
|
|
909fd326a0 | ||
|
|
876de4065a | ||
|
|
60e159f0d0 | ||
|
|
80f3aae30c | ||
|
|
98b1862433 | ||
|
|
d2311c193f | ||
|
|
f05ed96461 | ||
|
|
dc23dfaf4d | ||
|
|
62315f7c2e | ||
|
|
b2d121e780 | ||
|
|
fb4b029122 | ||
|
|
66239d23ea | ||
|
|
dbb45f1c13 | ||
|
|
6284e16b64 | ||
|
|
f6c55085fe | ||
|
|
30eafd26e7 | ||
|
|
63423d96b4 | ||
|
|
474334aff2 | ||
|
|
be102f86bf | ||
|
|
d7962c7190 | ||
|
|
7fe9385c3b | ||
|
|
a9d9d1348a | ||
|
|
4eaf624555 | ||
|
|
e37c131fb4 | ||
|
|
9df4606492 | ||
|
|
3be8070274 | ||
|
|
8a440d705f | ||
|
|
5849474022 | ||
|
|
b7d67c0ece | ||
|
|
b04cf71bc0 | ||
|
|
ea87df649a | ||
|
|
dab7a9112f | ||
|
|
d3dc89832a | ||
|
|
3ade9ca447 | ||
|
|
149e9a2613 | ||
|
|
d164148ce2 | ||
|
|
d898b52449 | ||
|
|
dcf7a1e580 | ||
|
|
65c6bb74eb | ||
|
|
d6d88bea91 | ||
|
|
d6467f768a |
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
@@ -16,3 +16,8 @@
|
||||
|
||||
# Be they Fluent translations or Freemarker templates, I know them both!
|
||||
*.ftl @RemieRichards
|
||||
|
||||
# commands commands commands commands
|
||||
**/Toolshed/** @moonheart08
|
||||
*Command.cs @moonheart08
|
||||
*Commands.cs @moonheart08
|
||||
|
||||
4
.github/workflows/build-docfx.yml
vendored
4
.github/workflows/build-docfx.yml
vendored
@@ -7,12 +7,12 @@ jobs:
|
||||
docfx:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/build-test.yml
vendored
4
.github/workflows/build-test.yml
vendored
@@ -15,12 +15,12 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Install dependencies
|
||||
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -35,12 +35,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/publish-client.yml
vendored
4
.github/workflows/publish-client.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
||||
$ver = [regex]::Match($env:GITHUB_REF, "refs/tags/v?(.+)").Groups[1].Value
|
||||
echo ("::set-output name=version::{0}" -f $ver)
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/test-content.yml
vendored
4
.github/workflows/test-content.yml
vendored
@@ -13,13 +13,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out content
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
repository: space-wizards/space-station-14
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Disable submodule autoupdate
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: 78aa82cef0...f19cea8010
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -25,4 +25,7 @@
|
||||
|
||||
<!-- analyzer -->
|
||||
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- serialization generator -->
|
||||
<Import Project="Robust.Serialization.Generator.targets" />
|
||||
</Project>
|
||||
|
||||
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
255
RELEASE-NOTES.md
255
RELEASE-NOTES.md
@@ -54,6 +54,257 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 156.0.2
|
||||
|
||||
|
||||
## 156.0.1
|
||||
|
||||
|
||||
## 156.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Revert container changes from 155.0.0.
|
||||
|
||||
|
||||
## 155.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* MapInitEvent now gets raised for components that get added to entities that have already been map-initialized.
|
||||
|
||||
### New features
|
||||
|
||||
* VirtualWritableDirProvider now supports file renaming/moving.
|
||||
* Added a new command for toggling the replay UI (`replay_toggleui`).
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed formatting of localization file errors.
|
||||
* Directed event subscriptions will no longer error if the corresponding component is queued for deletion.
|
||||
|
||||
|
||||
## 154.2.0
|
||||
|
||||
|
||||
|
||||
### New features
|
||||
|
||||
* Added support for advertising to multiple hubs simultaneously.
|
||||
* Added new functions to ContainerSystem that recursively look for a component on a contained entity's parents.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix Direction.TurnCw/TurnCcw to South returning Invalid.
|
||||
|
||||
|
||||
## 154.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add MathHelper.Max for TimeSpans.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Make joint initialisation only log under IsFirstTimePredicted on client.
|
||||
|
||||
### Other
|
||||
|
||||
* Mark the proxy Dirty(component) as obsolete in line with EntityManager (Dirty(EntityUid, Component) should be used in its place).
|
||||
|
||||
|
||||
## 154.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Change ignored prototypes to skip prototypes even if the prototype type is found.
|
||||
* Moved IPlayerData interface to shared.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a multiline text submit keybind function.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed multiline edits scrollbar margins.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added more event sources.
|
||||
* Made Toolshed types oneOff IoC injections.
|
||||
|
||||
|
||||
## 153.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed SharedUserInterfaceComponent component references.
|
||||
* Removed EntityDeletedMessage.
|
||||
|
||||
### Other
|
||||
|
||||
* Performance improvements for replay recording.
|
||||
* Lidgren has been updated to [v0.2.6](https://github.com/space-wizards/SpaceWizards.Lidgren.Network/blob/v0.2.6/RELEASE-NOTES.md).
|
||||
* Make EntityManager.AddComponent with a component instance set the owner if its default, add system proxy for it.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added some `EventSource` providers for PVS and replay recording: `Robust.Pvs` and `Robust.ReplayRecording`.
|
||||
* Added RecursiveMoveBenchmark.
|
||||
* Removed redundant prototype resolving.
|
||||
* Removed CollisionWake component removal subscription.
|
||||
* Removed redundant DebugTools.AssertNotNull(netId) in ClientGameStateManager
|
||||
|
||||
|
||||
## 152.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `Robust.Server.GameObjects.BoundUserInterface.InteractionRangeSqrd` is now a get-only property. Modify `InteractionRange` instead if you want to change it on active UIs.
|
||||
* Remove IContainerManager.
|
||||
* Remove and obsolete ComponentExt methods.
|
||||
* Remove EntityStarted and ComponentDeleted C# events.
|
||||
* Convert Tile.TypeId to an int. Old maps that were saved with TypeId being an ushort will still be properly deserialized.
|
||||
|
||||
### New features
|
||||
|
||||
* `BoundUserInterfaceCheckRangeEvent` can be used to implement custom logic for BUI range checks.
|
||||
* Add support for long values in CVars.
|
||||
* Allow user code to implement own logic for bound user interface range checks.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix timers counting down slower than real time and drifting.
|
||||
* Add missing System using statement to generated component states.
|
||||
* Fix build with USE_SYSTEM_SQLITE.
|
||||
* Fix prototype manager not being initialized in robust server simulation tests.
|
||||
* Fix not running serialization hooks when copying non-byref data definition fields without a custom type serializer.
|
||||
|
||||
### Other
|
||||
|
||||
* Remove warning for glibc 2.37.
|
||||
* Remove personally-identifiable file paths from client logs.
|
||||
|
||||
### Internal
|
||||
|
||||
* Disable obsoletion and inherited member hidden warnings in serialization source generated code.
|
||||
* Update CI workflows to use setup-dotnet 3.2.0 and checkout 3.6.0.
|
||||
* Fix entity spawn tests having instance per test lifecycle with a non static OneTimeTearDown method.
|
||||
* Add new PVS test to check that there is no issue with entity states referencing other entities that the client is not yet aware of.
|
||||
|
||||
|
||||
## 151.0.0
|
||||
|
||||
|
||||
## 150.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix some partial datadefs.
|
||||
|
||||
|
||||
## 150.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove the Id field from Fixtures as the Id is already stored on FixturesComponent.
|
||||
|
||||
### New features
|
||||
|
||||
* Add AbstractDictionarySerializer for abstract classes.
|
||||
* Add many new spawn functions for entities for common operations.
|
||||
|
||||
|
||||
## 149.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix serialization sharing instances when copying data definitions and not assigning null when the source is null.
|
||||
* Fixed resizing a window to be bigger than its set maxsize crashing the client.
|
||||
|
||||
|
||||
## 149.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Data definitions must now be partial, their data fields must not be readonly and their data field properties must have a setter.
|
||||
|
||||
### Internal
|
||||
|
||||
* Copying data definitions through the serialization manager is now faster and consumes less memory.
|
||||
|
||||
|
||||
## 148.4.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add recursive PVS overrides and remove IsOverride()
|
||||
|
||||
|
||||
## 148.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Happy eyeballs delay can be configured.
|
||||
* Added more colors.
|
||||
* Allow pre-startup components to be shut down.
|
||||
* Added tile texture reload command.
|
||||
* Add implementation of Random.Pick(ValueList<T> ..).
|
||||
* Add IntegrationInstance fields for common dependencies.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Prevent invalid prototypes from being spawned.
|
||||
* Change default value of EntityLastModifiedTick from zero to one.
|
||||
* Make DiscordRichPresence icon CVars server-side with replication.
|
||||
|
||||
|
||||
## 148.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* `SpinBox.LineEditControl` exposes the underlying `LineEdit`.
|
||||
* Add VV attributes to various fields across overlay and sessions.
|
||||
* Add IsPaused to EntityManager to check if an entity is paused.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix SetActiveTheme not updating the theme.
|
||||
|
||||
|
||||
## 148.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added IgnoreUIChecksComponent that lets entities ignore bound user interface range checks which would normally close the UI.
|
||||
* Add support for F16-F24 keybinds.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix gamestate bug where PVS is disabled.
|
||||
|
||||
### Other
|
||||
|
||||
* EntityQuery.HasComponent override for nullable entity uids.
|
||||
|
||||
|
||||
## 148.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Several NuGet dependencies are now private assets.
|
||||
* Added `IViewportControl.PixelToMap()` and `PixelToMapEvent`. These are variants of the existing screen-to-map functions that should account for distortion effects.
|
||||
|
||||
### New features
|
||||
|
||||
* Added several new rich-text tags, including italic and bold-italic.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed log messages for unknown components not working due to threaded IoC issues.
|
||||
* Replay recordings no longer record invalid prototype uploads.
|
||||
|
||||
|
||||
## 147.0.0
|
||||
|
||||
### Breaking changes
|
||||
@@ -204,7 +455,7 @@ END TEMPLATE-->
|
||||
* `IHttpClientHolder` holds a shared `HttpClient` for use by content. It has Happy Eyeballs fixed and an appropriate `User-Agent`.
|
||||
* Added `DataNode.ToString()`. Makes it easier to save yaml files and debug code.
|
||||
* Added some cvars to modify discord rich presence icons.
|
||||
* .ogg files now read the `Artist` and `Title` tags and make them available via new fields in `AudioStream`.
|
||||
* .ogg files now read the `Artist` and `Title` tags and make them available via new fields in `AudioStream`.
|
||||
* The default fragment shaders now have access to the local light level (`lowp vec3 lightSample`).
|
||||
* Added `IPrototypeManager.ValidateAllPrototypesSerializable()`, which can be used to check that all currently loaded prototypes can be serialised & deserialised.
|
||||
|
||||
@@ -213,7 +464,7 @@ END TEMPLATE-->
|
||||
* Fix certain debug commands and tools crashing on non-SS14 RobustToolbox games due to a missing font.
|
||||
* Discord rich presence strings are now truncated if they are too long.
|
||||
* Fixed a couple of broadphase/entity-lookup update bugs that were affecting containers and entities attached to other (non-grid/map) entities.
|
||||
* Fixed `INetChannel.Disconnect()` not properly disconnecting clients in integration tests.
|
||||
* Fixed `INetChannel.Disconnect()` not properly disconnecting clients in integration tests.
|
||||
|
||||
### Other
|
||||
|
||||
|
||||
@@ -558,3 +558,6 @@ cmd-vfs_ls-help = Usage: vfs_list <path>
|
||||
|
||||
cmd-vfs_ls-err-args = Need exactly 1 argument.
|
||||
cmd-vfs_ls-hint-path = <path>
|
||||
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
|
||||
cmd-reloadtiletextures-help = Usage: reloadtiletextures
|
||||
|
||||
@@ -18,6 +18,15 @@ input-key-F12 = F12
|
||||
input-key-F13 = F13
|
||||
input-key-F14 = F14
|
||||
input-key-F15 = F15
|
||||
input-key-F16 = F16
|
||||
input-key-F17 = F17
|
||||
input-key-F18 = F18
|
||||
input-key-F19 = F19
|
||||
input-key-F20 = F20
|
||||
input-key-F21 = F21
|
||||
input-key-F22 = F22
|
||||
input-key-F23 = F23
|
||||
input-key-F24 = F24
|
||||
input-key-Pause = Pause
|
||||
input-key-Left = Left
|
||||
input-key-Up = Up
|
||||
|
||||
@@ -161,9 +161,263 @@ command-description-EqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are equal.
|
||||
command-description-NotEqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are not equal.
|
||||
|
||||
command-description-append =
|
||||
Appends a value to the input enumerable.
|
||||
command-description-DefaultIfNullCommand =
|
||||
Replaces the input with the type's default value if it is null, albeit only for value types (not objects).
|
||||
command-description-OrValueCommand =
|
||||
If the input is null, uses the provided alternate value.
|
||||
command-description-DebugPrintCommand =
|
||||
Prints the given value transparently, for debug prints in a command run.
|
||||
command-description-i =
|
||||
Integer constant.
|
||||
command-description-f =
|
||||
Float constant.
|
||||
command-description-s =
|
||||
String constant.
|
||||
command-description-b =
|
||||
Bool constant.
|
||||
command-description-join =
|
||||
Joins two sequences together into one sequence.
|
||||
command-description-reduce =
|
||||
Given a block to use as a reducer, turns a sequence into a single value.
|
||||
The left hand side of the block is implied, and the right hand is stored in $value.
|
||||
command-description-rep =
|
||||
Repeats the input value N times to form a sequence.
|
||||
command-description-take =
|
||||
Takes N values from the input sequence
|
||||
command-description-spawn-at =
|
||||
Spawns an entity at the given coordinates.
|
||||
command-description-spawn-on =
|
||||
Spawns an entity on the given entity, at it's coordinates.
|
||||
command-description-spawn-attached =
|
||||
Spawns an entity attached to the given entity, at (0 0) relative to it.
|
||||
command-description-mappos =
|
||||
Returns an entity's coordinates relative to it's current map.
|
||||
command-description-pos =
|
||||
Returns an entity's coordinates.
|
||||
command-description-tp-coords =
|
||||
Teleports the target to the given coordinates.
|
||||
command-description-tp-to =
|
||||
Teleports the target to the given other entity.
|
||||
command-description-tp-into =
|
||||
Teleports the target "into" the given other entity, attaching it at (0 0) relative to it.
|
||||
command-description-comp-get =
|
||||
Gets the given component from the given entity.
|
||||
command-description-comp-add =
|
||||
Adds the given component to the given entity.
|
||||
command-description-comp-ensure =
|
||||
Ensures the given entity has the given component.
|
||||
command-description-comp-has =
|
||||
Check if the given entity has the given component.
|
||||
command-description-AddVecCommand =
|
||||
Adds a scalar (single value) to every element in the input.
|
||||
command-description-SubVecCommand =
|
||||
Subtracts a scalar (single value) from every element in the input.
|
||||
command-description-MulVecCommand =
|
||||
Multiplies a scalar (single value) by every element in the input.
|
||||
command-description-DivVecCommand =
|
||||
Divides every element in the input by a scalar (single value).
|
||||
command-description-rng-to =
|
||||
Returns a number from its input to its argument (i.e. n..m inclusive)
|
||||
command-description-rng-from =
|
||||
Returns a number to its input from its argument (i.e. m..n inclusive)
|
||||
command-description-rng-prob =
|
||||
Returns a boolean based on the input probability/chance (from 0 to 1)
|
||||
command-description-sum =
|
||||
Computes the sum of the input.
|
||||
command-description-bin =
|
||||
"Bins" the input, counting up how many times each unique element occurs.
|
||||
command-description-extremes =
|
||||
Returns the two extreme ends of a list, interwoven.
|
||||
command-description-sortby =
|
||||
Sorts the input least to greatest by the computed key.
|
||||
command-description-sortmapby =
|
||||
Sorts the input least to greatest by the computed key, replacing the value with it's computed key afterward.
|
||||
command-description-sort =
|
||||
Sorts the input least to greatest.
|
||||
command-description-sortdownby =
|
||||
Sorts the input greatest to least by the computed key.
|
||||
command-description-sortmapdownby =
|
||||
Sorts the input greatest to least by the computed key, replacing the value with it's computed key afterward.
|
||||
command-description-sortdown =
|
||||
Sorts the input greatest to least.
|
||||
command-description-iota =
|
||||
Returns a list of numbers 1 to N.
|
||||
command-description-to =
|
||||
Returns a list of numbers N to M.
|
||||
command-description-curtick =
|
||||
The current game tick.
|
||||
command-description-curtime =
|
||||
The current game time (a TimeSpan)
|
||||
command-description-realtime =
|
||||
The current realtime since startup (a TimeSpan)
|
||||
command-description-servertime =
|
||||
The current server game time, or zero if we are the server (a TimeSpan)
|
||||
command-description-replace =
|
||||
Replaces the input entities with the given prototype, preserving position and rotation (but nothing else)
|
||||
command-description-allcomps =
|
||||
Returns all components on the given entity.
|
||||
command-description-entitysystemupdateorder-tick =
|
||||
Lists the tick update order of entity systems.
|
||||
|
||||
command-description-entitysystemupdateorder-frame =
|
||||
Lists the frame update order of entity systems.
|
||||
command-description-more =
|
||||
Prints the contents of $more, i.e. any extras that Toolshed didn't print from the last command.
|
||||
command-description-ModulusCommand =
|
||||
Computes the modulus of two values.
|
||||
This is usually remainder, check C#'s documentation for the type.
|
||||
command-description-ModVecCommand =
|
||||
Performs the modulus operation over the input with the given constant right-hand value.
|
||||
command-description-BitAndNotCommand =
|
||||
Performs bitwise AND-NOT over the input.
|
||||
command-description-BitOrNotCommand =
|
||||
Performs bitwise OR-NOT over the input.
|
||||
command-description-BitXnorCommand =
|
||||
Performs bitwise XNOR over the input.
|
||||
command-description-BitNotCommand =
|
||||
Performs bitwise NOT on the input.
|
||||
command-description-abs =
|
||||
Computes the absolute value of the input (removing the sign)
|
||||
command-description-average =
|
||||
Computes the average (arithmetic mean) of the input.
|
||||
command-description-bibytecount =
|
||||
Returns the size of the input in bytes, given that the input implements IBinaryInteger.
|
||||
This is NOT sizeof.
|
||||
command-description-shortestbitlength =
|
||||
Returns the minimum number of bits needed to represent the input value.
|
||||
command-description-countleadzeros =
|
||||
Counts the number of leading binary zeros in the input value.
|
||||
command-description-counttrailingzeros =
|
||||
Counts the number of trailing binary zeros in the input value.
|
||||
command-description-fpi =
|
||||
pi (3.14159...) as a float.
|
||||
command-description-fe =
|
||||
e (2.71828...) as a float.
|
||||
command-description-ftau =
|
||||
tau (6.28318...) as a float.
|
||||
command-description-fepsilon =
|
||||
The epsilon value for a float, exactly 1.4e-45.
|
||||
command-description-dpi =
|
||||
pi (3.14159...) as a double.
|
||||
command-description-de =
|
||||
e (2.71828...) as a double.
|
||||
command-description-dtau =
|
||||
tau (6.28318...) as a double.
|
||||
command-description-depsilon =
|
||||
The epsilon value for a double, exactly 4.9406564584124654E-324.
|
||||
command-description-hpi =
|
||||
pi (3.14...) as a half.
|
||||
command-description-he =
|
||||
e (2.71...) as a half.
|
||||
command-description-htau =
|
||||
tau (6.28...) as a half.
|
||||
command-description-hepsilon =
|
||||
The epsilon value for a half, exactly 5.9604645E-08.
|
||||
command-description-floor =
|
||||
Returns the floor of the input value (rounding toward zero).
|
||||
command-description-ceil =
|
||||
Returns the ceil of the input value (rounding away from zero).
|
||||
command-description-round =
|
||||
Rounds the input value.
|
||||
command-description-trunc =
|
||||
Truncates the input value.
|
||||
command-description-round2frac =
|
||||
Rounds the input value to the specified number of fractional digits.
|
||||
command-description-exponentbytecount =
|
||||
Returns the number of bytes required to store the exponent.
|
||||
command-description-significandbytecount =
|
||||
Returns the number of bytes required to store the significand.
|
||||
command-description-significandbitcount =
|
||||
Returns the exact bit length of the significand.
|
||||
command-description-exponentshortestbitcount =
|
||||
Returns the minimum number of bits to store the exponent.
|
||||
command-description-stepnext =
|
||||
Steps to the next float value, adding one to the significand with carry.
|
||||
command-description-stepprev =
|
||||
Steps to the previous float value, subtracting one from the significand with carry.
|
||||
command-description-checkedto =
|
||||
Converts from the input numeric type to the target, erroring if not possible.
|
||||
command-description-saturateto =
|
||||
Converts from the input numeric type to the target, saturating if the value is out of range.
|
||||
For example, converting 382 to a byte would saturate to 255 (the maximum value of a byte).
|
||||
command-description-truncto =
|
||||
Converts from the input numeric type to the target, with truncation.
|
||||
In the case of integers, this is a bit cast with sign extension.
|
||||
command-description-iscanonical =
|
||||
Returns whether the input is in canonical form.
|
||||
command-description-iscomplex =
|
||||
Returns whether the input is a complex number (by value, not by type)
|
||||
command-description-iseven =
|
||||
Returns whether the input is even.
|
||||
Not a javascript package.
|
||||
command-description-isodd =
|
||||
Returns whether the input is odd.
|
||||
command-description-isfinite =
|
||||
Returns whether the input is finite.
|
||||
command-description-isimaginary =
|
||||
Returns whether the input is purely imaginary (no real part).
|
||||
command-description-isinfinite =
|
||||
Returns whether the input is infinite.
|
||||
command-description-isinteger =
|
||||
Returns whether the input is an integer (by value, not by type)
|
||||
command-description-isnan =
|
||||
Returns whether the input is Not a Number (NaN).
|
||||
This is a special floating point value, so this is by value, not by type.
|
||||
command-description-isnegative =
|
||||
Returns whether the input is negative.
|
||||
command-description-ispositive =
|
||||
Returns whether the input is positive.
|
||||
command-description-isreal =
|
||||
Returns whether the input is purely real (no imaginary part).
|
||||
command-description-issubnormal =
|
||||
Returns whether the input is in sub-normal form.
|
||||
command-description-iszero =
|
||||
Returns whether the input is zero.
|
||||
command-description-pow =
|
||||
Computes the power of its lefthand to its righthand. x^y.
|
||||
command-description-sqrt =
|
||||
Computes the square root of its input.
|
||||
command-description-cbrt =
|
||||
Computes the cube root of its input.
|
||||
command-description-root =
|
||||
Computes the Nth root of its input.
|
||||
command-description-hypot =
|
||||
Computes the hypotenuse of a triangle with the given sides A and B.
|
||||
command-description-sin =
|
||||
Computes the sine of the input.
|
||||
command-description-sinpi =
|
||||
Computes the sine of the input multiplied by pi.
|
||||
command-description-asin =
|
||||
Computes the arcsine of the input.
|
||||
command-description-asinpi =
|
||||
Computes the arcsine of the input multiplied by pi.
|
||||
command-description-cos =
|
||||
Computes the cosine of the input.
|
||||
command-description-cospi =
|
||||
Computes the cosine of the input multiplied by pi.
|
||||
command-description-acos =
|
||||
Computes the arcosine of the input.
|
||||
command-description-acospi =
|
||||
Computes the arcosine of the input multiplied by pi.
|
||||
command-description-tan =
|
||||
Computes the tangent of the input.
|
||||
command-description-tanpi =
|
||||
Computes the tangent of the input multiplied by pi.
|
||||
command-description-atan =
|
||||
Computes the arctangent of the input.
|
||||
command-description-atanpi =
|
||||
Computes the arctangent of the input multiplied by pi.
|
||||
command-description-iterate =
|
||||
Iterates the given function over the input N times, returning a list of results.
|
||||
Think of this like successively applying the function to a value, tracking all the intermediate values.
|
||||
command-description-pick =
|
||||
Picks a random value from the input.
|
||||
command-description-tee =
|
||||
Tees the input into the given block, ignoring the block's result.
|
||||
This essentially lets you have a branch in your code to do multiple operations on one value.
|
||||
command-description-cmd-info =
|
||||
Returns a CommandSpec for the given command.
|
||||
On it's own, this means it'll print the comamnd's help message.
|
||||
command-description-comp-rm =
|
||||
Removes the given component from the entity.
|
||||
|
||||
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
||||
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
||||
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
|
||||
Diagnostics.IdDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} is a DataDefinition but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type that is a data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
|
||||
Diagnostics.IdNestedDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} contains nested data definition {1} but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type containing a nested data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
|
||||
Diagnostics.IdDataFieldWritable,
|
||||
"Data field must not be readonly",
|
||||
"Data field {0} in data definition {1} is readonly.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to remove the readonly modifier."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
|
||||
Diagnostics.IdDataFieldPropertyWritable,
|
||||
"Data field property must have a setter",
|
||||
"Data field property {0} in data definition {1} does not have a setter.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to add a setter."
|
||||
);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
|
||||
);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.ClassDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.StructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordStructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.InterfaceDeclaration);
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataField, SyntaxKind.FieldDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataFieldProperty, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
private void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not TypeDeclarationSyntax declaration)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(declaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
if (!IsPartial(declaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataDefinitionPartialRule, declaration.Keyword.GetLocation(), type.Name));
|
||||
}
|
||||
|
||||
var containingType = type.ContainingType;
|
||||
while (containingType != null)
|
||||
{
|
||||
var containingTypeDeclaration = (TypeDeclarationSyntax) containingType.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
if (!IsPartial(containingTypeDeclaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(NestedDataDefinitionPartialRule, containingTypeDeclaration.Keyword.GetLocation(), containingType.Name, type.Name));
|
||||
}
|
||||
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not FieldDeclarationSyntax field)
|
||||
return;
|
||||
|
||||
var typeDeclaration = field.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
foreach (var variable in field.Declaration.Variables)
|
||||
{
|
||||
var fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
|
||||
if (fieldSymbol == null)
|
||||
continue;
|
||||
|
||||
if (IsReadOnlyDataField(type, fieldSymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not PropertyDeclarationSyntax property)
|
||||
return;
|
||||
|
||||
var typeDeclaration = property.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type) || type.IsRecord || type.IsValueType)
|
||||
return;
|
||||
|
||||
var propertySymbol = context.SemanticModel.GetDeclaredSymbol(property);
|
||||
if (propertySymbol == null)
|
||||
return;
|
||||
|
||||
if (IsReadOnlyDataField(type, propertySymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
|
||||
{
|
||||
if (!IsDataField(field, out _, out _))
|
||||
return false;
|
||||
|
||||
return IsReadOnlyMember(type, field);
|
||||
}
|
||||
|
||||
private static bool IsPartial(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.Modifiers.IndexOf(SyntaxKind.PartialKeyword) != -1;
|
||||
}
|
||||
|
||||
private static bool IsDataDefinition(ITypeSymbol? type)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
return HasAttribute(type, DataDefinitionNamespace) ||
|
||||
IsImplicitDataDefinition(type);
|
||||
}
|
||||
|
||||
private static bool IsDataField(ISymbol member, out ITypeSymbol type, out AttributeData attribute)
|
||||
{
|
||||
// TODO data records and other attributes
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
foreach (var attr in field.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = field.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
foreach (var attr in property.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = property.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = null!;
|
||||
attribute = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool Inherits(ITypeSymbol type, string parent)
|
||||
{
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (baseType.ToDisplayString() == parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
|
||||
{
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
return field.IsReadOnly;
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
if (property.SetMethod == null)
|
||||
return true;
|
||||
|
||||
if (property.SetMethod.IsInitOnly)
|
||||
return type.IsReferenceType;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasAttribute(ITypeSymbol type, string attributeName)
|
||||
{
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (HasAttribute(baseType, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var @interface in type.AllInterfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinitionInterface(ITypeSymbol @interface)
|
||||
{
|
||||
if (HasAttribute(@interface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var subInterface in @interface.AllInterfaces)
|
||||
{
|
||||
if (HasAttribute(subInterface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
yield return baseType;
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
#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;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
||||
using static Robust.Analyzers.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public sealed class DefinitionFixer : CodeFixProvider
|
||||
{
|
||||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
|
||||
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
|
||||
);
|
||||
|
||||
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
foreach (var diagnostic in context.Diagnostics)
|
||||
{
|
||||
switch (diagnostic.Id)
|
||||
{
|
||||
case IdDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdNestedDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdDataFieldWritable:
|
||||
return RegisterDataFieldFix(context, diagnostic);
|
||||
case IdDataFieldPropertyWritable:
|
||||
return RegisterDataFieldPropertyFix(context, diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
private static async Task RegisterPartialTypeFix(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<TypeDeclarationSyntax>().First();
|
||||
|
||||
if (token == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make type partial",
|
||||
c => MakeDataDefinitionPartial(context.Document, token, c),
|
||||
"Make type partial"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeDataDefinitionPartial(Document document, TypeDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = SyntaxFactory.Token(PartialKeyword);
|
||||
var newDeclaration = declaration.AddModifiers(token);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var field = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (field == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakeFieldWritable(context.Document, field, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldPropertyFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var property = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<PropertyDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakePropertyWritable(context.Document, property, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeFieldWritable(Document document, FieldDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = declaration.Modifiers.First(t => t.IsKind(ReadOnlyKeyword));
|
||||
var newDeclaration = declaration.WithModifiers(declaration.Modifiers.Remove(token));
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakePropertyWritable(Document document, PropertyDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var newDeclaration = declaration;
|
||||
var privateSet = newDeclaration
|
||||
.AccessorList?
|
||||
.Accessors
|
||||
.FirstOrDefault(s => s.IsKind(SetAccessorDeclaration) || s.IsKind(InitAccessorDeclaration));
|
||||
|
||||
if (newDeclaration.AccessorList != null && privateSet != null)
|
||||
{
|
||||
newDeclaration = newDeclaration.WithAccessorList(
|
||||
newDeclaration.AccessorList.WithAccessors(
|
||||
newDeclaration.AccessorList.Accessors.Remove(privateSet)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
AccessorDeclarationSyntax setter;
|
||||
if (declaration.Modifiers.Any(m => m.IsKind(PrivateKeyword)))
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
SyntaxFactory.TokenList(SyntaxFactory.Token(PrivateKeyword)),
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
|
||||
newDeclaration = newDeclaration.AddAccessorListAccessors(setter);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ public static class Diagnostics
|
||||
public const string IdValueEventSubscribedByRef = "RA0014";
|
||||
public const string IdByRefEventRaisedByValue = "RA0015";
|
||||
public const string IdValueEventRaisedByRef = "RA0016";
|
||||
public const string IdDataDefinitionPartial = "RA0017";
|
||||
public const string IdNestedDataDefinitionPartial = "RA0018";
|
||||
public const string IdDataFieldWritable = "RA0019";
|
||||
public const string IdDataFieldPropertyWritable = "RA0020";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class AddRemoveComponentBenchmark
|
||||
public partial class AddRemoveComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -48,7 +48,7 @@ public class AddRemoveComponentBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
public class ComponentIteratorBenchmark
|
||||
public partial class ComponentIteratorBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -69,7 +69,7 @@ public class ComponentIteratorBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Analyzers;
|
||||
@@ -9,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class GetComponentBenchmark
|
||||
public partial class GetComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -55,7 +54,7 @@ public class GetComponentBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class SpawnDeleteEntityBenchmark
|
||||
public partial class SpawnDeleteEntityBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -56,7 +56,7 @@ public class SpawnDeleteEntityBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
[Virtual]
|
||||
public class DataDefinitionWithString
|
||||
public partial class DataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed class SealedDataDefinitionWithString
|
||||
public sealed partial class SealedDataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; private set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -10,8 +11,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
/// Arbitrarily large data definition for benchmarks.
|
||||
/// Taken from content.
|
||||
/// </summary>
|
||||
[Prototype("seed")]
|
||||
public sealed class SeedDataDefinition : IPrototype
|
||||
public sealed partial class SeedDataDefinition : Component
|
||||
{
|
||||
public const string Prototype = @"
|
||||
- type: seed
|
||||
@@ -106,7 +106,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public struct SeedChemQuantity
|
||||
public partial struct SeedChemQuantity
|
||||
{
|
||||
[DataField("Min")]
|
||||
public int Min;
|
||||
|
||||
171
Robust.Benchmarks/Transform/RecursiveMoveBenchmark.cs
Normal file
171
Robust.Benchmarks/Transform/RecursiveMoveBenchmark.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Transform;
|
||||
|
||||
/// <summary>
|
||||
/// This benchmark tests various transform/move related functions with an entity that has many children.
|
||||
/// </summary>
|
||||
[Virtual, MemoryDiagnoser]
|
||||
public class RecursiveMoveBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private SharedTransformSystem _transform = default!;
|
||||
private ContainerSystem _container = default!;
|
||||
private PvsSystem _pvs = default!;
|
||||
private EntityCoordinates _mapCoords;
|
||||
private EntityCoordinates _gridCoords;
|
||||
private EntityUid _ent;
|
||||
private EntityUid _child;
|
||||
private TransformComponent _childXform = default!;
|
||||
private EntityQuery<TransformComponent> _query;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.InitializeInstance();
|
||||
|
||||
if (!_simulation.Resolve<IConfigurationManager>().GetCVar(CVars.NetPVS))
|
||||
throw new InvalidOperationException("PVS must be enabled");
|
||||
|
||||
_entMan = _simulation.Resolve<IEntityManager>();
|
||||
_transform = _entMan.System<SharedTransformSystem>();
|
||||
_container = _entMan.System<ContainerSystem>();
|
||||
_pvs = _entMan.System<PvsSystem>();
|
||||
_query = _entMan.GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Create map & grid
|
||||
var mapMan = _simulation.Resolve<IMapManager>();
|
||||
var mapSys = _entMan.System<SharedMapSystem>();
|
||||
var mapId = mapMan.CreateMap();
|
||||
var map = mapMan.GetMapEntityId(mapId);
|
||||
var gridComp = mapMan.CreateGrid(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
_mapCoords = new EntityCoordinates(map, 100, 100);
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
|
||||
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
|
||||
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
|
||||
_ent = _entMan.Spawn();
|
||||
|
||||
// Quick check that SetCoordinates actually changes the parent as expected
|
||||
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
// Add 5 direct children in slots to represent clothing.
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var id = $"inventory{i}";
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, id);
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// body parts
|
||||
_container.EnsureContainer<Container>(_ent, "body");
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
// Simple organ
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// body part that has another body part / limb
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
|
||||
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Backpack
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// Misc backpack contents.
|
||||
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Emergency box inside of the backpack
|
||||
var box = backpackStorage.ContainedEntities.First();
|
||||
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Deepest child.
|
||||
_child = boxContainer.ContainedEntities.First();
|
||||
_childXform = _query.GetComponent(_child);
|
||||
|
||||
_pvs.ProcessCollections();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This implicitly measures move events, including PVS and entity lookups. Though given that most of the entities
|
||||
/// are in containers, this will bias the entity lookup aspect.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void MoveEntity()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Like <see cref="MoveEntity"/>, but also processes queued PVS chunk updates.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void MoveAndUpdateChunks()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_pvs.ProcessCollections();
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
_pvs.ProcessCollections();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public Vector2 GetWorldPos()
|
||||
{
|
||||
return _transform.GetWorldPosition(_childXform);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public EntityUid GetRootUid()
|
||||
{
|
||||
var xform = _childXform;
|
||||
while (xform.ParentUid.IsValid())
|
||||
{
|
||||
xform = _query.GetComponent(xform.ParentUid);
|
||||
}
|
||||
return xform.ParentUid;
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ namespace Robust.Client
|
||||
deps.Register<IComponentFactory, ComponentFactory>();
|
||||
deps.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<ClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<GameController, GameController>();
|
||||
deps.Register<IGameController, GameController>();
|
||||
deps.Register<IGameControllerInternal, GameController>();
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Physics;
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
public sealed partial class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
{
|
||||
public DynamicTree<ComponentTreeEntry<PointLightComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Physics;
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
public sealed partial class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
{
|
||||
public DynamicTree<ComponentTreeEntry<SpriteComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -630,7 +630,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mousePos = _eye.ScreenToMap(_input.MouseScreenPosition);
|
||||
var mousePos = _eye.PixelToMap(_input.MouseScreenPosition);
|
||||
|
||||
if (!_map.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
|
||||
var mouseSpot = _inputManager.MouseScreenPosition;
|
||||
var spot = _eyeManager.ScreenToMap(mouseSpot);
|
||||
var spot = _eyeManager.PixelToMap(mouseSpot);
|
||||
|
||||
if (!_mapManager.TryFindGridAt(spot, out var gridUid, out var grid))
|
||||
{
|
||||
|
||||
@@ -371,7 +371,7 @@ namespace Robust.Client.Debugging
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.ShapeInfo) != 0x0)
|
||||
{
|
||||
var hoverBodies = new List<PhysicsComponent>();
|
||||
var bounds = Box2.UnitCentered.Translated(_eyeManager.ScreenToMap(mousePos.Position).Position);
|
||||
var bounds = Box2.UnitCentered.Translated(_eyeManager.PixelToMap(mousePos.Position).Position);
|
||||
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, bounds))
|
||||
{
|
||||
@@ -404,7 +404,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Distance) != 0x0)
|
||||
{
|
||||
var mapPos = _eyeManager.ScreenToMap(mousePos);
|
||||
var mapPos = _eyeManager.PixelToMap(mousePos);
|
||||
|
||||
if (mapPos.MapId != args.MapId)
|
||||
return;
|
||||
|
||||
@@ -167,7 +167,6 @@ namespace Robust.Client
|
||||
_reflectionManager.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDefaultPrototypes();
|
||||
_prototypeManager.ResolveResults();
|
||||
_userInterfaceManager.Initialize();
|
||||
_eyeManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Prometheus;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Replays;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Plays back <see cref="Animation"/>s on entities.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class AnimationPlayerComponent : Component
|
||||
public sealed partial class AnimationPlayerComponent : Component
|
||||
{
|
||||
// TODO: Give this component a friend someday. Way too much content shit to change atm ._.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects;
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(GenericVisualizerSystem))]
|
||||
public sealed class GenericVisualizerComponent : Component
|
||||
public sealed partial class GenericVisualizerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a nested dictionary that maps appearance data keys -> sprite layer keys -> appearance data values -> layer data.
|
||||
|
||||
@@ -10,7 +10,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedEyeComponent))]
|
||||
public sealed class EyeComponent : SharedEyeComponent
|
||||
public sealed partial class EyeComponent : SharedEyeComponent
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Robust.Client.GameObjects;
|
||||
/// updated.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed class IconComponent : Component
|
||||
public sealed partial class IconComponent : Component
|
||||
{
|
||||
[IncludeDataField]
|
||||
public readonly SpriteSpecifier.Rsi Icon = default!;
|
||||
public SpriteSpecifier.Rsi Icon = default!;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Defines data fields used in the <see cref="InputSystem"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class InputComponent : Component
|
||||
public sealed partial class InputComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that will be made active for a client that attaches to this entity.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedPointLightComponent))]
|
||||
public sealed class PointLightComponent : SharedPointLightComponent, IComponentTreeEntry<PointLightComponent>
|
||||
public sealed partial class PointLightComponent : SharedPointLightComponent, IComponentTreeEntry<PointLightComponent>
|
||||
{
|
||||
public EntityUid? TreeUid { get; set; }
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public interface IRenderableComponent : IComponent
|
||||
public partial interface IRenderableComponent : IComponent
|
||||
{
|
||||
int DrawDepth { get; set; }
|
||||
float Bottom { get; }
|
||||
|
||||
@@ -32,7 +32,7 @@ using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteS
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class SpriteComponent : Component, IComponentDebug, ISerializationHooks, IComponentTreeEntry<SpriteComponent>, IAnimationProperties
|
||||
public sealed partial class SpriteComponent : Component, IComponentDebug, ISerializationHooks, IComponentTreeEntry<SpriteComponent>, IAnimationProperties
|
||||
{
|
||||
[Dependency] private readonly IResourceCache resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager prototypes = default!;
|
||||
|
||||
@@ -6,8 +6,8 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
public sealed class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
[RegisterComponent]
|
||||
public sealed partial class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
internal readonly Dictionary<Enum, PrototypeData> _interfaces = new();
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
while (chunkEnumerator.MoveNext(out var chunk))
|
||||
{
|
||||
foreach (var fixture in chunk.Fixtures)
|
||||
foreach (var fixture in chunk.Fixtures.Values)
|
||||
{
|
||||
var poly = (PolygonShape) fixture.Shape;
|
||||
|
||||
|
||||
@@ -206,8 +206,10 @@ namespace Robust.Client.GameObjects
|
||||
SetEntityContextActive(_inputManager, controlled);
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
protected override void PostInject()
|
||||
{
|
||||
base.PostInject();
|
||||
|
||||
_sawmillInputContext = _logManager.GetSawmill("input.context");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,7 +500,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (netId, comp) in netComps.Value)
|
||||
{
|
||||
DebugTools.AssertNotNull(netId);
|
||||
if (!comp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Robust.Client.Graphics
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
private ISawmill _logMill = default!;
|
||||
|
||||
// We default to this when we get set to a null eye.
|
||||
private readonly FixedEye _defaultEye = new();
|
||||
@@ -53,6 +54,7 @@ namespace Robust.Client.Graphics
|
||||
void IEyeManager.Initialize()
|
||||
{
|
||||
MainViewport = _uiManager.MainViewport;
|
||||
_logMill = IoCManager.Resolve<ILogManager>().RootSawmill;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -129,14 +131,14 @@ namespace Robust.Client.Graphics
|
||||
/// <inheritdoc />
|
||||
public ScreenCoordinates CoordinatesToScreen(EntityCoordinates point)
|
||||
{
|
||||
return MapToScreen(point.ToMap(_entityManager));
|
||||
return MapToScreen(point.ToMap(_entityManager, _entityManager.System<SharedTransformSystem>()));
|
||||
}
|
||||
|
||||
public ScreenCoordinates MapToScreen(MapCoordinates point)
|
||||
{
|
||||
if (CurrentEye.Position.MapId != point.MapId)
|
||||
{
|
||||
Logger.Error($"Attempted to convert map coordinates ({point}) to screen coordinates with an eye on another map ({CurrentEye.Position.MapId})");
|
||||
_logMill.Error($"Attempted to convert map coordinates ({point}) to screen coordinates with an eye on another map ({CurrentEye.Position.MapId})");
|
||||
return new(default, WindowId.Invalid);
|
||||
}
|
||||
|
||||
@@ -146,12 +148,10 @@ namespace Robust.Client.Graphics
|
||||
/// <inheritdoc />
|
||||
public MapCoordinates ScreenToMap(ScreenCoordinates point)
|
||||
{
|
||||
var (pos, window) = point;
|
||||
|
||||
if (_uiManager.MouseGetControl(point) is not IViewportControl viewport)
|
||||
return default;
|
||||
|
||||
return viewport.ScreenToMap(pos);
|
||||
return viewport.ScreenToMap(point.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -159,6 +159,21 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
return MainViewport.ScreenToMap(point);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapCoordinates PixelToMap(ScreenCoordinates point)
|
||||
{
|
||||
if (_uiManager.MouseGetControl(point) is not IViewportControl viewport)
|
||||
return default;
|
||||
|
||||
return viewport.PixelToMap(point.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapCoordinates PixelToMap(Vector2 point)
|
||||
{
|
||||
return MainViewport.PixelToMap(point);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CurrentEyeChangedEvent : EntityEventArgs
|
||||
|
||||
@@ -79,6 +79,16 @@ namespace Robust.Client.Graphics
|
||||
/// <returns>Corresponding point in the world.</returns>
|
||||
MapCoordinates ScreenToMap(Vector2 point);
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="ScreenToMap(ScreenCoordinates)"/>, except it should compensate for the effects of shaders on viewports.
|
||||
/// </summary>
|
||||
MapCoordinates PixelToMap(ScreenCoordinates point);
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="ScreenToMap(Vector2)"/>, except it should compensate for the effects of shaders on viewports.
|
||||
/// </summary>
|
||||
MapCoordinates PixelToMap(Vector2 point);
|
||||
|
||||
void ClearCurrentEye();
|
||||
void Initialize();
|
||||
}
|
||||
|
||||
@@ -207,6 +207,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{GlfwKey.F13, Key.F13},
|
||||
{GlfwKey.F14, Key.F14},
|
||||
{GlfwKey.F15, Key.F15},
|
||||
{GlfwKey.F16, Key.F16},
|
||||
{GlfwKey.F17, Key.F17},
|
||||
{GlfwKey.F18, Key.F18},
|
||||
{GlfwKey.F19, Key.F19},
|
||||
{GlfwKey.F20, Key.F20},
|
||||
{GlfwKey.F21, Key.F21},
|
||||
{GlfwKey.F22, Key.F22},
|
||||
{GlfwKey.F23, Key.F23},
|
||||
{GlfwKey.F24, Key.F24},
|
||||
{GlfwKey.Pause, Key.Pause},
|
||||
{GlfwKey.World1, Key.World1},
|
||||
};
|
||||
|
||||
@@ -191,6 +191,15 @@ internal partial class Clyde
|
||||
MapKey(SDL_SCANCODE_F13, Key.F13);
|
||||
MapKey(SDL_SCANCODE_F14, Key.F14);
|
||||
MapKey(SDL_SCANCODE_F15, Key.F15);
|
||||
MapKey(SDL_SCANCODE_F16, Key.F16);
|
||||
MapKey(SDL_SCANCODE_F17, Key.F17);
|
||||
MapKey(SDL_SCANCODE_F18, Key.F18);
|
||||
MapKey(SDL_SCANCODE_F19, Key.F19);
|
||||
MapKey(SDL_SCANCODE_F20, Key.F20);
|
||||
MapKey(SDL_SCANCODE_F21, Key.F21);
|
||||
MapKey(SDL_SCANCODE_F22, Key.F22);
|
||||
MapKey(SDL_SCANCODE_F23, Key.F23);
|
||||
MapKey(SDL_SCANCODE_F24, Key.F24);
|
||||
MapKey(SDL_SCANCODE_PAUSE, Key.Pause);
|
||||
|
||||
KeyMapReverse = new Dictionary<Key, SDL_Scancode>();
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
@@ -11,6 +12,7 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<Type, Overlay> _overlays = new Dictionary<Type, Overlay>();
|
||||
private ISawmill _logger = default!;
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public struct StencilParameters
|
||||
public partial struct StencilParameters
|
||||
{
|
||||
public StencilParameters()
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[ViewVariables] private ShaderKind Kind;
|
||||
[ViewVariables] private Dictionary<string, object>? _params;
|
||||
@@ -95,19 +95,19 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
[DataField("kind", required: true)]
|
||||
private readonly string _rawKind = default!;
|
||||
private string _rawKind = default!;
|
||||
|
||||
[DataField("path")]
|
||||
private readonly ResPath? _path;
|
||||
private ResPath? _path;
|
||||
|
||||
[DataField("params")]
|
||||
private readonly Dictionary<string, string>? _paramMapping;
|
||||
private Dictionary<string, string>? _paramMapping;
|
||||
|
||||
[DataField("light_mode")]
|
||||
private readonly string? _rawMode;
|
||||
private string? _rawMode;
|
||||
|
||||
[DataField("blend_mode")]
|
||||
private readonly string? _rawBlendMode;
|
||||
private string? _rawBlendMode;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Robust.Client.Input
|
||||
common.AddFunction(EngineKeyFunctions.TextWordDelete);
|
||||
common.AddFunction(EngineKeyFunctions.TextNewline);
|
||||
common.AddFunction(EngineKeyFunctions.TextSubmit);
|
||||
common.AddFunction(EngineKeyFunctions.MultilineTextSubmit);
|
||||
common.AddFunction(EngineKeyFunctions.TextCopy);
|
||||
common.AddFunction(EngineKeyFunctions.TextCut);
|
||||
common.AddFunction(EngineKeyFunctions.TextPaste);
|
||||
|
||||
@@ -161,6 +161,15 @@ namespace Robust.Client.Input
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
Pause,
|
||||
World1,
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
namespace Robust.Client.Input
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed class KeyBindingRegistration
|
||||
public sealed partial class KeyBindingRegistration
|
||||
{
|
||||
[DataField("function")]
|
||||
public BoundKeyFunction Function;
|
||||
|
||||
@@ -3,13 +3,16 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
@@ -24,7 +27,7 @@ namespace Robust.Client.Map
|
||||
|
||||
public Texture TileTextureAtlas => _tileTextureAtlas ?? Texture.Transparent;
|
||||
|
||||
private readonly Dictionary<ushort, Box2[]> _tileRegions = new();
|
||||
private readonly Dictionary<int, Box2[]> _tileRegions = new();
|
||||
|
||||
public Box2 ErrorTileRegion { get; private set; }
|
||||
|
||||
@@ -35,7 +38,7 @@ namespace Robust.Client.Map
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box2[]? TileAtlasRegion(ushort tileType)
|
||||
public Box2[]? TileAtlasRegion(int tileType)
|
||||
{
|
||||
if (_tileRegions.TryGetValue(tileType, out var region))
|
||||
{
|
||||
@@ -52,8 +55,11 @@ namespace Robust.Client.Map
|
||||
_genTextureAtlas();
|
||||
}
|
||||
|
||||
private void _genTextureAtlas()
|
||||
internal void _genTextureAtlas()
|
||||
{
|
||||
_tileRegions.Clear();
|
||||
_tileTextureAtlas = null;
|
||||
|
||||
var defList = TileDefs.Where(t => t.Sprite != null).ToList();
|
||||
|
||||
// If there are no tile definitions, we do nothing.
|
||||
@@ -144,4 +150,17 @@ namespace Robust.Client.Map
|
||||
_tileTextureAtlas = Texture.LoadFromImage(sheet, "Tile Atlas");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ReloadTileTexturesCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly ClydeTileDefinitionManager _tile = default!;
|
||||
|
||||
public override string Command => "reloadtiletextures";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_tile._genTextureAtlas();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,6 @@ namespace Robust.Client.Map
|
||||
/// Gets the region inside the texture atlas to use to draw a tile type.
|
||||
/// </summary>
|
||||
/// <returns>If null, do not draw the tile at all.</returns>
|
||||
Box2[]? TileAtlasRegion(ushort tileType);
|
||||
Box2[]? TileAtlasRegion(int tileType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace Robust.Client.Physics;
|
||||
/// Simple component used to tag entities that have physics prediction enabled.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class PredictedPhysicsComponent : Component
|
||||
public sealed partial class PredictedPhysicsComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ namespace Robust.Client.Placement
|
||||
return false;
|
||||
}
|
||||
coordinates = EntityCoordinates.FromMap(MapManager,
|
||||
EyeManager.ScreenToMap(InputManager.MouseScreenPosition));
|
||||
EyeManager.PixelToMap(InputManager.MouseScreenPosition));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Robust.Client.Placement
|
||||
public IEnumerable<EntityCoordinates> LineCoordinates()
|
||||
{
|
||||
var mouseScreen = pManager.InputManager.MouseScreenPosition;
|
||||
var mousePos = pManager.EyeManager.ScreenToMap(mouseScreen);
|
||||
var mousePos = pManager.EyeManager.PixelToMap(mouseScreen);
|
||||
|
||||
if (mousePos.MapId == MapId.Nullspace)
|
||||
yield break;
|
||||
@@ -165,7 +165,7 @@ namespace Robust.Client.Placement
|
||||
public IEnumerable<EntityCoordinates> GridCoordinates()
|
||||
{
|
||||
var mouseScreen = pManager.InputManager.MouseScreenPosition;
|
||||
var mousePos = pManager.EyeManager.ScreenToMap(mouseScreen);
|
||||
var mousePos = pManager.EyeManager.PixelToMap(mouseScreen);
|
||||
|
||||
if (mousePos.MapId == MapId.Nullspace)
|
||||
yield break;
|
||||
@@ -254,7 +254,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
protected EntityCoordinates ScreenToCursorGrid(ScreenCoordinates coords)
|
||||
{
|
||||
var mapCoords = pManager.EyeManager.ScreenToMap(coords.Position);
|
||||
var mapCoords = pManager.EyeManager.PixelToMap(coords.Position);
|
||||
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid))
|
||||
{
|
||||
return EntityCoordinates.FromMap(pManager.MapManager, mapCoords);
|
||||
|
||||
@@ -3,14 +3,18 @@ using System.Collections.Generic;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Player
|
||||
{
|
||||
public interface IPlayerManager : Shared.Players.ISharedPlayerManager
|
||||
public interface IPlayerManager : ISharedPlayerManager
|
||||
{
|
||||
new IEnumerable<ICommonSession> Sessions { get; }
|
||||
|
||||
[ViewVariables]
|
||||
IReadOnlyDictionary<NetUserId, ICommonSession> SessionsDict { get; }
|
||||
|
||||
[ViewVariables]
|
||||
LocalPlayer? LocalPlayer { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Player
|
||||
{
|
||||
@@ -17,11 +18,14 @@ namespace Robust.Client.Player
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public EntityUid? AttachedEntity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public NetUserId UserId { get; }
|
||||
|
||||
[ViewVariables]
|
||||
internal string Name { get; set; } = "<Unknown>";
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -31,9 +35,11 @@ namespace Robust.Client.Player
|
||||
set => this.Name = value;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
internal short Ping { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public INetChannel ConnectedClient { get; internal set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
22
Robust.Client/Replays/Commands/ReplayToggleUiCommand.cs
Normal file
22
Robust.Client/Replays/Commands/ReplayToggleUiCommand.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Robust.Client.Replays.UI;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Replays.Commands;
|
||||
|
||||
public sealed class ReplayToggleUiCommand : BaseReplayCommand
|
||||
{
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
|
||||
public override string Command => "replay_toggleui";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var screen = _userInterfaceManager.ActiveScreen;
|
||||
if (screen == null || !screen.TryGetWidget(out ReplayControlWidget? replayWidget))
|
||||
return;
|
||||
|
||||
replayWidget.Visible = !replayWidget.Visible;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Upload.Commands;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Replays;
|
||||
@@ -250,36 +251,56 @@ public sealed partial class ReplayLoadManager
|
||||
continue;
|
||||
|
||||
message.Messages.RemoveSwap(i);
|
||||
var changed = new Dictionary<Type, HashSet<string>>();
|
||||
_protoMan.LoadString(protoUpload.PrototypeData, true, changed);
|
||||
|
||||
foreach (var (kind, ids) in changed)
|
||||
try
|
||||
{
|
||||
var protos = prototypes[kind];
|
||||
var count = protos.Count;
|
||||
protos.UnionWith(ids);
|
||||
if (!ignoreDuplicates && ids.Count + count != protos.Count)
|
||||
{
|
||||
// An existing prototype was overwritten. Much like for resource uploading, supporting this
|
||||
// requires tracking the last-modified time of prototypes and either resetting or applying
|
||||
// prototype changes when jumping around in time. This also requires reworking how the initial
|
||||
// implicit state data is generated, because we can't simply cache it anymore.
|
||||
// Also, does reloading prototypes in release mode modify existing entities?
|
||||
|
||||
var msg = $"Overwriting an existing prototype! Kind: {kind.Name}. Ids: {string.Join(", ", ids)}";
|
||||
if (_confMan.GetCVar(CVars.ReplayIgnoreErrors))
|
||||
_sawmill.Error(msg);
|
||||
else
|
||||
throw new NotSupportedException(msg);
|
||||
}
|
||||
LoadPrototype(protoUpload.PrototypeData, prototypes, ignoreDuplicates);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is NotSupportedException || !_confMan.GetCVar(CVars.ReplayIgnoreErrors))
|
||||
throw;
|
||||
|
||||
_protoMan.ResolveResults();
|
||||
_protoMan.ReloadPrototypes(changed);
|
||||
_locMan.ReloadLocalizations();
|
||||
var msg = $"Caught exception while parsing uploaded prototypes in a replay. Exception: {e}";
|
||||
_sawmill.Error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadPrototype(
|
||||
string data,
|
||||
Dictionary<Type, HashSet<string>> prototypes,
|
||||
bool ignoreDuplicates)
|
||||
{
|
||||
var changed = new Dictionary<Type, HashSet<string>>();
|
||||
_protoMan.LoadString(data, true, changed);
|
||||
|
||||
foreach (var (kind, ids) in changed)
|
||||
{
|
||||
var protos = prototypes[kind];
|
||||
var count = protos.Count;
|
||||
protos.UnionWith(ids);
|
||||
if (!ignoreDuplicates && ids.Count + count != protos.Count)
|
||||
{
|
||||
// An existing prototype was overwritten. Much like for resource uploading, supporting this
|
||||
// requires tracking the last-modified time of prototypes and either resetting or applying
|
||||
// prototype changes when jumping around in time. This also requires reworking how the initial
|
||||
// implicit state data is generated, because we can't simply cache it anymore.
|
||||
// Also, does reloading prototypes in release mode modify existing entities?
|
||||
|
||||
var msg = $"Overwriting an existing prototype! Kind: {kind.Name}. Ids: {string.Join(", ", ids)}";
|
||||
if (_confMan.GetCVar(CVars.ReplayIgnoreErrors))
|
||||
_sawmill.Error(msg);
|
||||
else
|
||||
throw new NotSupportedException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
_protoMan.ResolveResults();
|
||||
_protoMan.ReloadPrototypes(changed);
|
||||
_locMan.ReloadLocalizations();
|
||||
}
|
||||
|
||||
private void UpdateDeletions(NetListAsArray<EntityUid> entityDeletions,
|
||||
Dictionary<EntityUid, EntityState> entStates, HashSet<EntityUid> detached)
|
||||
{
|
||||
|
||||
@@ -63,10 +63,8 @@ public sealed partial class ReplayControlWidget : UIWidget // AKA Tardis - The f
|
||||
base.FrameUpdate(args);
|
||||
if (_playback.Replay is not { } replay)
|
||||
{
|
||||
Visible = false;
|
||||
return;
|
||||
}
|
||||
Visible = true;
|
||||
|
||||
TickSlider.MinValue = 0;
|
||||
TickSlider.MaxValue = (float)replay.ReplayTime[^1].TotalSeconds;
|
||||
|
||||
@@ -10,25 +10,26 @@
|
||||
<RobustILLink>true</RobustILLink>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" PrivateAssets="compile" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' == 'True'" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Robust.Natives" Version="0.1.1" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" />
|
||||
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
|
||||
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(EnableClientScripting)' == 'True'">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" PrivateAssets="compile" />
|
||||
|
||||
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -715,7 +715,7 @@ namespace Robust.Client.UserInterface
|
||||
maxH = MathHelper.Clamp(maxConstraint, minH, maxH);
|
||||
|
||||
minConstraint = float.IsNaN(setH) ? 0 : setH;
|
||||
minH = MathHelper.Clamp(maxH, minConstraint, minH);
|
||||
minH = MathHelper.Clamp(minConstraint, minH, maxH);
|
||||
|
||||
return new Vector2(
|
||||
Math.Clamp(avail.X, minW, maxW),
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
public void AddMessage(FormattedMessage message)
|
||||
{
|
||||
var entry = new RichTextEntry(message, this, _tagManager);
|
||||
var entry = new RichTextEntry(message, this, _tagManager, null);
|
||||
|
||||
entry.Update(_getFont(), _getContentBox().Width, UIScale);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
@@ -21,18 +22,18 @@ namespace Robust.Client.UserInterface.Controls
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void SetMessage(FormattedMessage message)
|
||||
public void SetMessage(FormattedMessage message, Type[]? tagsAllowed = null, Color? defaultColor = null)
|
||||
{
|
||||
_message = message;
|
||||
_entry = new RichTextEntry(_message, this, _tagManager);
|
||||
_entry = new RichTextEntry(_message, this, _tagManager, tagsAllowed, defaultColor);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
|
||||
public void SetMessage(string message)
|
||||
public void SetMessage(string message, Type[]? tagsAllowed = null, Color? defaultColor = null)
|
||||
{
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(message);
|
||||
SetMessage(msg);
|
||||
SetMessage(msg, tagsAllowed, defaultColor);
|
||||
}
|
||||
|
||||
public string? GetMessage() => _message?.ToMarkup();
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public const string LeftButtonStyle = "spinbox-left";
|
||||
public const string RightButtonStyle = "spinbox-right";
|
||||
public const string MiddleButtonStyle = "spinbox-middle";
|
||||
private LineEdit _lineEdit;
|
||||
public LineEdit LineEditControl { get; }
|
||||
private List<Button> _leftButtons = new();
|
||||
private List<Button> _rightButtons = new();
|
||||
private int _stepSize = 1;
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
_value = value;
|
||||
_lineEdit.Text = value.ToString();
|
||||
LineEditControl.Text = value.ToString();
|
||||
ValueChanged?.Invoke(new ValueChangedEventArgs(value));
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
_value = value;
|
||||
_lineEdit.Text = value.ToString();
|
||||
LineEditControl.Text = value.ToString();
|
||||
}
|
||||
|
||||
public event Action<ValueChangedEventArgs>? ValueChanged;
|
||||
@@ -62,17 +62,17 @@ namespace Robust.Client.UserInterface.Controls
|
||||
Orientation = LayoutOrientation.Horizontal;
|
||||
MouseFilter = MouseFilterMode.Pass;
|
||||
|
||||
_lineEdit = new LineEdit
|
||||
LineEditControl = new LineEdit
|
||||
{
|
||||
MinSize = new Vector2(40, 0),
|
||||
HorizontalExpand = true
|
||||
};
|
||||
AddChild(_lineEdit);
|
||||
AddChild(LineEditControl);
|
||||
|
||||
Value = 0;
|
||||
|
||||
_lineEdit.IsValid = (str) => int.TryParse(str, out var i) && (IsValid == null || IsValid(i));
|
||||
_lineEdit.OnTextChanged += (args) =>
|
||||
LineEditControl.IsValid = (str) => int.TryParse(str, out var i) && (IsValid == null || IsValid(i));
|
||||
LineEditControl.OnTextChanged += (args) =>
|
||||
{
|
||||
if (int.TryParse(args.Text, out int i))
|
||||
Value = i;
|
||||
@@ -143,8 +143,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
public bool LineEditDisabled
|
||||
{
|
||||
get => !_lineEdit.Editable;
|
||||
set => _lineEdit.Editable = !value;
|
||||
get => !LineEditControl.Editable;
|
||||
set => LineEditControl.Editable = !value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -185,7 +185,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
base.MouseWheel(args);
|
||||
|
||||
if (!_lineEdit.HasKeyboardFocus())
|
||||
if (!LineEditControl.HasKeyboardFocus())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -776,9 +776,11 @@ public sealed class TextEdit : Control
|
||||
{
|
||||
var size = base.ArrangeOverride(finalSize);
|
||||
|
||||
_scrollBar.Page = size.Y * UIScale;
|
||||
var renderBoxSize = _renderBox.Size;
|
||||
|
||||
UpdateLineBreaks((int)(size.X * UIScale));
|
||||
_scrollBar.Page = renderBoxSize.Y * UIScale;
|
||||
|
||||
UpdateLineBreaks((int)(renderBoxSize.X * UIScale));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
|
||||
EntityCoordinates mouseGridPos;
|
||||
TileRef tile;
|
||||
|
||||
var mouseWorldMap = _eyeManager.ScreenToMap(mouseScreenPos);
|
||||
var mouseWorldMap = _eyeManager.PixelToMap(mouseScreenPos);
|
||||
if (mouseWorldMap == MapCoordinates.Nullspace)
|
||||
return;
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
/// </param>
|
||||
MapCoordinates ScreenToMap(Vector2 coords);
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="ScreenToMap(Vector2)"/>, except it should compensate for the effects of shaders on viewports.
|
||||
/// </summary>
|
||||
MapCoordinates PixelToMap(Vector2 point);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a point on the map to screen coordinates.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Robust.Client.UserInterface.CustomControls;
|
||||
|
||||
/// <summary>
|
||||
/// An event used to reverse distortion effects applied by shaders.
|
||||
/// Used to find the map position that visible pixels originate from so that severe distortion shaders do not make interaction nigh-impossible.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct PixelToMapEvent(Vector2 LocalPosition, IViewportControl Control, IClydeViewport Viewport)
|
||||
{
|
||||
/// <summary>
|
||||
/// The local position of the pixel within the <see cref="Control"/> that we are trying to convert to a map position.
|
||||
/// </summary>
|
||||
public readonly Vector2 LocalPosition = LocalPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The original (or WIP) location of the pixel within the <see cref="Control"/> that we are trying to convert to a map position.
|
||||
/// Used as the output of the event.
|
||||
/// </summary>
|
||||
public Vector2 VisiblePosition = LocalPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The control the pixel we are considering is located within.
|
||||
/// </summary>
|
||||
public readonly IViewportControl Control = Control;
|
||||
|
||||
/// <summary>
|
||||
/// The viewport being displayed by the control we are considering.
|
||||
/// </summary>
|
||||
public readonly IClydeViewport Viewport = Viewport;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
@@ -13,8 +14,9 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
[Virtual]
|
||||
public class ViewportContainer : Control, IViewportControl
|
||||
{
|
||||
private readonly IClyde _displayManager;
|
||||
private readonly IInputManager _inputManager;
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
public IClydeViewport? Viewport { get; set; }
|
||||
|
||||
@@ -36,8 +38,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
|
||||
public ViewportContainer()
|
||||
{
|
||||
_displayManager = IoCManager.Resolve<IClyde>();
|
||||
_inputManager = IoCManager.Resolve<IInputManager>();
|
||||
IoCManager.InjectDependencies(this);
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
Resized();
|
||||
}
|
||||
@@ -99,8 +100,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
// -- Handlers: In --
|
||||
|
||||
// -- Utils / S2M-M2S Base --
|
||||
|
||||
public MapCoordinates LocalPixelToMap(Vector2 point)
|
||||
public MapCoordinates LocalCoordsToMap(Vector2 point)
|
||||
{
|
||||
if (Viewport == null)
|
||||
return default;
|
||||
@@ -111,6 +111,19 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
return Viewport.LocalToWorld(point);
|
||||
}
|
||||
|
||||
public MapCoordinates LocalPixelToMap(Vector2 point)
|
||||
{
|
||||
if (Viewport == null)
|
||||
return default;
|
||||
|
||||
// pre-scaler
|
||||
point *= _viewportResolution;
|
||||
var ev = new PixelToMapEvent(point, this, Viewport);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, ref ev);
|
||||
|
||||
return Viewport.LocalToWorld(ev.VisiblePosition);
|
||||
}
|
||||
|
||||
public Vector2 WorldToLocalPixel(Vector2 point)
|
||||
{
|
||||
if (Viewport?.Eye == null)
|
||||
@@ -127,6 +140,12 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
// -- Utils / S2M-M2S Extended --
|
||||
|
||||
public MapCoordinates ScreenToMap(Vector2 point)
|
||||
{
|
||||
return LocalCoordsToMap(point - GlobalPixelPosition);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public MapCoordinates PixelToMap(Vector2 point)
|
||||
{
|
||||
return LocalPixelToMap(point - GlobalPixelPosition);
|
||||
}
|
||||
|
||||
29
Robust.Client/UserInterface/RichText/BoldItalicTag.cs
Normal file
29
Robust.Client/UserInterface/RichText/BoldItalicTag.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.UserInterface.RichText;
|
||||
|
||||
public sealed class BoldItalicTag : IMarkupTag
|
||||
{
|
||||
public const string BoldItalicFont = "DefaultBoldItalic";
|
||||
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public string Name => "bolditalic";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, BoldItalicFont);
|
||||
context.Font.Push(font);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
context.Font.Pop();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -14,12 +15,18 @@ public sealed class BoldTag : IMarkupTag
|
||||
|
||||
public string Name => "bold";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, BoldFont);
|
||||
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager,
|
||||
context.Tags.Any(static x => x is ItalicTag)
|
||||
? BoldItalicTag.BoldItalicFont
|
||||
: BoldFont
|
||||
);
|
||||
context.Font.Push(font);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
context.Font.Pop();
|
||||
|
||||
11
Robust.Client/UserInterface/RichText/BulletTag.cs
Normal file
11
Robust.Client/UserInterface/RichText/BulletTag.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.UserInterface.RichText;
|
||||
|
||||
public sealed class BulletTag : IMarkupTag
|
||||
{
|
||||
public string Name => "bullet";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string TextBefore(MarkupNode _) => " · ";
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Robust.Client.UserInterface.RichText;
|
||||
/// </summary>
|
||||
public sealed class ColorTag : IMarkupTag
|
||||
{
|
||||
private static readonly Color DefaultColor = new(200, 200, 200);
|
||||
public static readonly Color DefaultColor = new(200, 200, 200);
|
||||
|
||||
public string Name => "color";
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace Robust.Client.UserInterface.RichText;
|
||||
public sealed class FontPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField("path", required: true)]
|
||||
public ResPath Path { get; } = default!;
|
||||
public ResPath Path { get; private set; } = default!;
|
||||
}
|
||||
|
||||
@@ -14,12 +14,14 @@ namespace Robust.Client.UserInterface.RichText;
|
||||
public sealed class FontTag : IMarkupTag
|
||||
{
|
||||
public const string DefaultFont = "Default";
|
||||
public const int DefaultSize = 12;
|
||||
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public string Name => "font";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
string fontId = node.Value.StringValue ?? DefaultFont;
|
||||
@@ -28,6 +30,7 @@ public sealed class FontTag : IMarkupTag
|
||||
context.Font.Push(font);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
context.Font.Pop();
|
||||
@@ -44,7 +47,7 @@ public sealed class FontTag : IMarkupTag
|
||||
IPrototypeManager prototypeManager,
|
||||
string fontId)
|
||||
{
|
||||
var size = 12;
|
||||
var size = DefaultSize;
|
||||
|
||||
if (contextFontStack.TryPeek(out var previousFont))
|
||||
{
|
||||
|
||||
36
Robust.Client/UserInterface/RichText/HeadingTag.cs
Normal file
36
Robust.Client/UserInterface/RichText/HeadingTag.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.UserInterface.RichText;
|
||||
|
||||
public sealed class HeadingTag : IMarkupTag
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public string Name => "head";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
if (!node.Value.TryGetLong(out var levelParam))
|
||||
return;
|
||||
|
||||
var level = Math.Min(Math.Max((int)levelParam, 1), 3);
|
||||
node.Attributes["size"] = new MarkupParameter(
|
||||
(int)Math.Ceiling(FontTag.DefaultSize * 2 / Math.Sqrt(level))
|
||||
);
|
||||
|
||||
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, BoldTag.BoldFont);
|
||||
context.Font.Push(font);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
context.Font.Pop();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -11,14 +12,21 @@ public sealed class ItalicTag : IMarkupTag
|
||||
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public string Name => "italic";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, ItalicFont);
|
||||
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager,
|
||||
context.Tags.Any(static x => x is BoldTag)
|
||||
? BoldItalicTag.BoldItalicFont
|
||||
: ItalicFont
|
||||
);
|
||||
context.Font.Push(font);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
|
||||
{
|
||||
context.Font.Pop();
|
||||
|
||||
@@ -8,22 +8,26 @@ public sealed class MarkupDrawingContext
|
||||
{
|
||||
public readonly Stack<Color> Color;
|
||||
public readonly Stack<Font> Font;
|
||||
public readonly List<IMarkupTag> Tags;
|
||||
|
||||
public MarkupDrawingContext()
|
||||
{
|
||||
Color = new Stack<Color>();
|
||||
Font = new Stack<Font>();
|
||||
Tags = new List<IMarkupTag>();
|
||||
}
|
||||
|
||||
public MarkupDrawingContext(int capacity)
|
||||
{
|
||||
Color = new Stack<Color>(capacity);
|
||||
Font = new Stack<Font>(capacity);
|
||||
Tags = new List<IMarkupTag>();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Color.Clear();
|
||||
Font.Clear();
|
||||
Tags.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Sandboxing;
|
||||
@@ -15,24 +16,29 @@ public sealed class MarkupTagManager
|
||||
/// <summary>
|
||||
/// Tags defined in engine need to be instantiated here because of sandboxing
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, IMarkupTag> _markupTagTypes = new()
|
||||
{
|
||||
{"color", new ColorTag()},
|
||||
{"cmdlink", new CommandLinkTag()},
|
||||
{"font", new FontTag()},
|
||||
{"bold", new BoldTag()},
|
||||
{"italic", new ItalicTag()}
|
||||
};
|
||||
private readonly Dictionary<string, IMarkupTag> _markupTagTypes = new IMarkupTag[] {
|
||||
new BoldItalicTag(),
|
||||
new BoldTag(),
|
||||
new BulletTag(),
|
||||
new ColorTag(),
|
||||
new CommandLinkTag(),
|
||||
new FontTag(),
|
||||
new HeadingTag(),
|
||||
new ItalicTag()
|
||||
}.ToDictionary(x => x.Name.ToLower(), x => x);
|
||||
|
||||
/// <summary>
|
||||
/// A list of <see cref="IMarkupTag"/> types that shouldn't be instantiated through reflection
|
||||
/// </summary>
|
||||
private readonly List<Type> _engineTypes = new()
|
||||
{
|
||||
typeof(BoldItalicTag),
|
||||
typeof(BoldTag),
|
||||
typeof(BulletTag),
|
||||
typeof(ColorTag),
|
||||
typeof(CommandLinkTag),
|
||||
typeof(FontTag),
|
||||
typeof(BoldTag),
|
||||
typeof(HeadingTag),
|
||||
typeof(ItalicTag)
|
||||
};
|
||||
|
||||
@@ -59,9 +65,11 @@ public sealed class MarkupTagManager
|
||||
return _markupTagTypes.GetValueOrDefault(name);
|
||||
}
|
||||
|
||||
public bool TryGetMarkupTag(string name, [NotNullWhen(true)] out IMarkupTag? tag)
|
||||
public bool TryGetMarkupTag(string name, Type[]? tagsAllowed, [NotNullWhen(true)] out IMarkupTag? tag)
|
||||
{
|
||||
if (_markupTagTypes.TryGetValue(name, out var markupTag))
|
||||
if (_markupTagTypes.TryGetValue(name, out var markupTag)
|
||||
// Using a whitelist prevents new tags from sneaking in.
|
||||
&& (tagsAllowed == null || Array.IndexOf(tagsAllowed, markupTag.GetType()) != -1))
|
||||
{
|
||||
tag = markupTag;
|
||||
return true;
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace Robust.Client.UserInterface
|
||||
/// </summary>
|
||||
internal struct RichTextEntry
|
||||
{
|
||||
private static readonly Color DefaultColor = new(200, 200, 200);
|
||||
|
||||
private readonly Color _defaultColor;
|
||||
private readonly MarkupTagManager _tagManager;
|
||||
private readonly Type[]? _tagsAllowed;
|
||||
|
||||
public readonly FormattedMessage Message;
|
||||
|
||||
@@ -39,13 +39,15 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
private readonly Dictionary<int, Control> _tagControls = new();
|
||||
|
||||
public RichTextEntry(FormattedMessage message, Control parent, MarkupTagManager tagManager)
|
||||
public RichTextEntry(FormattedMessage message, Control parent, MarkupTagManager tagManager, Type[]? tagsAllowed = null, Color? defaultColor = null)
|
||||
{
|
||||
Message = message;
|
||||
Height = 0;
|
||||
Width = 0;
|
||||
LineBreaks = default;
|
||||
_defaultColor = defaultColor ?? new(200, 200, 200);
|
||||
_tagManager = tagManager;
|
||||
_tagsAllowed = tagsAllowed;
|
||||
|
||||
var nodeIndex = -1;
|
||||
foreach (var node in Message.Nodes)
|
||||
@@ -55,7 +57,7 @@ namespace Robust.Client.UserInterface
|
||||
if (node.Name == null)
|
||||
continue;
|
||||
|
||||
if (!_tagManager.TryGetMarkupTag(node.Name, out var tag) || !tag.TryGetControl(node, out var control))
|
||||
if (!_tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag) || !tag.TryGetControl(node, out var control))
|
||||
continue;
|
||||
|
||||
parent.Children.Add(control);
|
||||
@@ -82,7 +84,7 @@ namespace Robust.Client.UserInterface
|
||||
var wordWrap = new WordWrap(maxSizeX);
|
||||
var context = new MarkupDrawingContext();
|
||||
context.Font.Push(defaultFont);
|
||||
context.Color.Push(DefaultColor);
|
||||
context.Color.Push(_defaultColor);
|
||||
|
||||
// Go over every node.
|
||||
// Nodes can change the markup drawing context and return additional text.
|
||||
@@ -171,7 +173,7 @@ namespace Robust.Client.UserInterface
|
||||
float uiScale)
|
||||
{
|
||||
context.Clear();
|
||||
context.Color.Push(DefaultColor);
|
||||
context.Color.Push(_defaultColor);
|
||||
context.Font.Push(defaultFont);
|
||||
|
||||
var globalBreakCounter = 0;
|
||||
@@ -186,7 +188,7 @@ namespace Robust.Client.UserInterface
|
||||
var text = ProcessNode(node, context);
|
||||
if (!context.Color.TryPeek(out var color) || !context.Font.TryPeek(out var font))
|
||||
{
|
||||
color = DefaultColor;
|
||||
color = _defaultColor;
|
||||
font = defaultFont;
|
||||
}
|
||||
|
||||
@@ -226,15 +228,17 @@ namespace Robust.Client.UserInterface
|
||||
return node.Value.StringValue ?? "";
|
||||
|
||||
//Skip the node if there is no markup tag for it.
|
||||
if (!_tagManager.TryGetMarkupTag(node.Name, out var tag))
|
||||
if (!_tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag))
|
||||
return "";
|
||||
|
||||
if (!node.Closing)
|
||||
{
|
||||
context.Tags.Add(tag);
|
||||
tag.PushDrawContext(node, context);
|
||||
return tag.TextBefore(node);
|
||||
}
|
||||
|
||||
context.Tags.Remove(tag);
|
||||
tag.PopDrawContext(node, context);
|
||||
return tag.TextAfter(node);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public sealed class UITheme : IPrototype
|
||||
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField("path")]
|
||||
private ResPath _path;
|
||||
@@ -70,7 +70,7 @@ public sealed class UITheme : IPrototype
|
||||
|
||||
if (!texturePath.EndsWith(".png"))
|
||||
texturePath = $"{texturePath}.png";
|
||||
|
||||
|
||||
var resPath = new ResPath(texturePath);
|
||||
if (resPath.IsRelative)
|
||||
{
|
||||
|
||||
@@ -38,20 +38,22 @@ namespace Robust.Client.UserInterface
|
||||
/// <param name="tooltip">control to position (current size will be used to determine bounds)</param>
|
||||
public static void PositionTooltip(Vector2 screenBounds, Vector2 screenPosition, Control tooltip)
|
||||
{
|
||||
LayoutContainer.SetPosition(tooltip, screenPosition);
|
||||
|
||||
tooltip.Measure(Vector2Helpers.Infinity);
|
||||
var combinedMinSize = tooltip.DesiredSize;
|
||||
var (right, bottom) = tooltip.Position + combinedMinSize;
|
||||
|
||||
LayoutContainer.SetPosition(tooltip, new Vector2(screenPosition.X, screenPosition.Y - combinedMinSize.Y));
|
||||
|
||||
var right = tooltip.Position.X + combinedMinSize.X;
|
||||
var top = tooltip.Position.Y - combinedMinSize.Y;
|
||||
|
||||
if (right > screenBounds.X)
|
||||
{
|
||||
LayoutContainer.SetPosition(tooltip, new(screenPosition.X - combinedMinSize.X, tooltip.Position.Y));
|
||||
}
|
||||
|
||||
if (bottom > screenBounds.Y)
|
||||
if (top < 0f)
|
||||
{
|
||||
LayoutContainer.SetPosition(tooltip, new(tooltip.Position.X, screenPosition.Y - combinedMinSize.Y));
|
||||
LayoutContainer.SetPosition(tooltip, new(tooltip.Position.X, 0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ internal partial class UserInterfaceManager
|
||||
private float? _tooltipDelay;
|
||||
private bool _showingTooltip;
|
||||
private Control? _suppliedTooltip;
|
||||
private const float TooltipDelay = 1;
|
||||
private const float TooltipDelay = 0.25f;
|
||||
|
||||
private WindowRoot? _focusedRoot;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ internal partial class UserInterfaceManager
|
||||
public void SetActiveTheme(string themeName)
|
||||
{
|
||||
if (!_themes.TryGetValue(themeName, out var theme) || (theme == CurrentTheme)) return;
|
||||
CurrentTheme = theme;
|
||||
UpdateTheme(theme);
|
||||
}
|
||||
|
||||
public void SetDefaultTheme(string themeId)
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Robust.Client.Utility
|
||||
|
||||
public static Direction TurnCw(this Direction dir)
|
||||
{
|
||||
return (Direction)(((int)dir - 1) % 8);
|
||||
return (Direction)(((int)dir + 7) % 8);
|
||||
}
|
||||
|
||||
public static Direction TurnCcw(this Direction dir)
|
||||
|
||||
6
Robust.Serialization.Generator/DataDefinition.cs
Normal file
6
Robust.Serialization.Generator/DataDefinition.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
public sealed record DataDefinition(ITypeSymbol Type, string GenericTypeName, List<DataField> Fields, bool HasHooks, bool InvalidFields);
|
||||
17
Robust.Serialization.Generator/DataDefinitionComparer.cs
Normal file
17
Robust.Serialization.Generator/DataDefinitionComparer.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
public sealed class DataDefinitionComparer : IEqualityComparer<TypeDeclarationSyntax>
|
||||
{
|
||||
public bool Equals(TypeDeclarationSyntax x, TypeDeclarationSyntax y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public int GetHashCode(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.GetHashCode();
|
||||
}
|
||||
}
|
||||
14
Robust.Serialization.Generator/DataField.cs
Normal file
14
Robust.Serialization.Generator/DataField.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
public sealed record DataField(
|
||||
ISymbol Symbol,
|
||||
ITypeSymbol Type,
|
||||
(INamedTypeSymbol Serializer, CustomSerializerType Type)? CustomSerializer);
|
||||
|
||||
public enum CustomSerializerType
|
||||
{
|
||||
Copier,
|
||||
CopyCreator
|
||||
}
|
||||
552
Robust.Serialization.Generator/Generator.cs
Normal file
552
Robust.Serialization.Generator/Generator.cs
Normal file
@@ -0,0 +1,552 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Robust.Serialization.Generator.CustomSerializerType;
|
||||
using static Robust.Serialization.Generator.Types;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
[Generator]
|
||||
public class Generator : IIncrementalGenerator
|
||||
{
|
||||
private const string TypeCopierInterfaceNamespace = "Robust.Shared.Serialization.TypeSerializers.Interfaces.ITypeCopier";
|
||||
private const string TypeCopyCreatorInterfaceNamespace = "Robust.Shared.Serialization.TypeSerializers.Interfaces.ITypeCopyCreator";
|
||||
private const string SerializationHooksNamespace = "Robust.Shared.Serialization.ISerializationHooks";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext initContext)
|
||||
{
|
||||
IncrementalValuesProvider<TypeDeclarationSyntax> dataDefinitions = initContext.SyntaxProvider.CreateSyntaxProvider(
|
||||
static (node, _) => node is TypeDeclarationSyntax,
|
||||
static (context, _) =>
|
||||
{
|
||||
var type = (TypeDeclarationSyntax) context.Node;
|
||||
var symbol = (ITypeSymbol) context.SemanticModel.GetDeclaredSymbol(type)!;
|
||||
return IsDataDefinition(symbol) ? type : null;
|
||||
}
|
||||
).Where(static type => type != null)!;
|
||||
|
||||
var comparer = new DataDefinitionComparer();
|
||||
initContext.RegisterSourceOutput(
|
||||
initContext.CompilationProvider.Combine(dataDefinitions.WithComparer(comparer).Collect()),
|
||||
static (sourceContext, source) =>
|
||||
{
|
||||
var (compilation, declarations) = source;
|
||||
var builder = new StringBuilder();
|
||||
var containingTypes = new Stack<INamedTypeSymbol>();
|
||||
|
||||
foreach (var declaration in declarations)
|
||||
{
|
||||
builder.Clear();
|
||||
containingTypes.Clear();
|
||||
|
||||
var type = compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)!;
|
||||
|
||||
var nonPartial = !IsPartial(declaration);
|
||||
|
||||
var namespaceString = type.ContainingNamespace.IsGlobalNamespace
|
||||
? string.Empty
|
||||
: $"namespace {type.ContainingNamespace.ToDisplayString()};";
|
||||
|
||||
var containingType = type.ContainingType;
|
||||
while (containingType != null)
|
||||
{
|
||||
containingTypes.Push(containingType);
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
|
||||
var containingTypesStart = new StringBuilder();
|
||||
var containingTypesEnd = new StringBuilder();
|
||||
foreach (var parent in containingTypes)
|
||||
{
|
||||
var syntax = (ClassDeclarationSyntax) parent.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
if (!IsPartial(syntax))
|
||||
{
|
||||
nonPartial = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{");
|
||||
containingTypesEnd.AppendLine("}");
|
||||
}
|
||||
|
||||
var definition = GetDataDefinition(type);
|
||||
if (nonPartial || definition.InvalidFields)
|
||||
continue;
|
||||
|
||||
builder.AppendLine($$"""
|
||||
#nullable enable
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Exceptions;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
|
||||
#pragma warning disable RA0002 // Robust access analyzer
|
||||
|
||||
{{namespaceString}}
|
||||
|
||||
{{containingTypesStart}}
|
||||
|
||||
{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}>
|
||||
{
|
||||
{{GetConstructor(definition)}}
|
||||
|
||||
{{GetCopyMethods(definition)}}
|
||||
|
||||
{{GetInstantiators(definition)}}
|
||||
}
|
||||
|
||||
{{containingTypesEnd}}
|
||||
""");
|
||||
|
||||
var symbolName = type
|
||||
.ToDisplayString()
|
||||
.Replace('<', '{')
|
||||
.Replace('>', '}');
|
||||
|
||||
var sourceText = CSharpSyntaxTree
|
||||
.ParseText(builder.ToString())
|
||||
.GetRoot()
|
||||
.NormalizeWhitespace()
|
||||
.ToFullString();
|
||||
|
||||
sourceContext.AddSource($"{symbolName}.g.cs", sourceText);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static DataDefinition GetDataDefinition(ITypeSymbol definition)
|
||||
{
|
||||
var fields = new List<DataField>();
|
||||
var invalidFields = false;
|
||||
|
||||
foreach (var member in definition.GetMembers())
|
||||
{
|
||||
if (member is not IFieldSymbol && member is not IPropertySymbol)
|
||||
continue;
|
||||
|
||||
if (member.IsStatic)
|
||||
continue;
|
||||
|
||||
if (IsDataField(member, out var type, out var attribute))
|
||||
{
|
||||
if (attribute.ConstructorArguments.FirstOrDefault(arg => arg.Kind == TypedConstantKind.Type).Value is INamedTypeSymbol customSerializer)
|
||||
{
|
||||
if (ImplementsInterface(customSerializer, TypeCopierInterfaceNamespace))
|
||||
{
|
||||
fields.Add(new DataField(member, type, (customSerializer, Copier)));
|
||||
continue;
|
||||
}
|
||||
else if (ImplementsInterface(customSerializer, TypeCopyCreatorInterfaceNamespace))
|
||||
{
|
||||
fields.Add(new DataField(member, type, (customSerializer, CopyCreator)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fields.Add(new DataField(member, type, null));
|
||||
|
||||
if (IsReadOnlyMember(definition, type))
|
||||
{
|
||||
invalidFields = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var typeName = GetGenericTypeName(definition);
|
||||
var hasHooks = ImplementsInterface(definition, SerializationHooksNamespace);
|
||||
|
||||
return new DataDefinition(definition, typeName, fields, hasHooks, invalidFields);
|
||||
}
|
||||
|
||||
private static string GetConstructor(DataDefinition definition)
|
||||
{
|
||||
if (definition.Type.TypeKind == TypeKind.Interface)
|
||||
return string.Empty;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if (NeedsEmptyConstructor(definition.Type))
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
// Implicit constructor
|
||||
#pragma warning disable CS8618
|
||||
public {{definition.Type.Name}}()
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string GetCopyMethods(DataDefinition definition)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
var modifiers = IsVirtualClass(definition.Type) ? "virtual " : string.Empty;
|
||||
var baseCall = string.Empty;
|
||||
string baseCopy;
|
||||
var baseType = definition.Type.BaseType;
|
||||
|
||||
if (baseType != null && IsDataDefinition(definition.Type.BaseType))
|
||||
{
|
||||
var baseName = baseType.ToDisplayString();
|
||||
baseCall = $"""
|
||||
var definitionCast = ({baseName}) target;
|
||||
base.InternalCopy(ref definitionCast, serialization, hookCtx, context);
|
||||
target = ({definition.GenericTypeName}) definitionCast;
|
||||
""";
|
||||
|
||||
baseCopy = $$"""
|
||||
public override void Copy(ref {{baseName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var cast = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref cast, serialization, hookCtx, context);
|
||||
target = cast!;
|
||||
}
|
||||
|
||||
public override void Copy(ref object target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var cast = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref cast, serialization, hookCtx, context);
|
||||
target = cast!;
|
||||
}
|
||||
""";
|
||||
}
|
||||
else
|
||||
{
|
||||
baseCopy = $$"""
|
||||
public {{modifiers}} void Copy(ref object target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var cast = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref cast, serialization, hookCtx, context);
|
||||
target = cast!;
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
public {{modifiers}} void InternalCopy(ref {{definition.GenericTypeName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
{{baseCall}}
|
||||
{{CopyDataFields(definition)}}
|
||||
}
|
||||
|
||||
public {{modifiers}} void Copy(ref {{definition.GenericTypeName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
InternalCopy(ref target, serialization, hookCtx, context);
|
||||
}
|
||||
|
||||
{{baseCopy}}
|
||||
""");
|
||||
|
||||
foreach (var @interface in GetImplicitDataDefinitionInterfaces(definition.Type, true))
|
||||
{
|
||||
var interfaceModifiers = baseType != null && baseType.AllInterfaces.Contains(@interface, SymbolEqualityComparer.Default)
|
||||
? "override "
|
||||
: modifiers;
|
||||
var interfaceName = @interface.ToDisplayString();
|
||||
|
||||
builder.AppendLine($$"""
|
||||
public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
var def = ({{definition.GenericTypeName}}) target;
|
||||
Copy(ref def, serialization, hookCtx, context);
|
||||
target = def;
|
||||
}
|
||||
|
||||
public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
InternalCopy(ref target, serialization, hookCtx, context);
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string GetInstantiators(DataDefinition definition)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var modifiers = string.Empty;
|
||||
|
||||
if (definition.Type.BaseType is { } baseType && IsDataDefinition(baseType))
|
||||
modifiers = "override ";
|
||||
else if (IsVirtualClass(definition.Type))
|
||||
modifiers = "virtual ";
|
||||
|
||||
if (definition.Type.IsAbstract)
|
||||
{
|
||||
// TODO make abstract once data definitions are forced to be partial
|
||||
builder.AppendLine($$"""
|
||||
public {{modifiers}} {{definition.GenericTypeName}} Instantiate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
public {{modifiers}} {{definition.GenericTypeName}} Instantiate()
|
||||
{
|
||||
return new {{definition.GenericTypeName}}();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
foreach (var @interface in GetImplicitDataDefinitionInterfaces(definition.Type, false))
|
||||
{
|
||||
var interfaceName = @interface.ToDisplayString();
|
||||
builder.AppendLine($$"""
|
||||
{{interfaceName}} {{interfaceName}}.Instantiate()
|
||||
{
|
||||
return Instantiate();
|
||||
}
|
||||
|
||||
{{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate()
|
||||
{
|
||||
return Instantiate();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
// TODO serveronly? do we care? who knows!!
|
||||
private static StringBuilder CopyDataFields(DataDefinition definition)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine($"""
|
||||
if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.ToString().ToLower()}, context))
|
||||
return;
|
||||
""");
|
||||
|
||||
var structCopier = new StringBuilder();
|
||||
foreach (var field in definition.Fields)
|
||||
{
|
||||
var type = field.Type;
|
||||
var typeName = type.ToDisplayString();
|
||||
if (IsMultidimensionalArray(type))
|
||||
{
|
||||
typeName = typeName.Replace("*", "");
|
||||
}
|
||||
|
||||
var isNullableValueType = IsNullableValueType(type);
|
||||
var nonNullableTypeName = type.WithNullableAnnotation(NullableAnnotation.None).ToDisplayString();
|
||||
if (isNullableValueType)
|
||||
{
|
||||
nonNullableTypeName = typeName.Substring(0, typeName.Length - 1);
|
||||
}
|
||||
|
||||
var isClass = type.IsReferenceType || type.SpecialType == SpecialType.System_String;
|
||||
var isNullable = type.NullableAnnotation == NullableAnnotation.Annotated;
|
||||
var nullableOverride = isClass && !isNullable ? ", true" : string.Empty;
|
||||
var name = field.Symbol.Name;
|
||||
var tempVarName = $"{name}Temp";
|
||||
var nullableValue = isNullableValueType ? ".Value" : string.Empty;
|
||||
var nullNotAllowed = isClass && !isNullable;
|
||||
|
||||
if (field.CustomSerializer is { Serializer: var serializer, Type: var serializerType })
|
||||
{
|
||||
if (nullNotAllowed)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
throw new NullNotAllowedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{typeName}} {{tempVarName}} = default!;
|
||||
""");
|
||||
|
||||
if (isNullable || isNullableValueType)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
{{tempVarName}} = null!;
|
||||
}
|
||||
else
|
||||
{
|
||||
""");
|
||||
}
|
||||
|
||||
var serializerName = serializer.ToDisplayString();
|
||||
switch (serializerType)
|
||||
{
|
||||
case Copier:
|
||||
CopyToCustom(
|
||||
builder,
|
||||
nonNullableTypeName,
|
||||
serializerName,
|
||||
tempVarName,
|
||||
name,
|
||||
isNullable,
|
||||
isClass,
|
||||
isNullableValueType
|
||||
);
|
||||
break;
|
||||
case CopyCreator:
|
||||
CreateCopyCustom(
|
||||
builder,
|
||||
name,
|
||||
tempVarName,
|
||||
nonNullableTypeName,
|
||||
serializerName,
|
||||
nullableValue,
|
||||
nullableOverride
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (isNullable || isNullableValueType)
|
||||
{
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
structCopier.AppendLine($"{name} = {tempVarName}!,");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"target.{name} = {tempVarName}!;");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
{{typeName}} {{tempVarName}} = default!;
|
||||
""");
|
||||
|
||||
if (nullNotAllowed)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
throw new NullNotAllowedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
var hasHooks = ImplementsInterface(type, SerializationHooksNamespace) || !type.IsSealed;
|
||||
builder.AppendLine($$"""
|
||||
if (!serialization.TryCustomCopy(this.{{name}}, ref {{tempVarName}}, hookCtx, {{hasHooks.ToString().ToLower()}}, context))
|
||||
{
|
||||
""");
|
||||
|
||||
if (CanBeCopiedByValue(field.Symbol, field.Type))
|
||||
{
|
||||
builder.AppendLine($"{tempVarName} = {name};");
|
||||
}
|
||||
else if (IsDataDefinition(type) && !type.IsAbstract &&
|
||||
type is not INamedTypeSymbol { TypeKind: TypeKind.Interface })
|
||||
{
|
||||
var nullable = !type.IsValueType || IsNullableType(type);
|
||||
|
||||
if (nullable)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
{{tempVarName}} = null!;
|
||||
}
|
||||
else
|
||||
{
|
||||
""");
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
serialization.CopyTo({{name}}, ref {{tempVarName}}, hookCtx, context{{nullableOverride}});
|
||||
""");
|
||||
|
||||
if (nullable)
|
||||
{
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"{tempVarName} = serialization.CreateCopy({name}, hookCtx, context);");
|
||||
}
|
||||
|
||||
builder.AppendLine("}");
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
structCopier.AppendLine($"{name} = {tempVarName}!,");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"target.{name} = {tempVarName}!;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
target = target with
|
||||
{
|
||||
{{structCopier}}
|
||||
};
|
||||
""");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static void CopyToCustom(
|
||||
StringBuilder builder,
|
||||
string typeName,
|
||||
string serializerName,
|
||||
string tempVarName,
|
||||
string varName,
|
||||
bool isNullable,
|
||||
bool isClass,
|
||||
bool isNullableValueType)
|
||||
{
|
||||
var newTemp = isNullable && isClass ? $"{tempVarName} ??= new();" : string.Empty;
|
||||
var nullableOverride = isClass ? ", true" : string.Empty;
|
||||
var nullableValue = isNullableValueType ? ".Value" : string.Empty;
|
||||
var nonNullableTypeName = typeName.EndsWith("?") ? typeName.Substring(0, typeName.Length - 1) : typeName;
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{nonNullableTypeName}} {{tempVarName}}CopyTo = default!;
|
||||
{{newTemp}}
|
||||
serialization.CopyTo<{{typeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, ref {{tempVarName}}CopyTo, hookCtx, context{{nullableOverride}});
|
||||
{{tempVarName}} = {{tempVarName}}CopyTo;
|
||||
""");
|
||||
}
|
||||
|
||||
private static void CreateCopyCustom(
|
||||
StringBuilder builder,
|
||||
string varName,
|
||||
string tempVarName,
|
||||
string nonNullableTypeName,
|
||||
string serializerName,
|
||||
string nullableValue,
|
||||
string nullableOverride)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
{{tempVarName}} = serialization.CreateCopy<{{nonNullableTypeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, hookCtx, context{{nullableOverride}});
|
||||
""");
|
||||
}
|
||||
}
|
||||
8
Robust.Serialization.Generator/IsExternalInit.cs
Normal file
8
Robust.Serialization.Generator/IsExternalInit.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Runtime.CompilerServices;
|
||||
|
||||
// Need this to be able to define records in the project
|
||||
internal static class IsExternalInit
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.4.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
314
Robust.Serialization.Generator/Types.cs
Normal file
314
Robust.Serialization.Generator/Types.cs
Normal file
@@ -0,0 +1,314 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Robust.Serialization.Generator;
|
||||
|
||||
internal static class Types
|
||||
{
|
||||
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
||||
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
||||
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
||||
private const string CopyByRefNamespace = "Robust.Shared.Serialization.Manager.Attributes.CopyByRefAttribute";
|
||||
|
||||
internal static bool IsPartial(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.Modifiers.IndexOf(SyntaxKind.PartialKeyword) != -1;
|
||||
}
|
||||
|
||||
internal static bool IsDataDefinition(ITypeSymbol? type)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
return HasAttribute(type, DataDefinitionNamespace) ||
|
||||
IsImplicitDataDefinition(type);
|
||||
}
|
||||
|
||||
internal static bool IsDataField(ISymbol member, out ITypeSymbol type, out AttributeData attribute)
|
||||
{
|
||||
// TODO data records and other attributes
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
foreach (var attr in field.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = field.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
foreach (var attr in property.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = property.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = null!;
|
||||
attribute = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsImplicitDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (HasAttribute(baseType, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var @interface in type.AllInterfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsImplicitDataDefinitionInterface(ITypeSymbol @interface)
|
||||
{
|
||||
if (HasAttribute(@interface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var subInterface in @interface.AllInterfaces)
|
||||
{
|
||||
if (HasAttribute(subInterface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static IEnumerable<ITypeSymbol> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
|
||||
{
|
||||
var interfaces = all ? type.AllInterfaces : type.Interfaces;
|
||||
foreach (var @interface in interfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
yield return @interface;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsNullableType(ITypeSymbol type)
|
||||
{
|
||||
if (type.NullableAnnotation == NullableAnnotation.Annotated)
|
||||
return true;
|
||||
|
||||
if (type.OriginalDefinition.ToDisplayString() == "System.Nullable<T>")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsNullableValueType(ITypeSymbol type)
|
||||
{
|
||||
return type.IsValueType && IsNullableType(type);
|
||||
}
|
||||
|
||||
internal static bool IsMultidimensionalArray(ITypeSymbol type)
|
||||
{
|
||||
return type is IArrayTypeSymbol { Rank: > 1 };
|
||||
}
|
||||
|
||||
internal static bool CanBeCopiedByValue(ISymbol member, ITypeSymbol type)
|
||||
{
|
||||
if (type.OriginalDefinition.ToDisplayString() == "System.Nullable<T>")
|
||||
return CanBeCopiedByValue(member, ((INamedTypeSymbol) type).TypeArguments[0]);
|
||||
|
||||
if (type.TypeKind == TypeKind.Enum)
|
||||
return true;
|
||||
|
||||
switch (type.SpecialType)
|
||||
{
|
||||
case SpecialType.System_Enum:
|
||||
case SpecialType.System_Boolean:
|
||||
case SpecialType.System_Char:
|
||||
case SpecialType.System_SByte:
|
||||
case SpecialType.System_Byte:
|
||||
case SpecialType.System_Int16:
|
||||
case SpecialType.System_UInt16:
|
||||
case SpecialType.System_Int32:
|
||||
case SpecialType.System_UInt32:
|
||||
case SpecialType.System_Int64:
|
||||
case SpecialType.System_UInt64:
|
||||
case SpecialType.System_Decimal:
|
||||
case SpecialType.System_Single:
|
||||
case SpecialType.System_Double:
|
||||
case SpecialType.System_String:
|
||||
case SpecialType.System_DateTime:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasAttribute(member, CopyByRefNamespace))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static string GetGenericTypeName(ITypeSymbol symbol)
|
||||
{
|
||||
var name = symbol.Name;
|
||||
|
||||
if (symbol is INamedTypeSymbol { TypeParameters: { Length: > 0 } parameters })
|
||||
{
|
||||
name += "<";
|
||||
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
name += parameter.Name;
|
||||
|
||||
if (i < parameters.Length - 1)
|
||||
{
|
||||
name += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
name += ">";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
internal static string GetPartialTypeDefinitionLine(ITypeSymbol symbol)
|
||||
{
|
||||
var access = symbol.DeclaredAccessibility switch
|
||||
{
|
||||
Accessibility.Private => "private",
|
||||
Accessibility.ProtectedAndInternal => "protected internal",
|
||||
Accessibility.Protected => "protected",
|
||||
Accessibility.Internal => "internal",
|
||||
Accessibility.Public => "public",
|
||||
_ => "public"
|
||||
};
|
||||
|
||||
var typeKeyword = "partial ";
|
||||
if (symbol.TypeKind == TypeKind.Interface)
|
||||
{
|
||||
typeKeyword += "interface";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol.IsRecord)
|
||||
{
|
||||
typeKeyword += symbol.IsValueType ? "record struct" : "record";
|
||||
}
|
||||
else
|
||||
{
|
||||
typeKeyword += symbol.IsValueType ? "struct" : "class";
|
||||
}
|
||||
|
||||
if (symbol.IsAbstract)
|
||||
{
|
||||
typeKeyword = $"abstract {typeKeyword}";
|
||||
}
|
||||
}
|
||||
|
||||
var typeName = GetGenericTypeName(symbol);
|
||||
return $"{access} {typeKeyword} {typeName}";
|
||||
}
|
||||
|
||||
internal static bool Inherits(ITypeSymbol type, string parent)
|
||||
{
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (baseType.ToDisplayString() == parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool ImplementsInterface(ITypeSymbol type, string interfaceName)
|
||||
{
|
||||
foreach (var interfaceType in type.AllInterfaces)
|
||||
{
|
||||
if (interfaceType.ToDisplayString().Contains(interfaceName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
|
||||
{
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
return field.IsReadOnly;
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
if (property.SetMethod == null)
|
||||
return true;
|
||||
|
||||
if (property.SetMethod.IsInitOnly)
|
||||
return type.IsReferenceType;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool NeedsEmptyConstructor(ITypeSymbol type)
|
||||
{
|
||||
if (type is not INamedTypeSymbol named)
|
||||
return false;
|
||||
|
||||
if (named.InstanceConstructors.Length == 0)
|
||||
return true;
|
||||
|
||||
foreach (var constructor in named.InstanceConstructors)
|
||||
{
|
||||
if (constructor.Parameters.Length == 0 &&
|
||||
!constructor.IsImplicitlyDeclared)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool IsVirtualClass(ITypeSymbol type)
|
||||
{
|
||||
return type.IsReferenceType && !type.IsSealed && type.TypeKind != TypeKind.Interface;
|
||||
}
|
||||
|
||||
internal static bool HasAttribute(ISymbol symbol, string attributeName)
|
||||
{
|
||||
foreach (var attribute in symbol.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
yield return baseType;
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,7 +369,6 @@ namespace Robust.Server
|
||||
// otherwise the prototypes will be cleared
|
||||
_prototype.Initialize();
|
||||
_prototype.LoadDefaultPrototypes();
|
||||
_prototype.ResolveResults();
|
||||
_refMan.Initialize();
|
||||
|
||||
IoCManager.Resolve<ToolshedManager>().Initialize();
|
||||
@@ -389,6 +388,11 @@ namespace Robust.Server
|
||||
_protoLoadMan.Initialize();
|
||||
_netResMan.Initialize();
|
||||
|
||||
// String serializer has to be locked before PostInit as content can depend on it (e.g., replays that start
|
||||
// automatically recording on startup).
|
||||
AddFinalStringsToSerializer();
|
||||
_stringSerializer.LockStrings();
|
||||
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);
|
||||
|
||||
_statusHost.Start();
|
||||
@@ -398,9 +402,6 @@ namespace Robust.Server
|
||||
|
||||
_watchdogApi.Initialize();
|
||||
|
||||
AddFinalStringsToSerializer();
|
||||
_stringSerializer.LockStrings();
|
||||
|
||||
if (OperatingSystem.IsWindows() && _config.GetCVar(CVars.SysWinTickPeriod) >= 0)
|
||||
{
|
||||
WindowsTickPeriod.TimeBeginPeriod((uint) _config.GetCVar(CVars.SysWinTickPeriod));
|
||||
|
||||
@@ -59,19 +59,20 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
|
||||
if (_entityManager.TryGetComponent(uid, out FixturesComponent? manager))
|
||||
{
|
||||
foreach (var fixture in manager.Fixtures.Values)
|
||||
foreach (var (id, fixture) in manager.Fixtures)
|
||||
{
|
||||
switch (fixture.Shape)
|
||||
{
|
||||
case EdgeShape edge:
|
||||
physics.SetVertices(uid, fixture, edge,
|
||||
physics.SetVertices(uid, id, fixture,
|
||||
edge,
|
||||
edge.Vertex0 * scale,
|
||||
edge.Vertex1 * scale,
|
||||
edge.Vertex2 * scale,
|
||||
edge.Vertex3 * scale, manager);
|
||||
break;
|
||||
case PhysShapeCircle circle:
|
||||
physics.SetPositionRadius(uid, fixture, circle, circle.Position * scale, circle.Radius * scale, manager);
|
||||
physics.SetPositionRadius(uid, id, fixture, circle, circle.Position * scale, circle.Radius * scale, manager);
|
||||
break;
|
||||
case PolygonShape poly:
|
||||
var verts = poly.Vertices;
|
||||
@@ -81,7 +82,7 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
verts[i] *= scale;
|
||||
}
|
||||
|
||||
physics.SetVertices(uid, fixture, poly, verts, manager);
|
||||
physics.SetVertices(uid, id, fixture, poly, verts, manager);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user