Compare commits

...

117 Commits

Author SHA1 Message Date
DrSmugleaf
33184ecfa5 Version: 154.2.0 2023-09-08 14:44:37 -07:00
Morb
11cf0c1703 Fix turn into Invalid direction (#4350) 2023-09-08 11:14:14 +10:00
DrSmugleaf
528544b7a2 Remove redundant new() constraint from EntitySystem.AddComp (#4353) 2023-09-07 11:10:35 +10:00
chromiumboy
8571d7e7b5 Added method to search nested containers for a specified component (#4337) 2023-09-06 10:24:14 +10:00
DrSmugleaf
0f06423b7a Remove RobustAutoGenerated from partials generated by serialization (#4338) 2023-09-06 10:23:28 +10:00
PrPleGoo
eb9e0ffefc Advertise to multiple hubs simultaneously (#4285) 2023-09-06 00:25:11 +02:00
metalgearsloth
903619ecef Version: 154.1.0 2023-09-05 00:14:15 +10:00
DrSmugleaf
879c6ea538 Make joint initialization only log under IsFirstTimePredicted (#4346) 2023-09-02 21:45:21 -07:00
metalgearsloth
5478545aeb Mark Dirty(comp) as obsolete (#4344) 2023-09-03 06:56:04 +10:00
Wrexbe (Josh)
650929dcbb Add Timespan helpers (#4342) 2023-08-31 11:51:06 -07:00
DrSmugleaf
a289659b49 Version: 154.0.0 2023-08-30 21:22:39 -07:00
DrSmugleaf
85d15c21e1 Move IPlayerData interface to shared (#4339) 2023-08-30 21:17:32 -07:00
DrSmugleaf
bcd1566440 Respect ignored prototypes even if the kind name is registered (#4340) 2023-08-30 21:01:47 -07:00
Julian Giebel
749ac2c364 Fix some multiline edit issues (#4332) 2023-08-30 09:36:36 +10:00
Pieter-Jan Briers
5eed3bc281 Make toolshed stuff oneOff IoC injections.
Removes a ton of IoC injector delegates.
2023-08-29 21:56:57 +02:00
Pieter-Jan Briers
d78f378493 More event sources 2023-08-29 21:43:02 +02:00
DrSmugleaf
eef44c15cf Version: 153.0.0 2023-08-28 16:00:34 -07:00
DrSmugleaf
3d1b2418f9 Remove redundant DebugTools.AssertNotNull(netId) in ClientGameStateManager (#4333) 2023-08-28 15:57:46 -07:00
DrSmugleaf
6b49a86ee5 Make EntityManager.AddComponent with a comp instance set the owner if its default, add system proxy (#4328) 2023-08-28 15:31:17 -07:00
metalgearsloth
cd13cd3cd8 Delete EntityDeletedMessage (#4329) 2023-08-29 05:21:29 +10:00
metalgearsloth
2b8d8d6636 Remove UI comprefs (#4320) 2023-08-28 03:49:57 +10:00
Pieter-Jan Briers
409fe1a125 Some warning fixes 2023-08-27 15:35:15 +02:00
Pieter-Jan Briers
ab5db4641c Update Lidgren to v0.2.6 2023-08-27 13:18:19 +02:00
metalgearsloth
064e8ee365 Minor CompAdd stuff (#4327) 2023-08-27 12:54:09 +02:00
metalgearsloth
02dcff7eae Remove CollisionWake comp removal sub (#4326) 2023-08-27 15:47:46 +10:00
metalgearsloth
e1e5f8de54 Fix master build (#4325) 2023-08-27 15:33:15 +10:00
Leon Friedrich
d5ba822a79 Remove redundant prototype resolving (#4322) 2023-08-27 15:24:25 +10:00
Leon Friedrich
f448c6b8fa Add RecursiveMoveBenchmark (#4323) 2023-08-27 15:24:04 +10:00
Pieter-Jan Briers
5e1d80be35 Attempts to fix replay recording performance issues.
Replays now use a dedicated thread (rather than thread pool) for write operations.

Moved batch operations to this thread as well. They were previously happening during PVS. Looking at some trace files these compression ops can easily take 5+ ms in some cases, so moving them somewhere else is appreciated.

Added EventSource instrumentation for PVS and replay recording.
2023-08-27 02:15:15 +02:00
DrSmugleaf
01546f32da Version: 152.0.0 2023-08-26 15:31:30 -07:00
DrSmugleaf
aeeaaaefc5 Fix not running hooks when copying non-byref data definition fields without a custom serializer (#4324) 2023-08-26 15:20:46 -07:00
Leon Friedrich
b6c8060af1 Add new PVS test (#4312) 2023-08-26 22:23:32 +10:00
DrSmugleaf
99685838da Fix entity spawn tests having instance per test lifecycle with non static setup and tear downs (#4321) 2023-08-26 22:16:47 +10:00
ike709
8917b29255 Convert Tile.TypeId to an int (#4307)
Co-authored-by: ike709 <ike709@github.com>
Co-authored-by: DrSmugleaf <drsmugleaf@gmail.com>
2023-08-26 22:16:14 +10:00
DrSmugleaf
f0c4d7c5eb Update CI to use setup-dotnet 3.2.0 and checkout 3.6.0 (#4319) 2023-08-25 15:45:19 -07:00
Pieter-Jan Briers
6a00c62d3c Allow content to implement own logic for BUI range checks. (#4301)
They were currently inconsistent with interaction logic in SS14. Please fix and thank.
2023-08-26 09:29:43 +12:00
metalgearsloth
fc3116fca5 Remove ComponentDeleted C# event (#4317)
No one's used it for 12 years probably no reason to obs first.
2023-08-26 09:24:33 +12:00
metalgearsloth
98c1397b3a Remove EntityStarted C# event (#4318) 2023-08-25 19:57:43 +02:00
Leon Friedrich
2464bb6c2f Remove and obsolete ComponentExt functions (#4313) 2023-08-25 23:20:39 +10:00
Leon Friedrich
709142acee Fix prototype manager not being initialized in tests (#4294) 2023-08-24 19:02:02 +02:00
Kevin Zheng
af4e3e5e1c Remove personally-identifiable file paths from client logs (#4267) 2023-08-24 18:56:48 +02:00
Kevin Zheng
d51a18c6ea Fix build with USE_SYSTEM_SQLITE (#4266) 2023-08-24 18:55:54 +02:00
metalgearsloth
d5c3d4c0c9 Add system to CompNetworkGenerator (#4310)
Robust doesn't global using this but content does so any automatic comp states on engine don't work.
2023-08-24 04:26:30 -07:00
metalgearsloth
a4474d8df8 Remove IContainerManager (#4308) 2023-08-24 16:39:35 +10:00
DrSmugleaf
d66f7c7c06 Disable obsoletion and inherited member hidden warnings in serialization source generated code (#4302) 2023-08-24 13:04:32 +10:00
DrSmugleaf
b6879869d6 Add support for long values in CVars (#4299) 2023-08-24 00:57:09 +02:00
Errant
815b8e0c48 removed warning for glibc (#4296) 2023-08-24 00:56:38 +02:00
Arimah Greene
ef4e3baa7f Fix Timer drift (#4300) 2023-08-24 00:54:30 +02:00
Moony
270ddb5a53 Update CODEOWNERS 2023-08-23 16:23:38 -05:00
moonheart08
6133fe0808 Version: 151.0.0 2023-08-23 16:05:56 -05:00
Moony
909fd326a0 Toolshed part 2 (#4256)
* Save work.

* three billion tweaks

* Rune-aware parser.

* a

* all shedded out for the night

* a

* oogh

* Publicizes a lot of common generic commands, so custom toolshed envs can include them.

* Implement parsing for all number types.

* i think i might implode

* a

* Tests.

* a

* Enum parser test.

* do u like parsers

* oopls

* ug fixes

* Toolshed is approaching a non-insignificant part of the engine's size.

* Pool toolshed's tests, also type tests.

* bwa

* tests pass :yay:

* Update Robust.Shared/CVars.cs

Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>

* how did this not fail tests

* awa

* many levels of silly

---------

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
2023-08-23 16:03:34 -05:00
metalgearsloth
876de4065a Version: 150.0.1 2023-08-23 19:16:17 +10:00
metalgearsloth
60e159f0d0 Fix some merge artifacts (#4297) 2023-08-23 19:15:29 +10:00
metalgearsloth
80f3aae30c Version: 150.0.0 2023-08-23 18:55:43 +10:00
Leon Friedrich
98b1862433 Add new spawn functions (#4280) 2023-08-23 18:54:55 +10:00
Leon Friedrich
d2311c193f Add AbstractDictionarySerializer (#4276) 2023-08-23 18:53:13 +10:00
metalgearsloth
f05ed96461 Remove FixtureId from fixtures (#4270) 2023-08-23 18:50:48 +10:00
DrSmugleaf
dc23dfaf4d Version: 149.0.1 2023-08-23 00:27:59 -07:00
DmitriyMX
62315f7c2e fix: crash client when window set maxsize (#4291) 2023-08-23 16:59:57 +10:00
DrSmugleaf
b2d121e780 Fix serialization sharing instances when copying data definitions and not assigning null when the source is null (#4295) 2023-08-22 23:59:03 -07:00
DrSmugleaf
fb4b029122 Version: 149.0.0 2023-08-22 18:07:35 -07:00
DrSmugleaf
66239d23ea Refactor serialization copying to use source generators (#4286) 2023-08-22 17:37:13 -07:00
DrSmugleaf
dbb45f1c13 Version: 148.4.0 2023-08-21 23:19:25 -07:00
Leon Friedrich
6284e16b64 Add recursive PVS overrides and remove IsOverride() (#4262) 2023-08-21 23:17:53 -07:00
DrSmugleaf
f6c55085fe Version: 148.3.0 2023-08-21 19:01:15 -07:00
DrSmugleaf
30eafd26e7 Fix test checking that Robust's and .NET's colors are equal (#4287) 2023-08-21 16:26:06 -07:00
Pieter-Jan Briers
63423d96b4 Fixes for new color PR (#4278)
Undo change to violet color

add to named color list
2023-08-21 23:06:20 +02:00
Morb
474334aff2 Make DiscordRichPresence icon CVars server-side with replication (#4272) 2023-08-21 10:44:40 +02:00
Leon Friedrich
be102f86bf Add IntegrationInstance fields for common dependencies (#4283) 2023-08-21 14:35:27 +10:00
Tom Leys
d7962c7190 Add implementation of Random.Pick(ValueList<T> ..) (#4257) 2023-08-21 13:56:18 +10:00
Leon Friedrich
7fe9385c3b Change default value of EntityLastModifiedTick from zero to one. (#4282)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2023-08-21 13:55:41 +10:00
Kara
a9d9d1348a Tile texture reload command (#4268) 2023-08-21 13:46:58 +10:00
Leon Friedrich
4eaf624555 Allow pre-startup components to be shut down. (#4281) 2023-08-21 13:45:57 +10:00
Leon Friedrich
e37c131fb4 Prevent invalid prototypes from being spawned (#4279) 2023-08-21 12:29:02 +10:00
PrPleGoo
9df4606492 Added colors (#4278) 2023-08-20 18:48:00 +02:00
Pieter-Jan Briers
3be8070274 Happy eyeballs delay can be configured. 2023-08-20 17:45:43 +02:00
metalgearsloth
8a440d705f Version: 148.2.0 2023-08-20 15:53:35 +10:00
metalgearsloth
5849474022 Add IsPaused to EntityManager (#4277) 2023-08-20 15:50:11 +10:00
c4llv07e
b7d67c0ece Fix UserInterface.SetActiveTheme didn't update theme (#4263) 2023-08-20 03:36:38 +10:00
Pieter-Jan Briers
b04cf71bc0 Expose SpinBox.LineEditControl 2023-08-19 16:32:05 +02:00
Leon Friedrich
ea87df649a Add readonly VV attributes to various fields. (#4265) 2023-08-20 00:13:18 +10:00
metalgearsloth
dab7a9112f Version: 148.1.0 2023-08-16 19:19:46 +10:00
DrSmugleaf
d3dc89832a Add component that lets entities ignore BUI range checks (#4264) 2023-08-16 15:41:01 +10:00
Leon Friedrich
3ade9ca447 Add support for f16-f24 keys (#4261) 2023-08-13 22:15:18 +10:00
Leon Friedrich
149e9a2613 Fix a gamestate bug. (#4260) 2023-08-13 11:22:15 +10:00
ElectroJr
d164148ce2 Version: 148.0.0 2023-08-12 19:40:55 -04:00
/ʊniɹɑː/
d898b52449 more richtext (#4187) 2023-08-13 09:34:44 +10:00
TemporalOroboros
dcf7a1e580 PixelToMap (#4188) 2023-08-13 09:34:33 +10:00
Pieter-Jan Briers
65c6bb74eb Mark a bunch of NuGet dependencies as private compile assets (#4258) 2023-08-13 07:22:14 +10:00
Leon Friedrich
d6d88bea91 Fix replay handling of bad prototype uploads (#4259) 2023-08-13 07:07:47 +10:00
Pieter-Jan Briers
d6467f768a Fix Logger calls in ComponentRegistrySerializer 2023-08-11 23:50:54 +02:00
ElectroJr
4f0f020f56 Version: 147.0.0 2023-08-10 00:40:01 -04:00
Leon Friedrich
5ce8369fb9 Rename a Dirty() proxy method to DirtyEntity() (#4253) 2023-08-10 14:17:57 +10:00
Pieter-Jan Briers
2446e64033 entitysystemupdateorder debug command 2023-08-08 21:36:14 +02:00
metalgearsloth
bdd65cda4b Version: 146.0.0 2023-08-08 17:26:42 +10:00
Leon Friedrich
77e949bfe8 More serialization related changes, (#4250) 2023-08-08 17:22:10 +10:00
metalgearsloth
d4171351f4 Metadata + cancollide stuff (#4247) 2023-08-08 12:03:27 +10:00
metalgearsloth
e67d0ad3d6 Xform stuff (#4246) 2023-08-08 11:55:56 +10:00
Artur
aff5711fde Add missing CultureInfo.InvariantCulture in angle validaton (#4248) 2023-08-08 11:52:24 +10:00
ElectroJr
a886222946 Version: 145.0.0 2023-08-06 21:45:09 -04:00
Leon Friedrich
5843f1087e Add ContentFileRead test and fix file reading on windows (#4242) 2023-08-07 11:39:09 +10:00
Leon Friedrich
93c0ce815f Add IPrototypeManager.EnumerateKinds() (#4244) 2023-08-07 11:24:34 +10:00
Leon Friedrich
1c64fa1f28 Fix TransformSystem.SetCoordinates() error logs. (#4245) 2023-08-07 11:24:26 +10:00
Leon Friedrich
c825c1e413 Remove IoCManager.Resolve calls in Resource.Load (#4243) 2023-08-07 11:24:15 +10:00
Chief-Engineer
f30fb47834 Add GetActorFromUserId to actor system (#4239) 2023-08-07 11:24:00 +10:00
Leon Friedrich
5d255e06c8 Fix SpriteSpecifier yaml validator (#4241) 2023-08-06 22:14:02 +10:00
metalgearsloth
80357c8ec4 Version: 144.0.1 2023-08-06 15:07:48 +10:00
metalgearsloth
ac3a434bdf Shrink entitylookup tile enlargement even further (#4240) 2023-08-06 15:06:08 +10:00
metalgearsloth
21719b8884 Version: 144.0.0 2023-08-06 12:45:49 +10:00
metalgearsloth
dbb6b90654 Tile enlargement + new flag for lookups (#4205) 2023-08-06 12:41:27 +10:00
ElectroJr
4b92be5324 Version: 143.3.0 2023-08-05 21:16:11 -04:00
Leon Friedrich
95169b7a71 Add temporary debug logs (#4237) 2023-08-06 11:11:23 +10:00
Leon Friedrich
b699e22c85 More serialization fixes (#4224) 2023-08-06 11:08:48 +10:00
Leon Friedrich
e48cc62d0b Clamp audio offset in ApplyAudioParams() (#4221) 2023-08-06 10:38:53 +10:00
Leon Friedrich
aade062a49 Allow replay loading to ignore some errors (#4236) 2023-08-06 10:33:31 +10:00
Chief-Engineer
e02166d5c4 add placement events (#4235) 2023-08-06 10:32:04 +10:00
Leon Friedrich
8037bfae14 Remove an incorrect assert (#4238) 2023-08-06 10:29:30 +10:00
495 changed files with 12662 additions and 3450 deletions

5
.github/CODEOWNERS vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -25,4 +25,7 @@
<!-- analyzer -->
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
<!-- serialization generator -->
<Import Project="Robust.Serialization.Generator.targets" />
</Project>

View 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>

View File

@@ -54,11 +54,312 @@ END TEMPLATE-->
*None yet*
## 154.2.0
### 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
* Renamed one of the EntitySystem.Dirty() methods to `DirtyEntity()` to avoid confusion with the component-dirtying methods.
### New features
* Added debug commands that return the entity system update order.
### Bugfixes
* Fixed a bug in MetaDataSystem that was causing the metadata component to not be marked as dirty.
## 146.0.0
### Breaking changes
* Remove readOnly for DataFields and rename some ShaderPrototype C# fields internally to align with the normal schema.
### Bugfixes
* Add InvariantCulture to angle validation.
### Internal
* Add some additional EntityQuery<T> usages and remove a redundant CanCollide call on fixture shutdown.
## 145.0.0
### Breaking changes
* Removed some old SpriteComponent data-fields ("rsi", and "layerDatums").
### New features
* Added `ActorSystem.TryGetActorFromUserId()`.
* Added IPrototypeManager.EnumerateKinds().
### Bugfixes
* Fixed SpriteSpecifierSerializer yaml validation not working properly.
* Fixed IoC/Threading exceptions in `Resource.Load()`.
* Fixed `TransformSystem.SetCoordinates()` throwing uninformative client-side errors.
* Fixed `IResourceManager.ContentFileExists()` and `TryContentFileRead()` throwing exceptions on windows when trying to open a directory.
## 144.0.1
### Bugfixes
* Fix some EntityLookup queries incorrectly being double transformed internally.
* Shrink TileEnlargement even further for EntityLookup default queries.
## 144.0.0
### Breaking changes
* Add new args to entitylookup methods to allow for shrinkage of tile-bounds checks. Default changed to shrink the grid-local AABB by the polygon skin to avoid clipping neighboring tile entities.
* Non-hard fixtures will no longer count by default for EntityLookup.
### New features
* Added new EntityLookup flag to return non-hard fixtures or not.
## 143.3.0
### New features
* Entity placement and spawn commands now raise informative events that content can handle.
* Replay clients can now optionally ignore some errors instead of refusing to load the replay.
### Bugfixes
* `AudioParams.PlayOffsetSecond` will no longer apply an offset that is larger then the length of the audio stream.
* Fixed yaml serialization of arrays of virtual/abstract objects.
### Other
* Removed an incorrect gamestate debug assert.
## 143.2.0
### New features
* Add support for tests to load extra prototypes.
* Add support for tests to load extra prototypes from multiple sources.
### Bugfixes
@@ -117,7 +418,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.
@@ -126,7 +427,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

View File

@@ -1,6 +1,6 @@
- type: uiTheme
id: Default
path: /Textures/Interface/Default
path: /Textures/Interface/Default/
colors:
# Root
rootBackground: "#000000"

View File

@@ -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

View File

@@ -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

View File

@@ -161,3 +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.

View 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;
}
}
}

View 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);
}
}

View File

@@ -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.");

View File

@@ -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
{
}
}

View File

@@ -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
{
}
}

View File

@@ -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
{
}
}

View File

@@ -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
{
}
}

View File

@@ -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!;
}
}

View File

@@ -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!;
}
}

View File

@@ -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;

View 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;
}
}

View File

@@ -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>();

View File

@@ -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!;
}

View File

@@ -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!;
}

View File

@@ -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))
{

View File

@@ -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))
{

View File

@@ -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;

View File

@@ -167,7 +167,6 @@ namespace Robust.Client
_reflectionManager.Initialize();
_prototypeManager.Initialize();
_prototypeManager.LoadDefaultPrototypes();
_prototypeManager.ResolveResults();
_userInterfaceManager.Initialize();
_eyeManager.Initialize();
_entityManager.Initialize();

View File

@@ -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 ._.

View File

@@ -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.

View File

@@ -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!;

View File

@@ -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!;
}

View File

@@ -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.

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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!;
@@ -167,40 +167,9 @@ namespace Robust.Client.GameObjects
public bool TreeUpdateQueued { get; set; }
[DataField("layerDatums")]
private List<PrototypeLayerData> LayerDatums
{
get
{
var layerDatums = new List<PrototypeLayerData>();
foreach (var layer in Layers)
{
layerDatums.Add(layer.ToPrototypeData());
}
return layerDatums;
}
set
{
if (value == null) return;
Layers.Clear();
foreach (var layerDatum in value)
{
AddLayer(layerDatum);
}
_layerMapShared = true;
QueueUpdateRenderTree();
QueueUpdateIsInert();
}
}
private RSI? _baseRsi;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("rsi", priority: 2)]
public RSI? BaseRSI
{
get => _baseRsi;
@@ -357,7 +326,16 @@ namespace Robust.Client.GameObjects
if (layerDatums.Count != 0)
{
LayerMap.Clear();
LayerDatums = layerDatums;
Layers.Clear();
foreach (var datum in layerDatums)
{
AddLayer(datum);
}
_layerMapShared = true;
QueueUpdateRenderTree();
QueueUpdateIsInert();
}
UpdateLocalMatrix();

View File

@@ -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();

View File

@@ -314,9 +314,9 @@ public sealed class AudioSystem : SharedAudioSystem
return source != null;
}
private PlayingStream CreateAndStartPlayingStream(IClydeAudioSource source, AudioParams? audioParams)
private PlayingStream CreateAndStartPlayingStream(IClydeAudioSource source, AudioParams? audioParams, AudioStream stream)
{
ApplyAudioParams(audioParams, source);
ApplyAudioParams(audioParams, source, stream);
source.StartPlaying();
var playing = new PlayingStream
{
@@ -365,7 +365,7 @@ public sealed class AudioSystem : SharedAudioSystem
source.SetGlobal();
return CreateAndStartPlayingStream(source, audioParams);
return CreateAndStartPlayingStream(source, audioParams, stream);
}
/// <summary>
@@ -416,7 +416,7 @@ public sealed class AudioSystem : SharedAudioSystem
if (!source.SetPosition(worldPos))
return Play(stream, fallbackCoordinates.Value, fallbackCoordinates.Value, audioParams);
var playing = CreateAndStartPlayingStream(source, audioParams);
var playing = CreateAndStartPlayingStream(source, audioParams, stream);
playing.TrackingEntity = entity;
playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null;
return playing;
@@ -469,7 +469,7 @@ public sealed class AudioSystem : SharedAudioSystem
return null;
}
var playing = CreateAndStartPlayingStream(source, audioParams);
var playing = CreateAndStartPlayingStream(source, audioParams, stream);
playing.TrackingCoordinates = coordinates;
playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null;
return playing;
@@ -493,7 +493,7 @@ public sealed class AudioSystem : SharedAudioSystem
return null;
}
private void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source)
private void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source, AudioStream audio)
{
if (!audioParams.HasValue)
return;
@@ -508,8 +508,12 @@ public sealed class AudioSystem : SharedAudioSystem
source.SetRolloffFactor(audioParams.Value.RolloffFactor);
source.SetMaxDistance(audioParams.Value.MaxDistance);
source.SetReferenceDistance(audioParams.Value.ReferenceDistance);
source.SetPlaybackPosition(audioParams.Value.PlayOffsetSeconds);
source.IsLooping = audioParams.Value.Loop;
// TODO clamp the offset inside of SetPlaybackPosition() itself.
var offset = audioParams.Value.PlayOffsetSeconds;
offset = Math.Clamp(offset, 0f, (float) audio.Length.TotalSeconds);
source.SetPlaybackPosition(offset);
}
public sealed class PlayingStream : IPlayingAudioStream

View File

@@ -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;

View File

@@ -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");
}
}

View File

@@ -500,7 +500,6 @@ namespace Robust.Client.GameStates
foreach (var (netId, comp) in netComps.Value)
{
DebugTools.AssertNotNull(netId);
if (!comp.NetSyncEnabled)
continue;

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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},
};

View File

@@ -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>();

View File

@@ -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!;

View File

@@ -225,7 +225,7 @@ namespace Robust.Client.Graphics
}
[DataDefinition]
public struct StencilParameters
public partial struct StencilParameters
{
public StencilParameters()
{

View File

@@ -20,8 +20,8 @@ namespace Robust.Client.Graphics
public sealed class ShaderPrototype : IPrototype, ISerializationHooks
{
[ViewVariables]
[IdDataFieldAttribute]
public string ID { get; } = default!;
[IdDataField]
public string ID { get; private set; } = default!;
[ViewVariables] private ShaderKind Kind;
[ViewVariables] private Dictionary<string, object>? _params;
@@ -65,12 +65,12 @@ namespace Robust.Client.Graphics
case ShaderKind.Canvas:
var hasLight = rawMode != "unshaded";
var hasLight = _rawMode != "unshaded";
ShaderBlendMode? blend = null;
if (rawBlendMode != null)
if (_rawBlendMode != null)
{
if (!Enum.TryParse<ShaderBlendMode>(rawBlendMode.ToUpper(), out var parsed))
Logger.Error($"invalid mode: {rawBlendMode}");
if (!Enum.TryParse<ShaderBlendMode>(_rawBlendMode.ToUpper(), out var parsed))
Logger.Error($"invalid mode: {_rawBlendMode}");
else
blend = parsed;
}
@@ -94,11 +94,20 @@ namespace Robust.Client.Graphics
return Instance().Duplicate();
}
[DataField("kind", readOnly: true, required: true)] private string _rawKind = default!;
[DataField("path", readOnly: true)] private ResPath path;
[DataField("params", readOnly: true)] private Dictionary<string, string>? paramMapping;
[DataField("light_mode", readOnly: true)] private string? rawMode;
[DataField("blend_mode", readOnly: true)] private string? rawBlendMode;
[DataField("kind", required: true)]
private string _rawKind = default!;
[DataField("path")]
private ResPath? _path;
[DataField("params")]
private Dictionary<string, string>? _paramMapping;
[DataField("light_mode")]
private string? _rawMode;
[DataField("blend_mode")]
private string? _rawBlendMode;
void ISerializationHooks.AfterDeserialization()
{
@@ -106,18 +115,22 @@ namespace Robust.Client.Graphics
{
case "source":
Kind = ShaderKind.Source;
if (path == null) throw new InvalidOperationException();
_source = IoCManager.Resolve<IResourceCache>().GetResource<ShaderSourceResource>(path);
if (paramMapping != null)
// TODO use a custom type serializer.
if (_path == null)
throw new InvalidOperationException("Source shaders must specify a source file.");
_source = IoCManager.Resolve<IResourceCache>().GetResource<ShaderSourceResource>(_path.Value);
if (_paramMapping != null)
{
_params = new Dictionary<string, object>();
foreach (var item in paramMapping!)
foreach (var item in _paramMapping!)
{
var name = item.Key;
if (!_source.ParsedShader.Uniforms.TryGetValue(name, out var uniformDefinition))
{
Logger.ErrorS("shader", "Shader param '{0}' does not exist on shader '{1}'", name, path);
Logger.ErrorS("shader", "Shader param '{0}' does not exist on shader '{1}'", name, _path);
continue;
}

View File

@@ -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);

View File

@@ -161,6 +161,15 @@ namespace Robust.Client.Input
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
Pause,
World1,
}

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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
{
}

View File

@@ -532,7 +532,7 @@ namespace Robust.Client.Placement
return false;
}
coordinates = EntityCoordinates.FromMap(MapManager,
EyeManager.ScreenToMap(InputManager.MouseScreenPosition));
EyeManager.PixelToMap(InputManager.MouseScreenPosition));
return true;
}
}

View File

@@ -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);

View File

@@ -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>

View File

@@ -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 />

View File

@@ -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;
@@ -131,12 +132,16 @@ public sealed partial class ReplayLoadManager
var ticksSinceLastCheckpoint = 0;
var spawnedTracker = 0;
var stateTracker = 0;
var curState = state0;
for (var i = 1; i < states.Count; i++)
{
if (i % 10 == 0)
await callback(i, states.Count, LoadingState.ProcessingFiles, false);
var curState = states[i];
var lastState = curState;
curState = states[i];
DebugTools.Assert(curState.FromSequence <= lastState.ToSequence);
UpdatePlayerStates(curState.PlayerStates.Span, playerStates);
UpdateEntityStates(curState.EntityStates.Span, entStates, ref spawnedTracker, ref stateTracker, detached);
UpdateMessages(messages[i], uploadedFiles, prototypes, cvars, detachQueue, ref timeBase);
@@ -222,7 +227,13 @@ public sealed partial class ReplayLoadManager
// forwards. Also, note that files HAVE to be uploaded while generating checkpoints, in case
// someone spawns an entity that relies on uploaded data.
if (!ignoreDuplicates)
throw new NotSupportedException("Overwriting an existing file is not yet supported by replays.");
{
var msg = $"Overwriting an existing file upload! Path: {path}";
if (_confMan.GetCVar(CVars.ReplayIgnoreErrors))
_sawmill.Error(msg);
else
throw new NotSupportedException(msg);
}
message.Messages.RemoveSwap(i);
break;
@@ -240,31 +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?
throw new NotSupportedException($"Overwriting an existing prototype is not yet supported by replays.");
}
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)
{

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Robust.Client.GameStates;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -64,6 +66,18 @@ public sealed partial class ReplayLoadManager
}
}
throw new Exception("Missing metadata component");
if (!entState.ComponentChanges.HasContents)
{
// This shouldn't be possible, yet it has happened?
// TODO this should probably also throw an exception.
_sawmill.Error($"Encountered blank entity state? Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}. Attempting to continue.");
return null;
}
if (!_confMan.GetCVar(CVars.ReplayIgnoreErrors))
throw new MissingMetadataException(entState.Uid);
_sawmill.Error($"Missing metadata component. Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}.");
return null;
}
}

View File

@@ -11,6 +11,7 @@ using Robust.Shared.Utility;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Robust.Shared;
using Robust.Shared.Replays;
using static Robust.Shared.Replays.ReplayConstants;
@@ -136,9 +137,21 @@ public sealed partial class ReplayLoadManager
if (data == null)
throw new Exception("Failed to load yaml metadata");
TimeSpan duration;
var finalData = LoadYamlFinalMetadata(fileReader);
if (finalData == null)
throw new Exception("Failed to load final yaml metadata");
{
var msg = "Failed to load final yaml metadata";
if (!_confMan.GetCVar(CVars.ReplayIgnoreErrors))
throw new Exception(msg);
_sawmill.Error(msg);
duration = TimeSpan.FromDays(1);
}
else
{
duration = TimeSpan.Parse(((ValueDataNode) finalData[MetaFinalKeyDuration]).Value);
}
var typeHash = Convert.FromHexString(((ValueDataNode) data[MetaKeyTypeHash]).Value);
var stringHash = Convert.FromHexString(((ValueDataNode) data[MetaKeyStringHash]).Value);
@@ -146,7 +159,6 @@ public sealed partial class ReplayLoadManager
var timeBaseTick = ((ValueDataNode) data[MetaKeyBaseTick]).Value;
var timeBaseTimespan = ((ValueDataNode) data[MetaKeyBaseTime]).Value;
var clientSide = bool.Parse(((ValueDataNode) data[MetaKeyIsClientRecording]).Value);
var duration = TimeSpan.Parse(((ValueDataNode) finalData[MetaFinalKeyDuration]).Value);
if (!typeHash.SequenceEqual(_serializer.GetSerializableTypesHash()))
throw new Exception($"{nameof(IRobustSerializer)} hashes do not match. Loading replays using a bad replay-client version?");

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Client.Graphics;
using Robust.Shared.ContentPack;
using Robust.Shared.Utility;
@@ -40,5 +41,9 @@ namespace Robust.Client.ResourceManagement
// Resource load callbacks so content can hook stuff like click maps.
event Action<TextureLoadedEventArgs> OnRawTextureLoaded;
event Action<RsiLoadedEventArgs> OnRsiLoaded;
IClyde Clyde { get; }
IClydeAudio ClydeAudio { get; }
IFontManager FontManager { get; }
}
}

View File

@@ -18,7 +18,9 @@ namespace Robust.Client.ResourceManagement
{
internal partial class ResourceCache
{
[Dependency] private readonly IClyde _clyde = default!;
[field: Dependency] public IClyde Clyde { get; } = default!;
[field: Dependency] public IClydeAudio ClydeAudio { get; } = default!;
[field: Dependency] public IFontManager FontManager { get; } = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
@@ -70,7 +72,7 @@ namespace Robust.Client.ResourceManagement
try
{
TextureResource.LoadTexture(_clyde, data);
TextureResource.LoadTexture(Clyde, data);
}
catch (Exception e)
{
@@ -198,7 +200,7 @@ namespace Robust.Client.ResourceManagement
void FinalizeMetaAtlas(int toIndex, Image<Rgba32> sheet)
{
var atlas = _clyde.LoadTextureFromImage(sheet);
var atlas = Clyde.LoadTextureFromImage(sheet);
for (int i = finalized + 1; i <= toIndex; i++)
{
var rsi = rsiList[i];

View File

@@ -20,14 +20,13 @@ namespace Robust.Client.ResourceManagement
using (var fileStream = cache.ContentFileRead(path))
{
var clyde = IoCManager.Resolve<IClydeAudio>();
if (path.Extension == "ogg")
{
AudioStream = clyde.LoadAudioOggVorbis(fileStream, path.ToString());
AudioStream = cache.ClydeAudio.LoadAudioOggVorbis(fileStream, path.ToString());
}
else if (path.Extension == "wav")
{
AudioStream = clyde.LoadAudioWav(fileStream, path.ToString());
AudioStream = cache.ClydeAudio.LoadAudioWav(fileStream, path.ToString());
}
else
{

View File

@@ -18,7 +18,7 @@ namespace Robust.Client.ResourceManagement
using (stream)
{
FontFaceHandle = IoCManager.Resolve<IFontManagerInternal>().Load(stream);
FontFaceHandle = ((IFontManagerInternal)cache.FontManager).Load(stream);
}
}

View File

@@ -34,12 +34,10 @@ namespace Robust.Client.ResourceManagement
public override void Load(IResourceCache cache, ResPath path)
{
var clyde = IoCManager.Resolve<IClyde>();
var loadStepData = new LoadStepData {Path = path};
LoadPreTexture(cache, loadStepData);
loadStepData.AtlasTexture = clyde.LoadTextureFromImage(
loadStepData.AtlasTexture = cache.Clyde.LoadTextureFromImage(
loadStepData.AtlasSheet,
loadStepData.Path.ToString());

View File

@@ -1,6 +1,7 @@
using System.IO;
using System.Threading;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Clyde;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
@@ -27,8 +28,7 @@ namespace Robust.Client.ResourceManagement
ParsedShader = ShaderParser.Parse(reader, cache);
}
var clyde = IoCManager.Resolve<IClydeInternal>();
ClydeHandle = clyde.LoadShader(ParsedShader, path.ToString());
ClydeHandle = ((IClydeInternal)cache.Clyde).LoadShader(ParsedShader, path.ToString());
}
public override void Reload(IResourceCache cache, ResPath path, CancellationToken ct = default)
@@ -57,8 +57,7 @@ namespace Robust.Client.ResourceManagement
}
}
var clyde = IoCManager.Resolve<IClydeInternal>();
clyde.ReloadShader(ClydeHandle, ParsedShader);
((IClydeInternal)cache.Clyde).ReloadShader(ClydeHandle, ParsedShader);
}
}
}

View File

@@ -20,8 +20,6 @@ namespace Robust.Client.ResourceManagement
public override void Load(IResourceCache cache, ResPath path)
{
var clyde = IoCManager.Resolve<IClyde>();
if (path.Directory.Filename.EndsWith(".rsi"))
{
Logger.WarningS(
@@ -33,7 +31,7 @@ namespace Robust.Client.ResourceManagement
var data = new LoadStepData {Path = path};
LoadPreTexture(cache, data);
LoadTexture(clyde, data);
LoadTexture(cache.Clyde, data);
LoadFinish(cache, data);
}

View File

@@ -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="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.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="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>

View File

@@ -1,5 +1,4 @@
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -29,7 +28,7 @@ public sealed class ClientSpriteSpecifierSerializer : SpriteSpecifierSerializer
}
var res = dependencies.Resolve<IResourceCache>();
var rsiPath = SpriteSpecifierSerializer.TextureRoot / valuePathNode.Value;
var rsiPath = TextureRoot / valuePathNode.Value;
if (!res.TryGetResource(rsiPath, out RSIResource? resource))
{
return new ErrorNode(node, "Failed to load RSI");
@@ -40,6 +39,10 @@ public sealed class ClientSpriteSpecifierSerializer : SpriteSpecifierSerializer
return new ErrorNode(node, "Invalid RSI state");
}
return new ValidatedMappingNode(new());
return new ValidatedMappingNode(new()
{
{ new ValidatedValueNode(new ValueDataNode("sprite")), new ValidatedValueNode(pathNode)},
{ new ValidatedValueNode(new ValueDataNode("state")), new ValidatedValueNode(valueStateNode)},
});
}
}

View File

@@ -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),

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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);
}

View 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();
}
}

View File

@@ -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();

View 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 _) => " · ";
}

View File

@@ -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";

View File

@@ -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!;
}

View File

@@ -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))
{

View 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();
}
}

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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)
{

View File

@@ -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)

View File

@@ -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)

View 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);

View 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();
}
}

Some files were not shown because too many files have changed in this diff Show More