Compare commits

...

372 Commits

Author SHA1 Message Date
Pieter-Jan Briers
b65e0c64ea Version: 226.2.2 2024-08-11 19:54:41 +02:00
Pieter-Jan Briers
4c388bc03d Use absolute path for explorer.exe
frick me

(cherry picked from commit 0284eb0430)
2024-08-11 19:54:41 +02:00
Pieter-Jan Briers
658dee1591 Version: 226.2.1 2024-08-11 17:54:19 +02:00
Pieter-Jan Briers
b6e5cca127 Security updates (#5353)
* Fix security bug in WritableDirProvider.OpenOsWindow()

Reported by @NarryG and @nyeogmi

* Sandbox updates

* Update ImageSharp again

(cherry picked from commit 7d778248ee)
(cherry picked from commit f66cda74e95619ddba2221bda644bf4394619805)
2024-08-11 17:54:19 +02:00
metalgearsloth
da5416a2da Version: 226.2.0 2024-06-20 17:28:11 +10:00
metalgearsloth
021845d956 Add some System.Random methods (#5177)
* Add some System.Random methods

* weh
2024-06-20 17:23:47 +10:00
Leon Friedrich
7fab9f3b8d Fix ContainerSystem debug assert (#5254) 2024-06-20 17:23:38 +10:00
Pieter-Jan Briers
69c1161562 FormattedMessage/DebugConsole performance improvements (#5244)
* Add VisibilityChanged virtual to Control

* Defer updating invisible OutputPanels on UIScale change

DebugConsole falls under this when not hidden, and it significantly improves perf of e.g. resizing the window when there's a lot of stuff in there.

* Avoid redundant UI Scale updates on window resize.

Window resizing can change the UI scale, due to the auto-scaling system. This system had multiple perf issues:

UI scale was set and propagated even if it didn't change (system disabled, not effective, etc). This was just wasted processing.

UI scale was updated for every window resize event. When the game is lagging (due to the aforementioned UI scale updates being expensive...) this means multiple window resize events in a single frame ALL cause a UI scale update, which is useless.

UI scale updates from resizing now avoid doing *nothing* and are deferred until later in the frame for natural batching.

* Reduce allocations/memory usage of various rich-text related things

Just allocate a buncha dictionaries what could possibly go wrong.

I kept to non-breaking-changes which means this couldn't as effective as it should be.

There's some truly repulsive stuff here. Ugh.

* Cap debug console content size.

It's a CVar.

OutputPanel has been switched to use a new RingBufferList datastructure to make removal of the oldest entry efficient.

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-06-20 17:22:12 +10:00
metalgearsloth
095fe9d60f Turn broadphase contacts into a job (#5245)
Okay when I said no more physics this was a low-hanging fruit as we can get rid of the mapmanager getmapentityid for every contact so.
2024-06-20 17:19:26 +10:00
Leon Friedrich
14138fbcc2 Separate PVS serialization from compression & sending (#5246) 2024-06-20 17:18:51 +10:00
Pieter-Jan Briers
48ce24e98b Remove race condition invoking ThreadPool.SetMinThreads call
For some reason we call ThreadPool.SetMinThreads on startup of the game server. Calling this function this early seems to put us at high risk of triggering the following deadlock bug in the .NET runtime: https://github.com/dotnet/runtime/issues/93175

Given I have zero trust in whether this manual ThreadPool fuckery is even helpful, I'm just gonna nuke it and call it a day.
2024-06-20 03:12:01 +02:00
Pieter-Jan Briers
9cde21a7b3 Lower default MTU again.
Yet more reports of people running into issues with the current default.
2024-06-20 00:15:27 +02:00
Pieter-Jan Briers
ae1051e813 Cache non-existence of ResourceCache TryGetResource.
Many patterns (both in engine and content) make use of regular TryGetResource returning null. The problem is that if the resource doesn't exist, it won't be cached and the code attempts to load it from disk *every single time*.

For example, opening an inventory in SS14 would hang the client for ages on some UI themes due to the UITheme texture fallback system constantly trying to load a texture that doesn't exist.
2024-06-19 22:50:09 +02:00
Leon Friedrich
a3f80ac7dd Increase default value of res.rsi_atlas_size (#5250) 2024-06-19 22:09:39 +02:00
CaasGit
f98ef78a21 Update LoaderApi to the latest commit. (#5256) 2024-06-19 19:52:49 +02:00
metalgearsloth
bf8054b181 Version: 226.1.0 2024-06-18 21:50:18 +10:00
metalgearsloth
6b875e6676 Add local entities APIs (#5178)
Need for some vgroid stuff
2024-06-18 21:41:38 +10:00
Vasilis
a687c0a6c0 Change "to" to "from" on advert error (#5247)
It's a message FROM the hub

Currently, if you get "You are banned from the hub, if you believe this is an error contact us" it may confuse someone that they have to visit the hub URL where they will be met with a 404 because it's not an actual website. Seems it looks like "contact us to website"

Similarly, with "Failed to contact status address" makes it look like it's an error message coming from robust failing to connect to the hub server. When it's actually coming from the hub, telling you probably don't have your ports open.

I believe changing it to "from" will get the message acros that this is a message from the HUB and not robust.
2024-06-17 15:15:30 +02:00
Pieter-Jan Briers
0580cf3ff7 Drop SQL exporter in Robust.Benchmarks to fix compilation.
It was using an old Npgsql version, which broke compilation. Updating it breaks some of the custom JSON mapping code.

Comment out the entire thing, it's not being used anymore anyways.
2024-06-17 02:00:11 +02:00
Pieter-Jan Briers
590964d5bf Update SpaceWizards.HttpListener to 0.1.1
This fixes an EXTREMELY RARE crash on server startup due to a race condition. Yes, it did cause a crash in practice that's how I noticed it.
2024-06-16 21:34:15 +02:00
Pieter-Jan Briers
ceda39813d Fix MsgPlayerList being capped to 255
WHY WAS THIS A BYTE.

This prevented having more than 255 people on a server, beyond that the game might get stuck as people's player states wouldn't necessarily get sent.
2024-06-16 21:31:57 +02:00
metalgearsloth
a3a8912f42 Version: 226.0.0 2024-06-17 01:50:04 +10:00
metalgearsloth
b40973157d Animation player fixes (#5238)
Ensures the event always goes out even if the animation is stopped.
2024-06-17 01:47:31 +10:00
Leon Friedrich
1de8731465 Reduce uses of IComponentFactory.GetIndex(Type) (#5242)
* Update `RaiseComponentEvent` & component lifestatge methods

* Fix ComponentNetworkGenerator

* a

* A
2024-06-17 01:46:51 +10:00
Leon Friedrich
3a479cb5f4 Add ComponentEventAttribute to AfterAutoHandleStateEvent (#5243) 2024-06-15 17:32:19 +02:00
slarticodefast
76eeebf439 Allow RequestScreenTexture to be set in overlays (#5234) 2024-06-15 23:35:10 +10:00
metalgearsloth
2fa83181e2 Version: 225.0.0 2024-06-15 16:46:54 +10:00
Leon Friedrich
36f02b4a18 Fix IComponentFactory mock in tests (#5240) 2024-06-15 16:38:05 +10:00
metalgearsloth
e842142dd7 Minor API niceties (#5219)
* Minor API niceties

* weh
2024-06-15 16:26:01 +10:00
Leon Friedrich
2eb740cea8 Try prevent eventbus loops (#5166)
* Add test

* Try prevent event bus linked list loops

* Eh, add an upper limit anyways
2024-06-15 12:20:48 +10:00
metalgearsloth
a044f04e3b Remove CompIdx locks (#5231)
* Remove CompIdx locks

So GetComponentState in PVS calls RaiseComponentEvent which in turn calls this. When you start getting a significant number of players it seems to run into lock contention considering every single compstate get will lock this.

Instead we'll just update the dictionary whenever RegisterClass is called instead.

* Fix thread-safety issues
2024-06-15 01:30:45 +10:00
Leon Friedrich
a4723d1f62 Avoid read lock in GetEntityQuery (#5236) 2024-06-15 01:19:29 +10:00
Pieter-Jan Briers
627c1eb054 Rewrite HappyEyeballsHttp
This makes the game use HTTPS more when available.

Implementation is just taken from my work on the launcher, nothing special here.
2024-06-14 11:27:34 +02:00
Pieter-Jan Briers
836aec0b87 Changelog for Toolshed ent change
Forgot this in 5c83678c78. Oops.
2024-06-14 03:03:33 +02:00
Pieter-Jan Briers
9116e64291 Implement info query ?can_skip_build=1
The hub has been adding this parameter for a while, instructing the game server that it doesn't need to run ACZ. This fixes the (relatively common) issue where the first publish fails because ACZ takes longer than the hub status timeout.

I apparently already committed some code for this once on accident. Whoops.
2024-06-14 03:02:12 +02:00
metalgearsloth
a6bfb5f557 Fix lookupflags oversight (#5233)
The ONE codepath CM-14 used and I forgot to add it.
2024-06-14 09:59:47 +10:00
Pieter-Jan Briers
5c83678c78 Fix "ent" toolshed command
Makes it use NetEntity instead of EntityUid.
2024-06-13 00:22:22 +02:00
Pieter-Jan Briers
eac94b1032 Allow Eye position to be set directly.
Eye is not a well-designed API, but we've got it so here we go. It was originally designed to have some form of support for non-entity eyes through the FixedEye type, by overriding the Position property in a child type. #1016 broke this however.

This PR just makes the property writable so this is possible again.

Co-authored-by: moonheart08 <moony@hellomouse.net>
2024-06-12 23:56:29 +02:00
metalgearsloth
efd870d070 Mark System<T> as pure (#5225) 2024-06-11 03:43:20 +02:00
Leon Friedrich
94f98073b0 Make PrototypeManager.TryIndex log errors when using invalid id structs (#5203)
* Make `PrototypeManager.TryIndex` log errors when using id structs

* A
2024-06-08 22:15:21 +10:00
DrSmugleaf
5aa9378de0 Add an overload of TerminatingOrDeleted with a nullable EntityUid (#5214) 2024-06-08 20:51:05 +10:00
Leon Friedrich
850e9ab695 Try optimize NetEntities console completion helper (#5217)
* Try optimize `NetEntities` completion options

* Actually just remove it

* a
2024-06-08 20:44:21 +10:00
Tayrtahn
7319f3a241 Raise an event when an entity's name is changed (#5216) 2024-06-08 10:45:28 +10:00
DrSmugleaf
b6252c9e4f Make Entity<T> work as a loc parameter (#5215) 2024-06-07 15:48:00 +02:00
ElectroJr
fcd507d1f9 Version: 224.1.1 2024-06-06 00:58:26 +12:00
Leon Friedrich
1eb874f4c3 Fix storage key-not-found exception (#5213) 2024-06-05 22:56:50 +10:00
ElectroJr
a628d31c4b Version: 224.1.0 2024-06-05 20:19:18 +12:00
Leon Friedrich
2b0ecd7166 Fix cvar type errors (#5212) 2024-06-05 18:18:49 +10:00
Tom Leys
bde650689b Perf: Avoid a copy of ComponentChanges every tick within Checkpoints (#5146)
* Perf: Avoid a copy of ComponentChanges every tick within Checkpoints

- Also remove temporary Dictionary created every tick * every change
- Reduces GC load, 6GB less temporary allocations on typical replays.

* perf: Checkpoints: Apply state changes in-place when possible

- Avoids >1GB of gas tile allocations.

* Revert "perf: Checkpoints: Apply state changes in-place when possible"

This reverts commit 1a478944a6.

* Fix delta state merge issues

---------

Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
2024-06-05 18:06:38 +10:00
Tom Leys
87d8d74d8c Perf: Improve replay playback responsiveness (#5152)
* Perf: Improve replay playback responsiveness

- new CVAR ReplayMaxScrubTime
- There is a time budget when applying replay ticks updates of only 10 ms
- Ensure we don't apply checkpoints that move us backwards in time by accident
- Prevent double-lookup of checkpoints.

* Fix merge error

* Fix it again, but for real this time

---------

Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
2024-06-05 17:38:36 +10:00
Nemanja
dddf13a19a EntityPrototypeView (#5185)
* ent proto view

* pee jay bee rahvew

* Fix EnteredTree() not respawning the entity

---------

Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
2024-06-05 17:04:49 +10:00
Leon Friedrich
75626a86a3 Add dummy sessions for integration tests (#5202)
* Add dummy sessions

* if FULL_RELEASE
2024-06-05 16:50:06 +10:00
Tornado Tech
3e3cd0e257 Fixed incmd command (#5192)
* Fixed incmd command

* Change `HandleInputCommand` argument type

* Localize console errors

* Why is input code even like this
2024-06-05 16:32:56 +10:00
deltanedas
a3a90154a4 add SetUi to shared ui system (#5092)
* re-add AddUi

* rename to SetUi, add if missing

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-06-05 15:25:49 +10:00
Ygg01
9240c94e59 Write Errors when a duplicate localization key is found. (#4885)
* Update Linguini to v0.8.1

* Add tests and verify desired behavior has been reached.

* Remove duplicate messages.

* Minor fix to message output. Add Wrapper for Fluent errors.

* Restart the test pipeline.

* Restart the test pipeline.

* Make so test don't do an early bailout.

* Ensure all errors get written rather than bailing on first.

* Fix text breakage.

* Remove obsolete // TODO LINGUINI

* line wrapping conventions

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
2024-06-05 15:23:41 +10:00
ShadowCommander
a95ba9f181 Fix Toolshed EnumTypeParse on non-lowercase enum values (#5211) 2024-06-05 15:22:02 +10:00
Leon Friedrich
074a4faa92 Try fix client-side BUI error spam (#5208) 2024-06-05 15:21:27 +10:00
metalgearsloth
6b4d74f46e Maybe fix bad resolve logging (#5207)
I'm not entirely sure what happens, I couldn't repro it locally even when I tried to force it to use threadpool threads. The only info I have is it happens and no other info has been provided so.
2024-06-05 09:00:41 +10:00
ElectroJr
f648218756 Version: 224.0.1 2024-06-03 02:57:09 +12:00
Leon Friedrich
b497efb0c0 Try fix IPrototypeManager.ResolveResults() error (#5200)
* Try fix `IPrototypeManager.ResolveResults()` error

* A

* I love test IoC

* I love test init logic
2024-06-03 00:52:27 +10:00
Leon Friedrich
d8f2b917b4 Fix buis not closing properly on entity deletion (#5198) 2024-06-02 22:12:39 +10:00
Leon Friedrich
6dd6b79db6 Fix PVS exception (#5196) 2024-06-02 21:58:48 +10:00
ElectroJr
ff4548f108 Version: 224.0.0 2024-06-02 16:24:12 +12:00
ElectroJr
8f41405b31 Update release notes 2024-06-02 16:23:06 +12:00
Leon Friedrich
f285c62674 Rework entity prototype categories (#5061)
* Improve entity categories

* A
2024-06-02 14:10:11 +10:00
eoineoineoin
56c30edf04 Replace Matrix3 with System.Numerics.Matrix3x2 (#5078)
* Delete Matrix3. Replace with System.Numerics.Matrix3x2

* Feedback

* release notes
2024-06-02 14:08:47 +10:00
Nemanja
783d529ec4 more placement manager fixes (#5186)
* more placement manager fixes

* Update Robust.Shared/GameObjects/EntityManager.cs

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>

---------

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>
2024-06-01 10:13:52 -07:00
Pieter-Jan Briers
895bfb8ec0 Add Array.Clear(Array) to sandbox
API added in .NET 6, overload that took explicit array bounds was already in sandbox.
2024-06-01 14:17:28 +02:00
Leon Friedrich
6ef67cf513 Add stack trace to some error logs and remove some audio error logs (#5188)
* Add stack trace to some error logs

* Remove terminating entity audio error logs
2024-05-31 13:16:47 -07:00
Leon Friedrich
15a2f6702c Add try-catch to EnsureClientBui() (#5189)
* Add `try-catch` to `EnsureClientBui()`

* Better comment

* Uneccesary !

* a
2024-05-31 18:01:29 +10:00
Leon Friedrich
c5c2c2022a Ignore invalid entityuids in bui system update method. (#5187) 2024-05-31 10:47:55 +10:00
Pieter-Jan Briers
3c378640dd Add cvar_subs command
Dumb little thing, just wanted to test something.
2024-05-31 02:00:53 +02:00
metalgearsloth
0a149fa91c Expand PVS BUIs (#5179)
Mainly happens to aghosts who go out of range. Shouldn't be a huge perf impact as the User component is added / removed as BUIs get opened.
2024-05-31 09:51:11 +10:00
metalgearsloth
fe8d1d9422 Fix BUI state getting stuck (#5180) 2024-05-30 11:02:44 +10:00
metalgearsloth
c89c529ba4 Version: 223.3.0 2024-05-29 17:35:10 +10:00
metalgearsloth
dd56de70b7 Fix grid-based audio (#5087)
* Fix grid-based audio

- Fixes parenting issues.
- Add SetGridAudio as an easy way to set it up and apply the override too.

* No more global

* Rejig it all

* mergew

* review

* Minor optimisation

* Revert "Minor optimisation"

This reverts commit d0cdac7690.
2024-05-29 16:14:37 +10:00
Nemanja
710408c613 Fix placement manager rotation jank (#5176) 2024-05-29 12:32:14 +10:00
metalgearsloth
2461cd94dd Better parallel exception logging (#5175) 2024-05-29 12:31:46 +10:00
Kara
721408bb37 Version: 223.2.0 2024-05-27 17:50:28 -05:00
Leon Friedrich
8b42c1dd46 Improve FormattedMessage exception handling (#5170)
* Improve FormattedMessage exception handling

* comments
2024-05-27 15:40:29 -07:00
deltanedas
688b0b0458 make EntityPrototype.Categories use ProtoId (#5171)
Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-05-27 15:38:51 -07:00
Nemanja
5fa49b5689 Fix rotation not being passed in properly in entitymanager methods (#5174) 2024-05-27 15:37:20 -07:00
Leon Friedrich
1f7a9bdf0a Remove container IoC resolves (#5172) 2024-05-26 13:19:52 +10:00
ElectroJr
796abe1230 Version: 223.1.2 2024-05-25 18:33:46 +12:00
KIBORG04
6c2cf26250 Sync LastTileModifiedTick in MapGridComponent with the client (#5169)
* i think

* forgot

* Add lastTileModifiedTick to MapGridComponentDeltaState

* A
2024-05-25 11:05:40 +10:00
Vasilis
ce262d5ff8 Include a bat to launch the server (#5167) 2024-05-24 18:32:26 +02:00
ElectroJr
c250010dad Version: 223.1.1 2024-05-24 18:58:03 +12:00
ElectroJr
709c7bc808 Fix cvar type cast 2024-05-24 18:57:46 +12:00
ElectroJr
37918da73c Version: 223.1.0 2024-05-24 18:44:51 +12:00
Leon Friedrich
6cc2083b09 Move some EntityCoordinates methods to TransformSystem (#5136)
* Move `EntityCoordinate` methods to `TransformSystem`

* TryGetInterfaceData

* Update IsInSameOrParentContainer

* Don't broadcast BoundUserInterfaceCheckRangeEvent

* Changelog

* Defer fetching component

* Re-add obsoleted methods

* Add obsolete container methods

* update release notes

* Add InRange()

* Make error logging optional
2024-05-24 16:44:17 +10:00
ElectroJr
6a6bfe33ca Version: 223.0.0 2024-05-24 16:10:00 +12:00
Leon Friedrich
9737a4249c Rework delta-states (#5149)
* Remove full "delta" states

* Update MapGridComponentState

* abstract ComponentState

* Release notes

* Fix tests

* Fix nullable errors

* A

* typo

* Turns out testing my own PRs is a good idea

* too many warnings
2024-05-24 14:08:41 +10:00
Leon Friedrich
a48a353939 Add IConfigurationManager.OnCvarValueChanged event (#5161)
* Add `IConfigurationManager.OnCvarValueChanged` event

* Prevent CVars from changing type

* Allow double registrations?

* Turn debug asserts into exceptions

* We should really just start using generics if we can

* Re-use CVarChangeInfo

* Explicit old value

* Rename `OnCvarValueChanged` to `OnCVarValueChanged`

* internal constructor
2024-05-24 14:04:56 +10:00
DrSmugleaf
f0b45d95cb Fix serialization source generator error when multiple partials of the same definition are user-defined (#5160) 2024-05-23 22:48:40 +02:00
Amy
d69c5500f2 better compat getpixel (#5162)
Co-authored-by: amylizzle <amylizzle@users.noreply.github.com>
2024-05-23 22:47:55 +02:00
Pieter-Jan Briers
cf133ca341 Update more URLs to new infra.
Mostly auth but also a reference to the old hub URL.
2024-05-23 17:40:43 +02:00
Tom Leys
b0922b8e0e perf: Budget less memory for Replay Checkpoints (#28052) (#5145)
* perf: Replays use less memory for checkpoints (#28052)

- Simple change of the CVars and some stats
- Based on a Lizard replay, checkpoints move from on average every 70 ticks to every 350.

* Set a minimum number of ticks that must pass between checkpoints

* Fix stat collection, split _checkpointMinInterval, more CheckpointState

* update release notes
2024-05-23 15:35:10 +10:00
AJCM-git
512ebd8422 LineEdit placeholder tweaks (#5153)
* LineEdit focus tweaks

* Doing it the correct way

* Reviews
2024-05-21 22:34:45 +02:00
Leon Friedrich
85f74c3ba3 Fix GetWorldViewbounds() and GetWorldViewport() (#5060)
* Fix GetWorldViewbounds() and GetWorldViewport()

* Remove some uses of `CurrentMap` and `CurrentEye`
2024-05-19 10:47:04 +10:00
Tayrtahn
da7abc6580 Add analyzer and fixer for redundant DataField tag arguments (#5134)
* Add analyzer and fixer for redundant DataField tag arguments

* Share Tag autogeneration logic
2024-05-17 07:44:03 +02:00
Vasilis
b1329d30bf Dont print watchdog token eletric boogaloo (#5143) 2024-05-17 07:15:30 +02:00
Джексон Миссиссиппи
12808d073e Make CVar RenderFOVColor settable by server only (#5142)
* | CVar.SERVER

* Update CVars.cs
2024-05-17 07:15:17 +02:00
Pieter-Jan Briers
ec794ce4e4 Version: 222.4.0 2024-05-17 02:51:44 +02:00
metalgearsloth
6b13475842 Revert "Add physics delta states (#5116)" (#5144)
This reverts commit 1189613908.
2024-05-17 02:21:05 +02:00
Pieter-Jan Briers
b48ee22800 Add more System.Numerics types to sandbox. 2024-05-16 22:25:13 +02:00
Pieter-Jan Briers
0b95a4edeb Version: 222.3.0 2024-05-16 20:14:37 +02:00
Pieter-Jan Briers
ed359481b4 We can't expect god to do the release notes. 2024-05-16 20:14:18 +02:00
metalgearsloth
1189613908 Add physics delta states (#5116)
* Add physics delta states

Significantly cuts down on data being sent + should make client state handling faster.

* Update Robust.Shared/Physics/Components/PhysicsComponentState.cs
2024-05-16 20:09:25 +02:00
DrSmugleaf
30907d8415 Fix ordered subscriptions not working when targeting a parent system type (#5135)
* Fix ordered subscriptions not working when targeting a parent system type

* Fix missing usages of expand ordering

* Extract method
2024-05-16 20:00:15 +02:00
Leon Friedrich
7f2da4d4f3 Fix paused entities not updating on prototype reload (#5128) 2024-05-16 19:06:11 +02:00
Ed
e30e963623 Hidden tiles (#5102)
* hidden tiles

* Update TileSpawningUIController.cs

* Update TileSpawningUIController.cs

* Update ITileDefinition.cs

* Update TileSpawningUIController.cs

* Move EditorHidden where clause out

* Make EditorHidden a DIM

So there's no breaking change

* Release notes.

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-05-16 19:05:39 +02:00
metalgearsloth
b056caeed7 Fix cross-map BUIs (#5115)
Even the ignore range bit is going to break with pvs but uhh not sure on that one unless we do overrides or something.
2024-05-16 19:00:11 +02:00
Leon Friedrich
fbc8086335 Don't iterate over component events when removing components (#5138)
* Don't iterate over component events when removing components

* Welp nevermind, forgot about tests

* A

* AAAAAA

* AAAA
2024-05-16 18:53:33 +02:00
Pieter-Jan Briers
799702b814 Work against .NET SDK update ManagePackageVersionsCentrally change
.NET SDK 8.0.300 changed ManagePackageVersionsCentrally to be implicitly set if Directory.Packages.props exists. We do not want this, as we intentionally have some projects that have it disabled.

We now explicitly unset the value in the Directory.Packages.props file to get the old behavior back.

See https://github.com/dotnet/core/issues/9309
2024-05-15 21:02:35 +02:00
ElectroJr
63df90f86f Version: 222.2.0 2024-05-14 23:01:41 +12:00
Vasilis
51f0c60bd3 Do not log wrong and correct watchdog token into info logs (#5133)
* Lets not do this?

* Webedit 2
2024-05-14 02:12:24 +02:00
metalgearsloth
a9ed53f47b Run BUI range checks in parallel (#5118)
These still take almost half-ms in server tick time on live as it has to do a raycast for everyone that has storage or their PDA open or whatever. Haven't benchmarked with a lot of clients but easiest way to tell is just check grafanaTM and I'm not sure how to check this locally.
2024-05-14 09:03:26 +10:00
Amy
41c40f1a94 Fix checking wrong property in TryGetVariableType() (#5120)
Co-authored-by: amylizzle <amylizzle@users.noreply.github.com>
2024-05-14 08:44:19 +10:00
deltanedas
6e61c35d35 add missing Comp inline to EntityQuery (#5123)
Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-05-14 08:43:03 +10:00
ShadowCommander
aae0a8bc51 Add doc comments to CreateEntityUninitialized (#5131) 2024-05-14 08:42:34 +10:00
DrSmugleaf
cb543240c6 Fix clients mispredicting a character's gender (#5119)
* Fix clients mispredicting a character's gender

* Allow nullable value in set
2024-05-12 22:20:21 -07:00
metalgearsloth
1654ab06f5 Revert "Add audio stream name to entity name" (#5127)
This reverts commit d2a2afe82e.
2024-05-13 10:53:30 +10:00
Brandon Li
211245215e remove XAMLIL after generating populate method (#5126) 2024-05-13 01:34:32 +02:00
ShadowCommander
10aaaa65c5 Add editorconfig wrapping settings (#5125) 2024-05-12 17:50:19 +02:00
ShadowCommander
d2a2afe82e Add audio stream name to entity name (#5121) 2024-05-10 17:58:38 +02:00
Pieter-Jan Briers
025d90d281 Change build upload server to Suns 2024-05-09 09:48:49 +02:00
metalgearsloth
c229f2e312 Fix valuelist ensurecapacity(0) throwing (#5113)
_items is still null so.
2024-05-08 20:46:45 +02:00
metalgearsloth
fe051a3577 Minor bui thing (#5114) 2024-05-08 19:59:13 +10:00
DrSmugleaf
51a0ef1e60 Fix error when JointRelayTargetComponent shuts down while applying state (#5110) 2024-05-08 19:21:55 +10:00
DrSmugleaf
702dfef5fc Version: 222.1.1 2024-05-07 22:28:07 -07:00
DrSmugleaf
a0c1ad246f Fix never setting BoundUserInterface.State (#5111)
* Fix never setting BoundUserInterface.State

* Make setter internal
2024-05-07 20:51:26 -07:00
metalgearsloth
1153888bd1 Add truncate for filesaving (#5098)
* Add truncate for filesaving

If I expose it to content I pretty much always want truncate to be honest.

* Update Robust.Client/UserInterface/FileDialogManager.cs

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2024-05-07 00:58:57 +02:00
Amy
ccbb6ddec7 Add method for getting type of var (#5070)
Co-authored-by: amylizzle <amylizzle@users.noreply.github.com>
2024-05-06 20:39:32 +02:00
metalgearsloth
970da5f717 Version: 222.1.0 2024-05-06 13:43:11 +10:00
Pieter-Jan Briers
4d528dd577 Analyzer to ban uncached regexes (#5107)
Using static Regex functions that take in a pattern is bad, because they constantly have to be re-parsed. Cache the Regex instance.
2024-05-06 10:30:31 +10:00
Pieter-Jan Briers
c83720b163 Update Lidgren to v0.3.1 2024-05-06 01:55:05 +02:00
Pieter-Jan Briers
bd87a805d4 Add CVars to turn Lidgren's error/warning logs off.
Combined with upcoming Lidgren changes, this should make DDoS-induced warning log spam not cause huge server perf issues anymore.
2024-05-06 01:47:27 +02:00
Leon Friedrich
fff42fb2b4 Partially fix UI assert (#5100)
* Partially fix UI assert

* Avoid breaking change in BoundKeyEventArgs

This is a public constructor, as much as it maybe shouldn't be. Adding this parameter is a breaking change.

* Replace .Disposed checks with ! .VisibleInTree

Control disposal should not be used anymore.

* Release notes

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-05-06 01:29:34 +02:00
metalgearsloth
4500669f65 Version: 222.0.0 2024-05-06 08:54:19 +10:00
Pieter-Jan Briers
7d19ea9338 Fix compiler error from merges
EntProtoId PR was incompatible with the PR to change EntityPrototype methods to require IComponentFactory passed in
2024-05-06 00:32:54 +02:00
Leon Friedrich
2dc610907d Make IComponentFactory argument in EntityPrototype mandatory (#5101) 2024-05-05 23:00:10 +02:00
DrSmugleaf
beb1c4b1fb Add EntProtoId<T> (#5097)
* Add EntProtoId<T>

* Fix error messages

* Shorten error messages

* Make services non-optional
2024-05-05 22:59:45 +02:00
metalgearsloth
7e331eaa75 Defer clientside BUI opens (#5073)
* Defer clientside BUI opens

Needs content fix first as storage UI breaks.

* tweaks

* Re-revert this because it seems needed
2024-05-03 12:58:19 +10:00
Leon Friedrich
caf9e45ad9 Fix PVS iterating over duplicate chunks when a a client has multiple viewers/eyes (#5094) 2024-05-03 05:52:56 +10:00
metalgearsloth
7cb3aeccc2 Version: 221.2.0 2024-05-02 12:20:54 +10:00
metalgearsloth
ae83e606d6 Add SetWorldRotNoLerp method (#5091)
* Add SetWorldRotNoLerp method

I neeeeed it.

* Also this one

* dum
2024-05-02 12:14:27 +10:00
metalgearsloth
d9d5ef7471 Add audio helpers for map-based audio (#5086)
Doesn't need to be a flag because we just set it as global, whereas gridaudio cares about stuff every frame.
2024-05-02 09:51:14 +10:00
Pieter-Jan Briers
0f97f366a6 Copy CopyToShaderParameters in SpriteComponent.CopyFrom.
Fixes dragging displacement-mapped mobs in SS14 making the displacement map visible.
2024-05-01 23:45:58 +02:00
Jezithyr
35ab0b8cc8 Version: 221.1.0 2024-04-30 12:51:22 -07:00
metalgearsloth
5a14e939bf TileChangedEvent bool (#5089)
Shows whether IsEmpty is different, useful in circumstances.

Also NotNullWhen null handling consistency.
2024-04-30 07:28:29 -07:00
T-Stalker
ccba6b5d1c Reduce default sound range to 15 (#5085) 2024-04-30 15:41:00 +10:00
DrSmugleaf
254a5987c7 Fix Array.Resize sandbox signature (#5084) 2024-04-30 02:14:56 +02:00
metalgearsloth
8550056e68 Version: 221.0.0 2024-04-29 18:46:57 +10:00
Leon Friedrich
25211e3781 Improve transform & state handling exception tolerance (#5081)
* Improve transform & state exception tolerance

* release notes

* Fix pvs assert

* Fix velocity conservation
2024-04-29 18:42:05 +10:00
Leon Friedrich
3500abfd47 Add IUserInterfaceManager.UpdateHovered() (#5083)
* Add `IUserInterfaceManager.UpdateHovered()`

* Try fix tests
2024-04-29 18:37:52 +10:00
Leon Friedrich
7d1915096a Use more entity queries in physics systems & entity manager (#5082) 2024-04-29 13:46:10 +10:00
Nemanja
4504731588 Add a method in SharedTransformSystem for swapping the position of two entities (#4988)
* swap pos method

* no forcing

* Sluth review

* weh
2024-04-29 13:45:12 +10:00
Leon Friedrich
701fa95a82 Temporarily disable macos tests (#5079) 2024-04-29 02:35:04 +10:00
ShadowCommander
40a9048704 Add margin input value order as a comment (#5067)
* Add margin input value order as a comment

* Make a better comment and move value to remark
2024-04-27 20:17:11 +02:00
Leon Friedrich
cee8d42776 Improve MergeImplicitData exception tolerance (#5075) 2024-04-28 02:23:56 +10:00
metalgearsloth
3330d96177 Version: 220.2.0 2024-04-27 16:05:51 +10:00
Pieter-Jan Briers
4033d96327 Engine changes for displacement maps. (#5023)
* Add load parameter support to RSIs.

Currently only supports turning sRGB off. RSIs with custom load parameters are not thrown into the meta-atlas.

As part of this, TextureLoadParameters and TextureSampleParameters has been made to support equality.

* Add UV2 channel to vertices.

This is a bad hack to make displacement maps work in Robust. The UV2 channel goes from 0 -> 1 across the draw and can therefore be used by displacement maps to map a separate displacement map layer on top of the regular meta-atlas RSI texture.

This creates float inaccuracy issues but they weren't bad enough to completely void the feature. I'm thinking I learn from this experience and completely re-do how UVs work with the renderer rewrite, so that hopefully won't happen anymore.

This required dumping the optimized PadVerticesV2 because the changed struct size made it impractical. RIP.

I don't like this approach at all but the renderer is slated for a rewrite anyways, and all shaders will need to be rewritten regardless.

* Add CopyToShaderParameters for sprite layers.

This effectively allows copying the parameters of a sprite layer into another layer's shader parameters. The use case is to copy texture coordinates for displacement maps, as the exact map used changes depending on orientation. It also enables animations to be used though I didn't use that personally.
2024-04-27 16:03:35 +10:00
metalgearsloth
6e0205d1a8 Version: 220.1.0 2024-04-27 12:33:45 +10:00
ShadowCommander
7cd95351c3 Remove IP address and HWId from ViewVariables (#5062)
* Remove IP address from ViewVariables

* Remove HWId from ViewVariables
2024-04-27 12:30:34 +10:00
Leon Friedrich
2a102f048f Fix client-side replay exception (#5068)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-04-27 12:30:00 +10:00
metalgearsloth
16bab1bc03 Close UIs on disconnect (#5071)
Engine handles it fine but content does not as the state gets handled before all comps are initialized.
2024-04-27 12:21:46 +10:00
metalgearsloth
123d0ae6ac Version: 220.0.0 2024-04-26 18:15:47 +10:00
metalgearsloth
d72de032fa Predicted BUIs (#5059)
* Add TryGetOpenBUI

Avoids having to get the component and openinterfaces separately.

* Couple more helpers

* entityquery

* reviews

* Shared BUIs

* zawehdo

* More boilerplate

* Bunch more work

* Building

* Stuff

* More state handling

* API cleanup

* Slight tweak

* Tweaks

* gabriel

* Disposies

* Active UI support

* Lots of fixes

- Fix states not applying properly, fix predicted messages, remove redundant message type, add RaiseUiMessage for an easy way to do it from shared, add the old BUI state change events back.

* Fix test failures

* weh

* Remove unncessary closes.

* release note
2024-04-26 18:12:55 +10:00
Tayrtahn
0fdba836ee Remove debug assert for Fixture.Owner equality (#5066)
* Removed debug assert for Fixture owner equality

* Blah
2024-04-26 14:47:09 +10:00
metalgearsloth
eb63809999 Version: 219.2.0 2024-04-25 00:31:04 +10:00
Leon Friedrich
4c3c74865c Fix yaml linter & improve validation of static fields (#5056) 2024-04-25 00:27:02 +10:00
Nemanja
b624f5b70f Add SetMapCoordinates (#5065)
* add set map coordinates function

* mmmmmmm yes

* Update Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-04-24 22:55:58 +10:00
Pieter-Jan Briers
6566a7658a Fix DebugCoordsPanel freezing when hovering a UI control.
It would bail out of the entire update logic if you aren't hovering over a map position, which isn't great when the control displays far more than in-simulation mouse position info.
2024-04-23 20:34:29 +02:00
metalgearsloth
9e3e1cc929 Optimise physics networking a lot (#5064)
Avoids unnecessarily dirtying every single tick when a mob is moving. Also avoids the getcomponent every time which is nice.
2024-04-23 22:36:40 +10:00
ElectroJr
4e87d93009 Version: 219.1.3 2024-04-23 00:26:17 -04:00
Leon Friedrich
1031ae4cc5 Fix mapping not pausing maps (#5063) 2024-04-23 14:25:40 +10:00
ElectroJr
73da147b88 Version: 219.1.2 2024-04-21 04:58:44 -04:00
Leon Friedrich
0ab59d70b1 More mapinit fixes (#5058) 2024-04-21 18:57:20 +10:00
ElectroJr
8e8470ac7e Version: 219.1.1 2024-04-21 02:50:34 -04:00
Leon Friedrich
15f94bd094 Fix mapinit persistence when overwriting existing maps (#5057)
* Fix mapinit persistence when overwriting existing maps

* Hours wasted chasing fucking chickens in a crate
2024-04-21 16:48:39 +10:00
DrSmugleaf
68888c4370 Make remaining IPrototypes partial (#5053) 2024-04-21 07:39:54 +10:00
ElectroJr
19f87dfbb3 Version: 219.1.0 2024-04-20 16:52:47 -04:00
Leon Friedrich
68e5b6924d Add ComponentRegistry overrides to more entity spawn methods (#5051) 2024-04-19 13:16:17 +10:00
Leon Friedrich
9f913cd2d9 Fix RecursiveMapInit (#5052)
* Fix RecursiveMapInit

* re-use sawmill
2024-04-19 13:15:42 +10:00
Vasilis
ec37d1c137 Auth is now required by default (#5050) 2024-04-18 17:51:25 +02:00
metalgearsloth
ea58924495 Audio stuff (#5048)
Better overlay debug and uses the adjusted distance for cutoff instead.
2024-04-18 14:36:29 +10:00
keronshb
c5aa735506 Adds rotation to Map Position spawns (#5047)
* add angle to rotation

* fixes inheritance

* proxy

* Fixes maprot

* changes angle to default and uses set coords overload
2024-04-18 14:25:07 +10:00
metalgearsloth
f5a6e52c7f Version: 219.0.0 2024-04-18 14:16:40 +10:00
Leon Friedrich
d5c4981648 Partial MapManager refactor (#5042)
* MapManager rejig

* Update Tests

* A
2024-04-18 14:05:02 +10:00
Pieter-Jan Briers
8c6170661d Die 2024-04-18 03:11:24 +02:00
metalgearsloth
1901059755 Version: 218.2.0 2024-04-17 19:06:59 +10:00
metalgearsloth
8cdec92be6 Add slider locking (#5044)
* Add slider locking

Useful if you just want like a playback slider without the client intefering.

* Name alignment
2024-04-17 19:04:52 +10:00
metalgearsloth
0a00e7ec29 Network audio states (#5024)
* Network audio states

Lets server pause or stop audio or whatever.

* Better API and state fix

* Bunch of fixes

- TimedDespawn proccing too early.
- PlaybackPosition setting
- SetState setting.

* Clamps and despawn fixes

* fix
2024-04-17 17:55:22 +10:00
DrSmugleaf
8c25a83066 Expose worldPosition in SpriteComponent.Render (#5043)
* Expose worldPosition in SpriteComponent.Render

* Set default value to zero
2024-04-17 16:17:12 +10:00
Pieter-Jan Briers
0fadfc2d9b Allow control layout properties to be set via style sheet. (take 2) (#5037)
* Reapply "Allow control layout properties to be set via style sheet." (#5035)

This reverts commit af36d24892.

* Fix tests

MaxSize properties had wrong default. Oops.
2024-04-17 00:42:12 +02:00
metalgearsloth
a6e7224672 Version: 218.1.0 2024-04-16 22:39:23 +10:00
metalgearsloth
37796f4806 Add a Vertical tab container (#4948)
* Vertical tab container

* weh

* Tab review
2024-04-16 22:33:43 +10:00
Pieter-Jan Briers
a5494d1df2 Change default hub URL 2024-04-15 22:27:25 +02:00
Errant
e2525a2103 Fix division remainder issue in color.cs (#5040) 2024-04-15 19:13:43 +02:00
Pieter-Jan Briers
b50f68866f Enable roslyn extension tests in CI (#5038)
* Enable roslyn extension tests in CI

* I'll be real I kinda just hoped that last one would work. dotnet test's --help documentation is useless garbage so I couldn't tell if that was supported or not. Guess not.

* Actually fix the Roslyn tests.

As far as I can tell, Roslyn tests haven't worked since #2976.

The tests used a pretty awful technique of linking the test code against the analyzer, so that the analyzer's copy of the relevant attributes got included into the test. This then broke when the namespace got changed by the linked PR.

Now the tests get an EmbeddedResource for the necessary test files compiled instead.

Also applied this to DependencyAssignAnalyzerTest because why not.
2024-04-14 09:26:07 +02:00
Pieter-Jan Briers
03a4d3e0a0 Add IEquatable`1.Equals to sandbox
Why wasn't this in here wtf.
2024-04-14 08:36:25 +02:00
metalgearsloth
af8fb52a4f Version: 218.0.0 2024-04-14 15:00:07 +10:00
metalgearsloth
fd60dc2887 Add EntManager + ProtoManager helpers for random picks (#4869)
* Add EntManager + ProtoManager helpers for random picks

Lets us cleanup content a bit from these being re-implemented.

* weh
2024-04-14 14:55:55 +10:00
metalgearsloth
cd24fd46b6 Default worldpos to null (#5036)
I think this is slightly more robust than defaulting to 0 actually while still fixing the issue.
2024-04-14 14:47:30 +10:00
metalgearsloth
44cc7127fa Default SetWorldPos to 0 rotation (#5034)
More closely aligns with the old default.
2024-04-14 14:28:16 +10:00
metalgearsloth
af36d24892 Revert "Allow control layout properties to be set via style sheet." (#5035)
This reverts commit 8c4deb2067.

# Conflicts:
#	RELEASE-NOTES.md
2024-04-14 14:14:44 +10:00
Leon Friedrich
caa8ff0f2d Modify container/spawn helper methods (#5030)
* Modify container/spawn helper methods

* A
2024-04-14 13:47:21 +10:00
Pieter-Jan Briers
cd67c67a5c Add analyzer to warn for assignment to dependency fields. 2024-04-14 05:14:12 +02:00
deltanedas
57b328e8c2 add CopyData to appearance system (#5022)
Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-04-14 02:24:31 +02:00
Pieter-Jan Briers
4874b1db68 Update UI themes on prototype reload. 2024-04-14 02:15:53 +02:00
Pieter-Jan Briers
814ad08884 Allow scaling the line height of a RichTextLabel 2024-04-14 02:10:01 +02:00
Pieter-Jan Briers
8c4deb2067 Allow control layout properties to be set via style sheet.
This works by setting the stylesheet values into the regular control property fields when updated. This means 0 performance overhead except when updating styles, and even then it's probably negligible. A bitfield is used to track which properties are set and how.

This code is all done manual for now. I wanted to make a source gen for this but couldn't be arsed at the moment. The code manually written here is basically what a future source gen would generate optimally.
2024-04-14 02:09:00 +02:00
Pieter-Jan Briers
f6a5120e56 Fix exception when inspecting element in some cases.
Happened when I was trying to develop item status stuff.
2024-04-14 02:05:40 +02:00
Pieter-Jan Briers
c1b8bf8e52 Make StatusHost request headers case insensitive
Woops that's how HTTP is supposed to work.
2024-04-11 02:28:46 +02:00
Pieter-Jan Briers
4b193bad26 Add non-generic GetCVar.
Surprised we didn't have this.
2024-04-11 02:25:40 +02:00
Tayrtahn
8db3da4852 Add Type tracking to FieldNotFoundErrorNode (#5032)
* Add Type tracking to FieldNotFoundErrorNode

* Suggested changes, plus xmldoc and primary constructor conversion.
2024-04-08 19:27:02 +02:00
Leon Friedrich
0c271fc2f8 Remove uneccesary Exists() checks in container system (#5031)
* Remove `Exists()` checks in container system

* A

* A
2024-04-08 01:24:09 +02:00
Pieter-Jan Briers
ed406c06b7 Fix HTTP errors on watchdog ping not being reported 2024-04-05 23:23:46 +02:00
Pieter-Jan Briers
b31940b489 Improve logging for watchdog pinsg 2024-04-05 02:22:06 +02:00
Pieter-Jan Briers
84360c653d Add better environment variable config system
ROBUST_CVARS had multiple issues:

* Not composable, i.e. two independent systems can't easily layer CVars to set as they all have to go into one var
* Not sanitary, there's no way to store things that have a ";" in them because it'd always get used as separator.

This adds a new ROBUST_CVAR_* system. For example I can set ROBUST_CVAR_game__hostname=foobar to set a CVar via a single env var. A double underscore ("__") is replaced with a period to make the CVar names safe for environment variables.

Also made Robust.Shared.Configuration.EnvironmentVariables internal because wtf that should not be public no.
2024-04-04 02:24:57 +02:00
metalgearsloth
6764ed56b0 Version: 217.2.1 2024-03-31 17:02:37 +11:00
Leon Friedrich
2b55d39e51 Make various ValueList enumerators use spans (#5019)
* Make various ValueList enumerators use spans

* Remove reference to EntityEventBus.OrderedRegistration

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-03-31 17:01:03 +11:00
metalgearsloth
fdc1de2430 Fix LineEdit tests (#5021) 2024-03-31 16:58:15 +11:00
metalgearsloth
99c5b0ad08 Version: 217.2.0 2024-03-31 15:29:26 +11:00
faint
9e2ab2a917 Double-clicking in LineEdit (#4831)
* Double-clicking in LineEdit

epic

* Fix IGameTiming dependency

* remove iocmanager.resolves

* test fix

* Update Robust.Client/UserInterface/Controls/LineEdit.cs

Co-authored-by: ShadowCommander <shadowjjt@gmail.com>

* review

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: ShadowCommander <shadowjjt@gmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-03-31 15:25:31 +11:00
metalgearsloth
36eb857b55 Fix TimeSpan addition (#5018)
Rider thinks nullable timespan += is fine but it no fine.
2024-03-31 15:02:18 +11:00
metalgearsloth
92d7f2723a Add ComponentRegistry helpers to EntityManager (#4934)
* Add ComponentRegistry helpers to EntityManager

* Metadata here too

* Remove object cast

* knock

* Plural
2024-03-31 14:54:01 +11:00
Leon Friedrich
91d3f67a94 Make IntersectRayWithPredicate ignore non-hard fixtures (#5017) 2024-03-31 14:48:37 +11:00
ElectroJr
b28b5ed09b Version: 217.1.0 2024-03-30 21:16:44 -04:00
Leon Friedrich
30eed7957f Add an EffectiveCurTime for physics subticks (#5014)
* Add an `EffectiveCurTime` for physics subticks

* Release notes
2024-03-31 12:15:30 +11:00
Fildrance
73c1449811 Add GetItems() extension for IRobustRandom (#4975)
* refactor: RobustRandom and RandomExtensions namespace change to file-scoped

* refactor: IRobustRandom xml-doc methods rearranged to be more structured.

* feat: GetItems methods added to RandomExtensions, tests for new methods added.

* fix: GetItems will not request count-1 from next random, as System.Random.Next have upper bound excluded.

* fix: enforced standard deviation on picking next items in RandomExtensions.GetItems + fixed hashet initial capacity +removed mandatory hashset allocation

* refactor: specified border values interaction in IRobustRandom xml-doc

* refactor: updated relese-notes

* refactor: changed release-notes PROPERLY

* fix: order by which unique random items are picked in RandomExtensions.GetItems were fixed to ACTUALLY follow normal distribution

* refractor: added comment for devious RandomExtensions.GetItems only-unique logic

* reduce code duplication

* Cleanup code a bit

Rename variables, and make it a bit more compact.
Also, IMO the description is unnecessary

* Remove obsolete extension

* Remove incorrect O(n) comments.

---------

Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru>
2024-03-30 10:42:39 +11:00
Pieter-Jan Briers
958b5dd06d Fix MapComponent.LightingEnabled funky FOV.
There's two separate bool checks, one wasn't turned off, meaning the FOV kept being rendered but not updated.
2024-03-29 16:54:27 +01:00
metalgearsloth
4002cbddb9 Version: 217.0.0 2024-03-29 17:04:51 +11:00
metalgearsloth
c38a14e78f WorldPos review fix (#5011)
Forgot I merged ghub and tried pushing and didn't check it failed.
2024-03-29 16:56:25 +11:00
metalgearsloth
5164d99996 Implement VV for AudioParams + dump obsolete params (#4994)
* Implement sound VV

No params yet because I'm lazy but paths and collections work.

* Fixes

* rn

* review

* pitch

* VV sound params + dump unneeded params

BusName never used and per-source Attenuation got dropped.

* weh?
2024-03-29 16:54:14 +11:00
metalgearsloth
c79217ab66 Rework SetWorldPosition (#4915)
* Remove ents from containers for worldpos updates

Avoids bugs from content not checking for this every time. Physics and anything else important manually uses localposition updates so shouldn't adversely impact performance that much.

* Fixes

* Fix grid pos

* Also fix the joint setter

* Add both

* Update Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs

Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>

---------

Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
2024-03-29 16:49:26 +11:00
deltanedas
9ef7f7cb37 add AddUi to shared ui system (#4984)
Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-03-27 14:36:11 +11:00
ShadowCommander
02ac314b1a Fix first measure of ScrollContainer scroll bars (#5005)
The first measure would include scroll bar size even if the available size fit all the contents. This would make the returned size larger than it should be.
2024-03-27 14:33:59 +11:00
metalgearsloth
8607ba1f16 Version: 216.0.0 2024-03-27 14:14:54 +11:00
Pieter-Jan Briers
2a9de462d5 Preserve tile maps when saving maps & related changes (#5003)
* Un-hardcode behavior to make a component not saved to map file.

MapSaveId is a special component that can't be saved to map files due to a hardcoded type check. This behavior can now be applied to any component with [UnsavedComponent].

Moved "component registration" attributes into a single file because they don't deserve their own (poorly organized) .cs files.

* Add ITileDefinitionManager.TryGetDefinition

Try-pattern version of the existing indexers.

* Preserve tile maps when saving maps

This changes the map saver and loader code so that the "tilemap" can be preserved between map modifications as much as possible.

The tile map from the loaded map gets stored onto MapSaveTileMapComponent components on all loaded grids. This tile map is then used when saving, meaning that changes to the engine's internal tile IDs do not cause diffs.

Fixes #5000

* Changelog

* Fix tests
2024-03-27 14:14:19 +11:00
metalgearsloth
c59ef5ab2d Fix buffered audio disposals (#5009) 2024-03-27 14:07:54 +11:00
Leon Friedrich
eba1d866fb Change default PVS LoD cvars (#5008) 2024-03-27 13:58:52 +11:00
Pieter-Jan Briers
ab2bff8f40 Make sawmill levels work differently. (#5006)
Before, sawmill log levels were very clunky to use. We set the root sawmill to something like debug or info, but then it becomes impossible to specify specific sawmills to be verbose. This is because sawmills checked the log level at every part of the hierarchy when navigating "up" to find log handlers to write into.

This changes the behavior so that the filter log level is the FIRST set log level encountered on the hierarchy. This means the log level is compared only once, and a sawmill down the hierarchy that has a level like Verbose set can cause verbose messages to come up even if the root is still set to Debug. This matches the logging behavior in libraries such as ASP.NET Core.
2024-03-27 13:27:54 +11:00
Pieter-Jan Briers
536fca4115 Version: 215.3.1 2024-03-26 18:47:35 +01:00
Pieter-Jan Briers
7a0d02463c Revert "Zstd Update (#5002)"
This reverts commit eee771c5f1.
2024-03-26 18:46:34 +01:00
Pieter-Jan Briers
6df53d60ed Changelog 2024-03-26 15:06:38 +01:00
Pieter-Jan Briers
ff38e9f12a Version: 215.3.0 2024-03-26 02:52:36 +01:00
Pieter-Jan Briers
00c58c76a8 Disable MTU Expansion again
Reports of problems, double checked with Grafana. Great.

I am going to do manual test runs of this system against Miros again if I end up looking to improve the situation.
2024-03-26 02:49:47 +01:00
Pieter-Jan Briers
0bf99e173c Texture GetPixel() fixes
Wow this API is bad. Well the API is just terrible for perf for multiple reasons but not fixing that...

1. Was using DSA GetTextureImage() instead of GetnTexImage() so only worked if DSA was available.
2. Was using stackalloc with a potentially huge amount of memory (original bug).
3. Had an off-by-one for the vertical coordinate.

All fixed now.

Fixes #5001
2024-03-26 01:11:30 +01:00
Wrexbe (Josh)
eee771c5f1 Zstd Update (#5002) 2024-03-25 22:20:39 +01:00
Tayrtahn
2946cd866c Added length comparison helpers for Vector2 (#4999) 2024-03-25 16:08:43 +01:00
chromiumboy
9a2a3d658d Provide option to stop PlaceManager from changing the player control scheme (#4977) 2024-03-25 17:44:11 +11:00
Leon Friedrich
d933f03a54 Add TryComp and HasComp to EntityQuery<T> (#4996) 2024-03-24 21:40:28 +01:00
metalgearsloth
25bbb21dc8 Version: 215.2.0 2024-03-25 00:57:09 +11:00
metalgearsloth
4460454563 Implement sound VV (#4966)
* Implement sound VV

No params yet because I'm lazy but paths and collections work.

* Fixes

* rn

* review
2024-03-24 16:59:40 +11:00
KISS
a2d8fa7a9b Making possible to QueueDeleteEntity on EndCollideEvent (#4883)
* made possible to destroy entity on EndCollideEvent

* figured queue delete issue

* review

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-03-24 16:32:57 +11:00
metalgearsloth
71f0491f10 Version: 215.1.0 2024-03-24 14:21:33 +11:00
Leon Friedrich
b4c1618338 Misc Toolshed tweaks (#4990)
* Toolshed tweaks

* oops

* Apply suggestions from code review

Co-authored-by: Moony <moony@hellomouse.net>

* Re-add NotImplementedException

* Move error message

---------

Co-authored-by: Moony <moony@hellomouse.net>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-03-24 14:18:49 +11:00
Wrexbe (Josh)
df0945f3cd VV editor for EntProtoId? (#4986)
Co-authored-by: wrexbe <wrexbe@protonmail.com>
2024-03-24 14:13:07 +11:00
metalgearsloth
8d477716b0 Add audio filepath completion helper (#4968)
* Add audio filepath completion helper

Due to how audio is packaged server doesn't have most audio files.

* RN

* weh
2024-03-24 14:12:31 +11:00
metalgearsloth
5c1a5e9826 Add 2 random methods (#4980)
* Add 2 random methods

One for valuelist shuffle and one for NextAngle range.

* rn

* Fix RN
2024-03-24 14:00:21 +11:00
metalgearsloth
6daa3ad2fc Version: 215.0.0 2024-03-24 13:24:11 +11:00
Vasilis
033a617102 Requirement for https://github.com/space-wizards/space-station-14/pull/25569 (#4992) 2024-03-23 22:23:39 +01:00
Tayrtahn
b9b565d53e Fix uncaught overflow exception when parsing NetEntities from strings. (#4989)
* Fixed uncaught overflow exception when parsing NetEntities from strings.

* or
2024-03-23 20:55:45 +01:00
Pieter-Jan Briers
b7ea4d0cca Add release notes for #4987 2024-03-23 16:38:19 +01:00
Pieter-Jan Briers
919ec01477 Enable MTU expansion by default.
Due to yet another need to lower the MTU we should enable this by default. Improves network efficiency.
2024-03-23 16:27:33 +01:00
Pieter-Jan Briers
e484eac29c Changelog for 3097784cd7 2024-03-23 16:25:49 +01:00
Pieter-Jan Briers
eedadb250f Add net.mtu_ipv6 CVar.
Wires up to the new MaximumTransmissionUnitV6 configuration in Lidgren. Default is that of Lidgren.
2024-03-23 16:25:25 +01:00
Kevin Zheng
3097784cd7 Lower default MTU to 900 (#4985)
Some players continue to have "stuck at connected" issues connecting to
most servers, but apparently this issue has become more prominent in the
last two weeks or so?

It is almost certainly an MTU problem because there are at least two
servers on the hub that run a lower default MTU, and these players had
no problem connecting to them. For one of these reporters, I actually
increased the MTU to 1000 and they could no longer connect, and could
connect again once it was lowered to 900.

It's not clear what recent changes, either to the codebase or to the
public Internet that have been exercising this MTU issue more. For those
experiencing MTU issues, it seems that connecting to a less full server
results in higher probability of success.

Nevertheless, bringing down the default MTU and then possibly enabling
MTU expansion in the future would make this game playable for a small
but not insignificant bit of players.
2024-03-23 16:22:06 +01:00
Pieter-Jan Briers
0fb41e06c8 Upgrade to Lidgren v0.3.0 2024-03-23 16:21:34 +01:00
nikthechampiongr
0a79382a62 Add helper command for Player toolshed commands (#4987)
Allows you to invoke players:entity with a username to immediate get that player's attached entity(if any)
2024-03-23 15:27:54 +01:00
Tayrtahn
1f2b38a6d1 Code cleanup: Purge calls to obsolete EntityCoordinates methods (#4983) 2024-03-23 13:07:33 +11:00
Pieter-Jan Briers
f760929527 Add IMeterFactory implementation to IoC
This will be useful as we start using more System.Diagnostics.Metrics.
2024-03-22 22:19:18 +01:00
Pieter-Jan Briers
f5ade69f6d Show MTU in network debug panel. 2024-03-22 22:19:17 +01:00
Tayrtahn
9bfe889c86 Code cleanup: Purge obsolete MapManager methods (#4981)
* GetGrid

* GridExists

* TryGetGrid
2024-03-21 16:31:33 +01:00
Tayrtahn
e3954494e7 Just two (#4982) 2024-03-20 15:48:19 +01:00
Pieter-Jan Briers
6697e36e84 Fix ResizableMemoryRegion metrics
This was broken for more reason than one.

So the obvious reason they weren't counting right is because I forgot to add a call to update the metric in Shrink(). Whoops.

The second issue is that .NET's new metric types are unintuitive as shit and Microsoft would've done better to just link to the OpenTelemetry specification instead of whatever garbage they documented instead.

It turns out UpDownCounter just wasn't the correct choice at all! Because it only ever gets DELTA VALUES sent to collection tools, it can never be used for absolute measurements (such as memory usage) **despite Microsoft literally using examples of absolute values in their docs** ("queue size"). The OpenTelemetry spec is clear on this.

This means writing more code, because the API is shit. Great.
2024-03-20 11:58:11 +01:00
Pieter-Jan Briers
dae4041e61 Make CheckBox texture vertically centered.
This makes it look better in circumstances where the checkbox is vertically big, for example due to surrounding inline buttons.
2024-03-20 11:12:06 +01:00
Pieter-Jan Briers
390f399750 Add IMetricsManager.UpdateMetrics system
This callback enables code to update its metrics only when required. Needed this for SS14 since online admin count stats are not something I want to update on an "arbitrary" basis.

Tons of consideration and commenting for how this plays in with stuff like dotnet-counters. Added the metrics.update_interval CVar to act as a fallback for this event when dotnet-counters and such is in use.
2024-03-20 11:11:32 +01:00
Pieter-Jan Briers
28cf7442ce Fix naming of ResizableMemoryRegion metrics 2024-03-20 11:09:28 +01:00
Leon Friedrich
d8b03be651 Add debug assert to Dirty(uid, comp) (#4978) 2024-03-18 21:37:25 +01:00
Tayrtahn
16c7c71ca6 Code cleanup: Dirty(Comp) (#4979)
* The easy ones

* SharedPhysicsSystem
2024-03-18 21:36:52 +01:00
Leon Friedrich
0245c371ae Make the replay has error use the ReplayIgnoreErrors cvar (#4974) 2024-03-17 22:50:00 +01:00
Leon Friedrich
c8cb13f832 Fix serialization error logging (#4973) 2024-03-17 18:01:00 +01:00
Pieter-Jan Briers
86ecfaa56b Allow replays with mismatching type hashes.
Things like .NET bumps and engine minor version updates break the hashes. Just allow mismatching hashes, printing a warning to the log at least.

If the hash mismatches it'll explode with some weird error like invalidcast/index so whatever.

It'd be better if there was a "hey this might not work, you sure you want to load this?" screen, but I don't feel like rewriting the entire replay loading system right now.

Fixes https://github.com/space-wizards/SS14.Launcher/issues/142
2024-03-17 16:43:01 +01:00
Pieter-Jan Briers
43a32e7015 Fix dump_netserializer_type_map to show full data.
The output it gave didn't show the full info to generate the manifest (e.g. assembly version numbers)
2024-03-17 16:40:06 +01:00
Leon Friedrich
83371885fa Support transform states with unknown parents (#4972) 2024-03-17 11:28:50 +11:00
ShadowCommander
e686e1b4cc Add collection parsing to the dev window for UI (#4959)
* Debug window collection popup

* Try fixing?

* DevUI shows children and string collections

* XAMLify the popup

* Rename popup

* Deduplicate code

* Simplify popup creation

* MouseFilter.Ignore is default so it can be removed
2024-03-16 23:35:04 +01:00
ElectroJr
b1f9d011ce Version: 214.2.0 2024-03-16 16:17:59 -04:00
Leon Friedrich
a2d0504368 Replace PVS dictionaries with memory magic (#4795)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-03-17 06:57:13 +11:00
metalgearsloth
7aa951ca48 Add undetachable PVS flag (#4889)
Useful in some rare cases, mainly for grid-related activities.
Specifically:
- Audio entity where we never want it detached.
- FTL previs effects to show impending squish.
2024-03-16 14:58:08 +11:00
metalgearsloth
75a80b7a8a Fix tooltips underflowing left side of screen (#4952)
* Fix tooltips underflowing left side of screen

If the tooltip is so large it would clip the right side then it would underflow completely off-screen. This just clamps it instead.

* Better

* rubb
2024-03-16 14:45:17 +11:00
metalgearsloth
69706b0257 Fix global audio (#4964)
* Fix global audio

* Better
2024-03-16 11:59:57 +11:00
Pieter-Jan Briers
10b191dff8 Version: 214.1.1 2024-03-16 01:13:47 +01:00
Pieter-Jan Briers
92ab3fb64b Fix connection denials always redialling
Bug caused by changes to connection denial.

Fixes #4963
2024-03-16 01:13:34 +01:00
metalgearsloth
92a0c14383 Version: 214.1.0 2024-03-15 20:20:20 +11:00
metalgearsloth
5aaf6d0994 Fix VV for entity prototypes (#4956)
* Fix VV for entity prototypes

* Fix ProtoId
2024-03-15 20:16:09 +11:00
metalgearsloth
15f4da5e4b Audio limit fix (#4962)
I screm. See https://github.com/space-wizards/RobustToolbox/issues/4961
2024-03-15 20:14:49 +11:00
Leon Friedrich
a528e87f3d Add pvs_override_info command (#4958) 2024-03-15 14:32:23 +11:00
Pieter-Jan Briers
4af67b1394 Version: 214.0.0 2024-03-14 20:42:17 +01:00
metalgearsloth
e8de9b98d3 Add basic audio limits (#4921)
* Add basic audio limits

* RN

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-03-14 11:54:43 +01:00
Pieter-Jan Briers
a0ffeff4e5 Release notes for last commit 2024-03-14 08:10:54 +01:00
Pieter-Jan Briers
07654564f3 TextEdit fixes
Fixed being able to position the cursor vertically if placeholder text was visible and multi-line. This is because the code was using line break info for the place holder. On top of not being correct behavior, this caused further exceptions since the cursor would get outside the editable text rope.

Fixed index exception if you try to move left in an empty text edit.

Has regression tests.

Fixes #4957, fixes #4953
2024-03-14 08:09:28 +01:00
Pieter-Jan Briers
7fbf8d05eb Add ability to add structured deny data to NetConnectingArgs. (#4487)
* Add ability to add structured deny data to NetConnectingArgs.

Builds on the (horrifying) NetStructuredDisconnectMessages so that content can do more stuff.

To be used by SS14 to throttle people when they try to connect to a full server.

* Completely rewrite NetStructuredDisconnectMessages

So this class was a mess, and it was so bad it wasn't usable from content! System.Text.Json isn't sandbox safe (and I don't want to look into that right now), so the previous API surface of "pass the JsonNode around everywhere" just didn't work at all for content.

I decided the easiest solution would be to completely rewrite the entire thing to be a layer over a Dictionary<string, object> instead. This warranted a complete rewrite of the API, which should be fine as I doubt anybody was using it anyways.

Also, fully tested.
2024-03-14 07:27:22 +01:00
ShadowCommander
c12971cb9b Add decimal variable to Range Control rounding (#4954)
* Add decimal variable to Range Control rounding

* Remove unnecessary virtual and add ViewVariables
2024-03-13 00:43:27 +01:00
metalgearsloth
2b6381c332 Version: 213.0.0 2024-03-11 14:36:45 +11:00
TemporalOroboros
8149a3aaad Removes Obsolete BaseContainer methods. (#4843) 2024-03-11 14:35:31 +11:00
Kot
4b39bf1f2d Check entity for existence before drawing it in the SpriteView (#4886)
* Check entity for existence before drawing it in the SpriteView

* Slightly refactor ResolveEntity to be more straightforward
2024-03-11 14:34:44 +11:00
metalgearsloth
53394fff44 Add GetEntitiesInRange for sets (#4951)
* Add GetEntitiesInRange for sets

Need it for an old method.

* rn

* Fix SO
2024-03-11 13:43:13 +11:00
metalgearsloth
4bed20e070 Add RaiseSharedEvent (#4950)
Used in some rare cases on content (popups + pickup prediction). I was too lazy to make system proxy methods because it's very infrequent.
2024-03-10 19:33:45 +01:00
metalgearsloth
e4b6af09f1 Version: 212.2.0 2024-03-11 02:08:08 +11:00
metalgearsloth
1ef29ae781 Add some physics helpers (#4946)
Thought I had these but couldn't find them.
2024-03-11 02:05:50 +11:00
Vasilis
5686950421 Increase replay compressed size (#4925)
They tend to get cut off (or well did on wizden before pjb changed it to this exact value bigger along with another patch). Before you got around 2ish hours in a replay before it stopped. I doupt most servers will reach 6ish hours before this takes effect. But those servers can increase this value of needed.
2024-03-08 12:24:41 +01:00
Pieter-Jan Briers
2b54aa8984 Version: 212.1.0 2024-03-07 21:29:29 +01:00
Pieter-Jan Briers
859f150404 YIPPEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE IMAGESHARP VULNERABILITY 2024-03-07 21:29:00 +01:00
metalgearsloth
558f4b5b16 ScrollContainer niceties (#4940)
- If scroll is not visible we don't handle it. This means nested containers don't interfere with their parents anymore.
- Fallback to Y-scrolling for H-scroll only containers.
2024-03-05 23:32:22 +01:00
metalgearsloth
108366152b Fix TextureRect KeepCentered (#4937)
Easiest way to repro is set a non-1.0 UIScale and open the main menu up, the logo will be fonky.
I checked the control dimensions and this aligned with my expectations.
2024-03-05 23:28:28 +01:00
I.K
c55327e1d1 Set a minimum of 0.05 for the light resolution (#4942) 2024-03-05 23:27:39 +01:00
metalgearsloth
370e0fa0d0 Add nullable versions for protomanager (#4938) 2024-03-05 15:24:01 +11:00
metalgearsloth
4f9f82c20c Version: 212.0.1 2024-03-03 19:42:48 +11:00
metalgearsloth
43670a8ddd Pass array by-ref (#4936)
Yeah idk how to fix this otherwise but using this would be nicer.
2024-03-03 19:41:29 +11:00
metalgearsloth
250313e1ed Version: 212.0.0 2024-03-03 18:34:22 +11:00
metalgearsloth
18d511d4b6 Minor fixes (#4935)
- Swear I pushed this array change
- Update changelog
2024-03-03 18:32:51 +11:00
metalgearsloth
da9e5fb370 Add grid tile to Vector2 methods (#4851)
* Add grid tile to Vector2 methods

Avoids me having to do it on content.

* Release note

* Engine

* Collapsible

* Add entitylookup methods for parent / map

Content's done it a bunch so make it reusable.

* Add MaxDimension property to Box2

Sometimes I want to pretend it's a circle radius.

* Add GetLocalPosition to controls

In my case I want the mouse's position inside of the control to show something under it unless there's a better way.

* Add global rectangles for controls

Like my other PR used to check if mouse is inbounds on the control without doing some skrunkly caching with mousemove.

* Add dotted line drawing to screen handle

Probably needs anti-aliasing but idk an easy way to do it.

* weh

* weh

* a

* weh

* weh

* Optimise ChunkEnumerator

It never unioned the AABB passed in with the grid's AABB so it might inadvertantly iterate a lot more dummy chunks than it needs to.

This helps speedup FindGridsIntersecting.

* weh

* Add DrawPrimitives overload for List<Vector2>

Storing ValueList in a field seems sussy so this is the next best thing.

* weh

* Bump pool size

* oop wrong method

* Add drawing methods for lists

Content may be using it over a valuelist for whatever reason.

* Add more ValueList conveniences

* Add more CollectionExtension methods

Maybe array.resize is bad for sandbox coin, in which case I'd also settle for changing it to a list instead.

* Add ToMapCoordinates method for NetCoordinates

* fr

* mraow

* Release notes
2024-03-03 18:29:35 +11:00
Tayrtahn
e3bac382ce Add some helper methods to PVS Filters (#4933) 2024-03-03 11:51:40 +11:00
DrSmugleaf
179c6790b6 Add support for automatically networking component dictionary fields with entity keys and values (#4932)
* Add support for automatically networking component dictionary fields with entity keys and values

* Fix using

* Fix order

* Add support for both key and value being entity uid
2024-03-03 11:51:23 +11:00
metalgearsloth
a7db5634df Add more CollectionExtension methods (#4910)
Maybe array.resize is bad for sandbox coin, in which case I'd also settle for changing it to a list instead.
2024-03-03 11:51:13 +11:00
deltanedas
2daa86ff59 add PushMarkup to FormattedMessage (#4924)
Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-03-03 11:49:41 +11:00
metalgearsloth
d6803f5294 Add DrawPrimitives overload for List<Vector2> (#4900)
* Add DrawPrimitives overload for List<Vector2>

Storing ValueList in a field seems sussy so this is the next best thing.

* weh
2024-03-02 21:34:42 +01:00
metalgearsloth
bdcc0f7b9d Add more ValueList conveniences (#4911)
* Add more ValueList conveniences

* Review

* a
2024-03-02 22:15:18 +11:00
metalgearsloth
ce49aa47cf Add ToMapCoordinates method for NetCoordinates (#4914) 2024-03-02 21:47:05 +11:00
metalgearsloth
c7d48b2526 Remove ISerHooks obsoletion (#4928)
Still needed in rare cases so not really deprecated and we already discourage coders from using it where possible.
2024-02-28 19:01:08 +01:00
rene-descartes2021
6bb7f5b4ef Allow for use of new .NET 8 MSBuild property <UseArtifactsOutput> (#4929) 2024-02-28 19:00:51 +01:00
Brandon Hu
2974310450 chore: Remove some typos (#4927)
* chore: Remove some typos

* Apply suggestions from code review

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2024-02-27 12:31:29 +01:00
metalgearsloth
2694dce076 Version: 211.0.2 2024-02-25 14:14:45 +11:00
metalgearsloth
8960d1d995 Fix TextureRect scaling (#4923)
From moony my git patch didn't apply so done manually
2024-02-25 14:13:54 +11:00
metalgearsloth
0a4683d33e Version: 211.0.1 2024-02-23 19:32:16 +11:00
metalgearsloth
379bcfabe0 Fix Map Grid chunk enumerators (#4920)
They have empty AABBs so always returned early.
2024-02-23 19:31:20 +11:00
metalgearsloth
1d91838166 Version: 211.0.0 2024-02-23 18:01:07 +11:00
metalgearsloth
a5d4b8096f Move chunk enumerators to engine (#4901)
* Move chunk enumerators to engine

* notes

* Cleanup
2024-02-23 17:51:34 +11:00
Pieter-Jan Briers
a77eee5658 Fix async console command completions on ServerConsoleHost
Fixes #4828

Asynchronous console command completions were just not being run on the server, the wrong function was being called. Hooray.

This caused sudo to break because it actually uses an async command completion (as other command completions it invokes might in turn be async).
2024-02-23 00:22:58 +01:00
metalgearsloth
156187a0dd Optimise ChunkEnumerator (#4899)
* Optimise ChunkEnumerator

It never unioned the AABB passed in with the grid's AABB so it might inadvertantly iterate a lot more dummy chunks than it needs to.

This helps speedup FindGridsIntersecting.

* weh

* oop wrong method

* Update RELEASE-NOTES.md
2024-02-22 13:26:04 +11:00
metalgearsloth
852f002f59 Make collinear vertices check public (#4913) 2024-02-21 21:08:23 +11:00
metalgearsloth
9dc49c1904 Make physics constants public (#4912) 2024-02-20 22:02:28 -08:00
Moony
1995b13e5d Fix TextureRect. (#4908)
Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
2024-02-20 15:41:01 -08:00
Pieter-Jan Briers
f985d10ed9 In which I spend too much time SIMDizing PadVerticesV2 2024-02-20 12:27:20 +01:00
Pieter-Jan Briers
ae6cebbfbb Source gen reorganizations + component unpause generator. (#4896)
* Source gen reorganizations + component unpause generator.

This commit (and subsequent commits) aims to clean up our Roslyn plugin (source gens + analyzers) stack to more sanely re-use common code

I also built a new source-gen that automatically generates unpausing implementations for components, incrementing attributed TimeSpan field when unpaused.

* Fix warnings in all Roslyn projects
2024-02-20 10:15:32 +01:00
Pieter-Jan Briers
ef0bc1a2e4 Version: 210.1.1 2024-02-17 22:17:52 +01:00
Pieter-Jan Briers
72ba484f5b Changelog for key binding fix PRs 2024-02-17 22:12:33 +01:00
Pieter-Jan Briers
a70e511fcb Change default of ButtonGroup.IsNoneSetAllowed to true.
This brings default ButtonGroup behavior back to before #4841.

The original comments in the code *did* clearly intend for the other behavior to be the default, but the code was blatantly bugged (whoops) so this didn't happen. Content relied on this A LOT and it's quite sane behavior regardless so just change the default back call it a day.
2024-02-17 22:09:23 +01:00
Errant
e7f9e95525 fix default keybinds not knowing their place (#4903)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-02-17 22:03:36 +01:00
nikthechampiongr
bd908f9db6 Invalid keybinds will no longer mess up your game. (#4902)
* Fix issues when saving invalid keybinds.

* Fix horrible thing I forgot to fix.

* Change error log to debug

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2024-02-17 21:54:35 +01:00
Leon Friedrich
f8cb1729a3 Version: 210.1.0 2024-02-16 11:21:40 +13:00
Leon Friedrich
fd9d5c8aa8 Make ButtonGroup setter behaviour consistent with comment 2024-02-16 11:19:14 +13:00
Pieter-Jan Briers
4677296934 Add IsNoneSetAllowed mode to ButtonGroup.
This allows a button group to have no button pressed by default, which is the behavior of most radio buttons.
2024-02-15 01:16:33 +01:00
Pieter-Jan Briers
708f5dd376 Un-hardcode C:\Windows in MidiManager
Keeping an eye out for our bros who put Windows on a drive that isn't C:
2024-02-14 14:32:05 +01:00
Pieter-Jan Briers
4a06acda32 NetUserId implement ISelfSerialize
For @VasilisThePikachu
2024-02-14 01:08:38 +01:00
Pieter-Jan Briers
e7beb2032b Version: 210.0.3
This version changes nothing but I need it because I pushed the previous
version after amending it which means the tag is the wrong commit SORRY.
2024-02-13 16:11:47 +01:00
Pieter-Jan Briers
c7bd75f800 Version: 210.0.2 2024-02-13 16:06:28 +01:00
Pieter-Jan Briers
b4165e8661 ALSO revert changes to TextureRect from #4841
Stretch modes are broken or something, SS14 lobby art looks wrong. Can't be arsed to debug it myself.
2024-02-13 16:05:46 +01:00
Pieter-Jan Briers
ad339b5bfd Version: 210.0.1 2024-02-13 15:30:20 +01:00
Pieter-Jan Briers
e1197af8ce Revert changes to TextureButton from #4841
Breaks SS14 stylesheets due to not responding to style properties anymore.

At least one of those seems to be unfixable (ModulateSelf usage) which makes me think we should just deprecate ModulateSelf instead. However I'm not fixing that here.
2024-02-13 15:29:46 +01:00
metalgearsloth
102cadf3a6 Version: 210.0.0 2024-02-13 18:26:01 +11:00
Hannah Giovanna Dawson
e7723b61bc Add UnicodeRange to sandbox (#4894)
* Add GetEncoding to sandbox (#4892)
Need this struct allowlisted to for nice unicode sanitization.

* Add UnicodeRanges too

* Changelog

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-02-11 17:22:11 +01:00
Moony
a9d17337a3 RT Patches for UI improvements (#4841)
* fix up buttons

* wah

* ough

* huhwuhuahsdhsfdj

* loud incorrect buzzer

* wawa

* Allow XmlnsDefinition

* wawa

* Release notes.

* Expose keybind loading.

* address reviews and other things

---------

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-02-11 17:18:39 +01:00
Pieter-Jan Briers
74622bac83 Add DateTimeSerializer 2024-02-11 16:40:52 +01:00
Pieter-Jan Briers
a3047b1687 More warning fixes 2024-02-11 15:51:07 +01:00
Pieter-Jan Briers
3a55118143 Replace CVar OnValueChanged in systems with Subs.CVar 2024-02-11 13:29:27 +01:00
Pieter-Jan Briers
3c5fbc648a Add Subs.CVar helper for subscribing to CVar changes from entity systems 2024-02-11 13:29:27 +01:00
Pieter-Jan Briers
f9c39bce0b Use ValueList for EntitySystem subscriptions list.
That's a hundred something lists just gone and I don't have to ??=
2024-02-11 13:29:27 +01:00
Hannah Giovanna Dawson
0e8c803c0f Add GetEncoding to sandbox (#4892) 2024-02-10 17:59:13 +01:00
c4llv07e
6bb7b88c69 Save discord rich presense to the user config (#4884)
Signed-off-by: c4llv07e <kseandi@gmail.com>
2024-02-02 00:36:59 +01:00
571 changed files with 18989 additions and 7270 deletions

View File

@@ -7,6 +7,18 @@ indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
max_line_length = 120
# ReSharper properties
resharper_csharp_max_line_length = 120
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_keep_existing_attribute_arrangement = true
resharper_place_field_attribute_on_same_line = if_owner_is_single_line
resharper_wrap_chained_binary_patterns = chop_if_long
resharper_wrap_chained_method_calls = chop_if_long
[*.{csproj,xml,yml,dll.config,targets,props}]
indent_size = 2

View File

@@ -10,7 +10,7 @@ jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-latest, windows-latest ] # , macos-latest] - temporarily disabled due to libfreetype.dll errors.
runs-on: ${{ matrix.os }}
@@ -27,7 +27,8 @@ jobs:
run: dotnet restore
- name: Build
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Test Engine
- name: Robust.UnitTesting
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -- NUnit.ConsoleOut=0
- name: Robust.Analyzers.Tests
run: dotnet test --no-build Robust.Analyzers.Tests/Robust.Analyzers.Tests.csproj -- NUnit.ConsoleOut=0

View File

@@ -33,10 +33,10 @@ jobs:
mkdir "release/${{ steps.parse_version.outputs.version }}"
mv release/*.zip "release/${{ steps.parse_version.outputs.version }}"
- name: Upload files to centcomm
- name: Upload files to Suns
uses: appleboy/scp-action@master
with:
host: centcomm.spacestation14.io
host: suns.spacestation14.com
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
source: "release/${{ steps.parse_version.outputs.version }}"
@@ -46,7 +46,7 @@ jobs:
- name: Update manifest JSON
uses: appleboy/ssh-action@master
with:
host: centcomm.spacestation14.io
host: suns.spacestation14.com
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
script: /home/robust-build-push/push.ps1 ${{ steps.parse_version.outputs.version }}

View File

@@ -1,14 +1,24 @@
<Project>
<PropertyGroup>
<!--
We actually set ManagePackageVersionsCentrally manually in another import file.
Since .NET SDK 8.0.300, ManagePackageVersionsCentrally is automatically set if Directory.Packages.props exists.
https://github.com/NuGet/NuGet.Client/pull/5572
We actively negate this here, as we have some packages in tree we don't want such automatic behavior for.
We use Directory.Build.props to get copy the state *after* our MSBuild config but before Nuget's config.
-->
<ManagePackageVersionsCentrally />
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="ILReader.Core" Version="1.0.0.4" />
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.0" />
<PackageVersion Include="Linguini.Bundle" Version="0.1.3" />
<PackageVersion Include="Linguini.Bundle" Version="0.8.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.8.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
@@ -33,7 +43,7 @@
<PackageVersion Include="NUnit.Analyzers" Version="3.10.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageVersion Include="Nett" Version="0.15.0" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
<PackageVersion Include="Pidgin" Version="3.2.2" />
@@ -45,17 +55,20 @@
<PackageVersion Include="Serilog" Version="3.1.1" />
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.2" />
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
<PackageVersion Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.22621.5" />
<PackageVersion Include="TerraFX.Interop.Xlib" Version="6.4.0" />
<PackageVersion Include="VorbisPizza" Version="1.3.0" />
<PackageVersion Include="YamlDotNet" Version="13.7.1" />
<PackageVersion Include="prometheus-net" Version="8.2.1" />
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
<PackageVersion Include="PolySharp" Version="1.14.1" />
</ItemGroup>
</Project>

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

@@ -24,12 +24,16 @@
<RobustInjectorsConfiguration>$(Configuration)</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(Configuration)' == 'DebugOpt'">Debug</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(Configuration)' == 'Tools'">Release</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(UseArtifactsOutput)' == 'true' And '$(RuntimeIdentifier)' != ''">$(RobustInjectorsConfiguration)_$(RuntimeIdentifier)</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(UseArtifactsOutput)' == 'true'">$(RobustInjectorsConfiguration.ToLower())</RobustInjectorsConfiguration>
<CompileRobustXamlTaskAssemblyFile Condition="'$(UseArtifactsOutput)' != 'true'">$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(RobustInjectorsConfiguration)\netstandard2.0\Robust.Client.Injectors.dll</CompileRobustXamlTaskAssemblyFile>
<CompileRobustXamlTaskAssemblyFile Condition="'$(UseArtifactsOutput)' == 'true'">$(MSBuildThisFileDirectory)\..\..\artifacts\bin\Robust.Client.Injectors\$(RobustInjectorsConfiguration)\Robust.Client.Injectors.dll</CompileRobustXamlTaskAssemblyFile>
</PropertyGroup>
<UsingTask
Condition="'$(_RobustUseExternalMSBuild)' != 'true' And $(DesignTimeBuild) != true"
TaskName="CompileRobustXamlTask"
AssemblyFile="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(RobustInjectorsConfiguration)\netstandard2.0\Robust.Client.Injectors.dll"/>
AssemblyFile="$(CompileRobustXamlTaskAssemblyFile)"/>
<Target
Name="CompileRobustXaml"
Condition="Exists('@(IntermediateAssembly)')"

View File

@@ -1,4 +1,4 @@
# Release notes for RobustToolbox.
# Release notes for RobustToolbox.
<!--
NOTE: automatically updated sometimes by version.py.
@@ -54,6 +54,888 @@ END TEMPLATE-->
*None yet*
## 226.2.2
## 226.2.1
## 226.2.0
### New features
* `Control.VisibilityChanged()` virtual function.
* Add some System.Random methods for NextFloat and NextPolarVector2.
### Bugfixes
* Fixes ContainerSystem failing client-side debug asserts when an entity gets unanchored & inserted into a container on the same tick.
* Remove potential race condition on server startup from invoking ThreadPool.SetMinThreads.
### Other
* Increase default value of res.rsi_atlas_size.
* Fix internal networking logic.
* Updates of `OutputPanel` contents caused by change in UI scale are now deferred until visible. Especially important to avoid updates from debug console.
* Debug console is now limited to only keep `con.max_entries` entries.
* Non-existent resources are cached by `IResourceCache.TryGetResource`. This avoids the game constantly trying to re-load non-existent resources in common patterns such as UI theme texture fallbacks.
* Default IPv4 MTU has been lowered to 700.
* Update Robust.LoaderApi.
### Internal
* Split out PVS serialization from compression and sending game states.
* Turn broadphase contacts into an IParallelRobustJob and remove unnecessary GetMapEntityIds for every contact.
## 226.1.0
### New features
* Add some GetLocalEntitiesIntersecting methods for `Entity<T>`.
### Other
* Fix internal networking logic
## 226.0.0
### Breaking changes
* `IEventBus.RaiseComponentEvent` now requires an EntityUid argument.
* The `AddedComponentEventArgs` and `RemovedComponentEventArgs` constructors are now internal
### New features
* Allow RequestScreenTexture to be set in overlays.
### Bugfixes
* Fix AnimationCompletedEvent not always going out.
## 225.0.0
### Breaking changes
* `NetEntity.Parse` and `TryParse` will now fail to parse empty strings.
* Try to prevent EventBus looping. This also caps the amount of directed component subscriptions for a particular component to 256.
### New features
* `IPrototypeManager.TryIndex` will now default to logging errors if passed an invalid prototype id struct (i,e., `EntProtoId` or `ProtoId<T>`). There is a new optional bool argument to disable logging errors.
* `Eye` now allows its `Position` to be set directly. Please only do this with the `FixedEye` child type constructed manually.
* Engine now respects the hub's `can_skip_build` parameter on info query, fixing an issue where the first hub advertisement fails due to ACZ taking too long.
* Add GetSession & TryGetSession to ActorSystem.
* Raise an event when an entity's name is changed.
### Bugfixes
* The `ent` toolshed command now takes `NetEntity` values, fixing parsing in practical uses.
* Fix ComponentFactory test mocks.
* Fix LookupFlags missing from a couple of EntityLookupSystem methods.
### Other
* Improved engine's Happy Eyeballs implementation, should result in more usage of IPv6 for HTTP APIs when available.
* Remove CompIdx locks to improve performance inside Pvs at higher player counts.
* Avoid a read lock in GetEntityQuery to also improve performance.
* Mark `EntityManager.System<T>` as Pure.
## 224.1.1
### Bugfixes
* Fixed UserInterfaceSystem sometimes throwing a key-not-found exception when trying to close UIs.
## 224.1.0
### New features
* `ServerIntegrationInstance` has new methods for adding dummy player sessions for tests that require multiple players.
* Linguini has been updated to v0.8.1. Errors will now be logged when a duplicate localization key is found.
* Added `UserInterfaceSystem.SetUi()` for modifying the `InterfaceData` associated with some BUI.
* Added the `EntityPrototypeView` control for spawning & rendering an entity prototype.
### Bugfixes
* Fix `UserInterfaceSystem` spamming client side errors when entities with UIs open are deleted while outside of PVS range.
* Fix Toolshed's EnumTypeParse not working enum values with upercase characters.
* Fixed `incmd` command not working due to an invalid cast.
### Other
* There have been various performance improvements to replay loading & playback.
### Internal
* Added `DummySession` and `DummyChannel` classes for use in integration tests and benchmarks to fool the server into thinking that there are multiple players connected.
* Added `ICommonSessionInternal` and updated `CommonSession` so that the internal setters now go through that interface.
## 224.0.1
### Bugfixes
* Fixes PVS throwing exceptions when invalid entities are passed to `ExpandPvsEvent`. Now it just logs an error.
* Fixes BUIs not properly closing, resulting in invalid entities in `UserInterfaceUserComponent.OpenInterfaces`
* Fixes an unknown/invalid prototype exception sometimes being thrown when running ``IPrototypeManager.ResolveResults()`
## 224.0.0
### Breaking changes
* `Matrix3` has been replaced with `System.Numerics.Matrix3x2`. Various Matrix related methods have been turned into extension methods in the `Matrix3Helpers` class.
* Engine `EntityCategory` prototype IDs have been changed to use CamelCase. I.e., `hideSpawnMenu` -> `HideSpawnMenu`
* Prototypes can now be implicitly cast `ProtoId<T>` or `EntProtoId` ID structs. The new implicit cast might cause previous function calls to be ambiguous.
### New features
* `Array.Clear(Array)` is now available in the sandbox.
* BUIs now use `ExpandPvsEvent`. I.e., if a player has a UI open, then the entity associated with that UI will always get sent to the player by the PVS system.
* Added `cvar_subs` command for listing all subscribers to cvar changes
* Entity categories have been reworked
* Each category now has a `HideSpawnMenu` field. The old `HideSpawnMenu` category is now just a normal category with that field set to true.
* Reworked category inheritance. Inheritance can now be disabled per category using a `Inheritable` field.
* Entity prototypes can now be automatically added to categories based on the components that they have, either by specifying components when defining the category in yml, or by adding the EntityCategoryAttribute to the component class.
### Bugfixes
* Fixed client-side BUI error log spam if an unknown entity has a UI open.
* Fixed placement manager spawning entities with incorrect rotations.
### Other
* Added a try-catch block to BUI constructors, to avoid clients getting stuck in error loops while applying states.
* Attempting to play sounds on terminating entities no longer logs an error.
## 223.3.0
### New features
* Better exception logging for IRobustJob.
* Add SetGridAudio helper for SharedAudioSystem.
### Bugfixes
* Fix placement manager not setting entity rotation correctly.
* Fix grid-based audio not playing correctly.
## 223.2.0
### New features
* Added several new `FormattedMessage` methods for better exception tolerance when parsing markup. Several existing methods have been marked as obsolete, with new renamed methods taking their place.
## 223.1.2
### Bugfixes
* `MapGridComponent.LastTileModifiedTick` is now actually networked to clients.
## 223.1.1
### Bugfixes
* Fixed an exception caused by enum cvars using integer type values instead of enum values
## 223.1.0
### Other
* Various `ContainerSystem` methods have been obsoleted in favour of overrides that take in an `Entity` struct instead of `EntityUid`
* Various `EntityCoordinates` methods have been obsoleted with replacements added to `SharedTransformSystem`
## 223.0.0
### Breaking changes
* The `ComponentState` class is now abstract. Networked components that don't have state information now just return a null state.
* The way that delta component states work has changed. It now expects there to be two different state classes, only one of which should implement `IComponentDeltaState<TFullState>`
### New features
* A new `replay.checkpoint_min_interval` cvar has been added. It can be used to limit the frequency at which checkpoints are generated when loading a replay.
* Added `IConfigurationManager.OnCVarValueChanged`. This is a c# event that gets invoked whenever any cvar value changes.
### Bugfixes
* `IEyeManager.GetWorldViewbounds()` and `IEyeManager.GetWorldViewbounds()` should now return the correct bounds if the main viewport does not take up the whole screen.
### Other
* The default values of various replay related cvars have been changed to try and reduce memory usage.
## 222.4.0
### New features
* Added the following types from `System.Numerics` to the sandbox: `Complex`, `Matrix3x2`, `Matrix4x4`, `Plane`, `Quaternion`, `Vector3`, `Vector4`.
## 222.3.0
### New features
* `ITileDefinition.EditorHidden` allows hiding a tile from the tile spawn panel.
* Ordered event subscriptions now take child types into account, so ordering based on a shared type will work.
### Bugfixes
* Cross-map BUI range checks now work.
* Paused entities update on prototype reload.
### Other
* Fixed build compatibility with .NET 8.0.300 SDK, due to changes in how Central Package Management behaves.
* Physics component has delta states to reduce network usage.
## 222.2.0
### New features
* Added `EntityQuery.Comp()` (abbreviation of `GetComponent()`)
### Bugfixes
* Fix `SerializationManager.TryGetVariableType` checking the wrong property.
* Fixed GrammarSystem mispredicting a character's gender
### Other
* User interface system now performs range checks in parallel
## 222.1.1
### Bugfixes
* Fixed never setting BoundUserInterface.State.
### Other
* Add truncate for filesaving.
* Add method for getting the type of a data field by name from ISerializationManager.
## 222.1.0
### New features
* Added `BoundKeyEventArgs.IsRepeat`.
* Added `net.lidgren_log_warning` and `net.lidgren_log_error` CVars.
### Bugfixes
* Fix assert trip when holding repeatable keybinds.
### Other
* Updated Lidgren to v0.3.1. This should provide performance improvements if warning/error logs are disabled.
## 222.0.0
### Breaking changes
* Mark IComponentFactory argument in EntityPrototype as mandatory.
### New features
* Add `EntProtoId<T>` to check for components on the attached entity as well.
### Bugfixes
* Fix PVS iterating duplicate chunks for multiple viewsubscriptions.
### Other
* Defer clientside BUI opens if it's the first state that comes in.
## 221.2.0
### New features
* Add SetMapAudio helper to SharedAudioSystem to setup map-wide audio entities.
* Add SetWorldRotNoLerp method to SharedTransformSystem to avoid client lerping.
### Bugfixes
* `SpriteComponent.CopyFrom` now copies `CopyToShaderParameters` configuration.
## 221.1.0
## 221.0.0
### Breaking changes
* `EntParentChangedMessage.OldMapId` is now an `EntityUid` instead of `MapId`
* `TransformSystem.DetachParentToNull()` is being renamed to `DetachEntity`
* The order in which `MoveEvent` handlers are invoked has been changed to prioritise engine subscriptions
### New features
* Added `UpdateHovered()` and `SetHovered()` to `IUserInterfaceManager`, for updating or modifying the currently hovered control.
* Add SwapPositions to TransformSystem to swap two entity's transforms.
### Bugfixes
* Improve client gamestate exception tolerance.
### Other
* If the currently hovered control is disposed, `UserInterfaceManager` will now look for a new control, rather than just setting the hovered control to null.
### Internal
* Use more `EntityQuery<T>` internally in EntityManager and PhysicsSystem.
## 220.2.0
### New features
* RSIs can now specify load parameters, mimicking the ones from `.png.yml`. Currently only disabling sRGB is supported.
* Added a second UV channel to Clyde's vertex format. On regular batched sprite draws, this goes 0 -> 1 across the sprite quad.
* Added a new `CopyToShaderParameters` system for `SpriteComponent` layers.
## 220.1.0
### Bugfixes
* Fix client-side replay exceptions due to dropped states when recording.
### Other
* Remove IP + HWId from ViewVariables.
* Close BUIs upon disconnect.
## 220.0.0
### Breaking changes
* Refactor UserInterfaceSystem.
- The API has been significantly cleaned up and standardised, most noticeably callers don't need to worry about TryGetUi and can rely on either HasUi, SetUiState, CloseUi, or OpenUi to handle their code as appropriate.
- Interface data is now stored via key rather than as a flat list which is a breaking change for YAML.
- BoundUserInterfaces can now be completely handled via Shared code. Existing Server-side callers will behave similarly to before.
- BoundUserInterfaces now properly close in many more situations, additionally they are now attached to the entity so reconnecting can re-open them and they can be serialized properly.
## 219.2.0
### New features
* Add SetMapCoordinates to TransformSystem.
* Improve YAML Linter and validation of static fields.
### Bugfixes
* Fix DebugCoordsPanel freezing when hovering a control.
### Other
* Optimise physics networking to not dirty every tick of movement.
## 219.1.3
### Bugfixes
* Fix map-loader not pausing pre-init maps when not actively overwriting an existing map.
## 219.1.2
### Bugfixes
* Fix map-loader not map-initialising grids when loading into a post-init map.
## 219.1.1
### Bugfixes
* Fix map-loader not map-initialising maps when overwriting a post-init map.
## 219.1.0
### New features
* Added a new optional arguments to various entity spawning methods, including a new argument to set the entity's rotation.
### Bugfixes
* Fixes map initialisation not always initialising all entities on a map.
### Other
* The default value of the `auth.mode` cvar has changed
## 219.0.0
### Breaking changes
* Move most IMapManager functionality to SharedMapSystem.
## 218.2.0
### New features
* Control layout properties such as `Margin` can now be set via style sheets.
* Expose worldposition in SpriteComponent.Render
* Network audio entity Play/Pause/Stop states and playback position.
* Add `Disabled` functionality to `Slider` control.
## 218.1.0
### New features
* Add IEquatable.Equals to the sandbox.
* Enable roslyn extensions tests in CI.
* Add a VerticalTabContainer control to match the horizontal one.
### Bugfixes
* Fix divison remainder issue for Colors, fixing purples.
### Other
* Default hub address (`hub.hub_urls`) has been changed to `https://hub.spacestation14.com/`.
## 218.0.0
### Breaking changes
* `Robust.Shared.Configuration.EnvironmentVariables` is now internal and no longer usable by content.
### New features
* Add TryGetRandom to EntityManager to get a random entity with the specified component and TryGetRandom to IPrototypeManager to return a random prototype of the specified type.
* Add CopyData to AppearanceSystem.
* Update UI themes on prototype reloads.
* Allow scaling the line height of a RichTextLabel.
* You can now specify CVar overrides via environment variable with the `ROBUST_CVAR_*` prefix. For example `ROBUST_CVAR_game__hostname=foobar` would set the appropriate CVar. Double underscores in the environment variable name are replaced with ".".
* Added non-generic variant of `GetCVar` to `IConfigurationManager`.
* Add type tracking to FieldNotFoundErrorNode for serialization.
* Distance between lines of a `RichTextLabel` can now be modified with `LineHeightScale`.
* UI theme prototypes are now updated when reloaded.
* New `RA0025` analyzer diagnostic warns for manual assignment to `[Dependency]` fields.
### Bugfixes
* Request headers in `IStatusHandlerContext` are now case-insensitive.
* SetWorldPosition rotation now more closely aligns with prior behavior.
* Fix exception when inspecting elements in some cases.
* Fix HTTP errors on watchdog ping not being reported.
### Other
* Add an analyzer for redundantly assigning to dependency fields.
### Internal
* Remove redundant Exists checks in ContainerSystem.
* Improve logging on watchdog pings.
## 217.2.1
### Bugfixes
* Fix LineEdit tests on engine.
### Internal
* Make various ValueList enumerators access the span directly for performance.
## 217.2.0
### New features
* Added `AddComponents` and `RemoveComponents` methods to EntityManager that handle EntityPrototype / ComponentRegistry bulk component changes.
* Add double-clicking to LineEdit.
### Bugfixes
* Properly ignore non-hard fixtures for IntersectRayWithPredicate.
* Fix nullable TimeSpan addition on some platforms.
## 217.1.0
### New features
* Added `IRobustRandom.GetItems` extension methods for randomly picking multiple items from a collections.
* Added `SharedPhysicsSystem.EffectiveCurTime`. This is effectively a variation of `IGameTiming.CurTime` that takes into account the current physics sub-step.
### Bugfixes
* Fix `MapComponent.LightingEnabled` not leaving FOV rendering in a broken state.
### Internal
* `Shuffle<T>(Span<T>, System.Random)` has been removed, just use the builtin method.
## 217.0.0
### Breaking changes
* TransformSystem.SetWorldPosition and SetWorldPositionRotation will now also perform parent updates as necessary. Previously it would just set the entity's LocalPosition which may break if they were inside of a container. Now they will be removed from their container and TryFindGridAt will run to correctly parent them to the new position. If the old functionality is desired then you can use GetInvWorldMatrix to update the LocalPosition (bearing in mind containers may prevent this).
### New features
* Implement VV for AudioParams on SoundSpecifiers.
* Add AddUi to the shared UI system.
### Bugfixes
* Fix the first measure of ScrollContainer bars.
## 216.0.0
### Breaking changes
* The `net.low_lod_distance` cvar has been replaced with a new `net.pvs_priority_range`. Instead of limiting the range at which all entities are sent to a player, it now extends the range at which high priorities can be sent. The default value of this new cvar is 32.5, which is larger than the default `net.pvs_range` value of 25.
### New features
* You can now specify a component to not be saved to map files with `[UnsavedComponent]`.
* Added `ITileDefinitionManager.TryGetDefinition`.
* The map loader now tries to preserve the `tilemap` contents of map files, which should reduce diffs when re-saving a map after the game's internal tile IDs have changed.
### Bugfixes
* Fix buffered audio sources not being disposed.
## 215.3.1
### Bugfixes
* Revert zstd update.
## 215.3.0
### New features
* `EntityQuery<T>` now has `HasComp` and `TryComp` methods that are shorter than its existing ones.
* Added `PlacementInformation.UseEditorContext`.
* Added `Vector2Helpers` functions for comparing ranges between vectors.
### Bugfixes
* `Texture.GetPixel()`: fixed off-by-one with Y coordinate.
* `Texture.GetPixel()`: fix stack overflow when reading large images.
* `Texture.GetPixel()`: use more widely compatible OpenGL calls.
### Other
* Disabled `net.mtu_expand` again by default, as it was causing issues.
* Updated `SharpZstd` dependency.
## 215.2.0
### New features
* Implement basic VV for SoundSpecifiers.
### Bugfixes
* Fix QueueDel during EndCollideEvents from throwing while removing contacts.
## 215.1.0
### New features
* Add a CompletionHelper for audio filepaths that handles server packaging.
* Add Random.NextAngle(min, max) method and Pick for `ValueList<T>`.
* Added an `ICommonSession` parser for toolshed commands.
### Bugfixes
## 215.0.0
### Breaking changes
* Update Lidgren to 0.3.0
### New features
* Made a new `IMetricsManager` interface with an `UpdateMetrics` event that can be used to update Prometheus metrics whenever they are scraped.
* Also added a `metrics.update_interval` CVar to go along with this, when metrics are scraped without usage of Prometheus directly.
* IoC now contains an `IMeterFactory` implementation that you can use to instantiate metric meters.
* `net.mtu_ipv6` CVar allows specifying a different MTU value for IPv6.
* Allows `player:entity` to take a parameter representing the player name.
* Add collection parsing to the dev window for UI.
* Add a debug assert to Dirty(uid, comp) to catch mismatches being passed in.
### Bugfixes
* Support transform states with unknown parents.
* Fix serialization error logging.
* Fix naming of ResizableMemoryRegion metrics.
* Fix uncaught overflow exception when parsing NetEntities.
### Other
* The replay system now allows loading a replay with a mismatching serializer type hash. This means replays should be more robust against future version updates (engine security patches or .NET updates).
* `CheckBox`'s interior texture is now vertically centered.
* Lidgren.Network has been updated to [`v0.3.0`](https://github.com/space-wizards/SpaceWizards.Lidgren.Network/blob/v0.3.0/RELEASE-NOTES.md).
* Lowered default IPv4 MTU to 900 (from 1000).
* Automatic MTU expansion (`net.mtu_expand`) is now enabled by default.
### Internal
* Cleanup some Dirty component calls internally.
## 214.2.0
### New features
* Added a `Undetachable` entity metadata flag, which stops the client from moving an entity to nullspace when it moves out of PVS range.
### Bugfixes
* Fix tooltips not clamping to the left side of the viewport.
* Fix global audio property not being properly set.
### Internal
* The server game state / PVS code has been rewritten. It should be somewhat faster now, albeit at the cost of using more memory. The current engine version may be unstable.
## 214.1.1
### Bugfixes
* Fixed connection denial always causing redial.
## 214.1.0
### New features
* Added the `pvs_override_info` command for debugging PVS overrides.
### Bugfixes
* Fix VV for prototype structs.
* Fix audio limits for clientside audio.
## 214.0.0
### Breaking changes
* `NetStructuredDisconnectMessages` has received a complete overhaul and has been moved to `NetDisconnectMessage`. The API is no longer designed such that consumers must pass around JSON nodes, as they are not in sandbox (and clunky).
### New features
* Add a basic default concurrent audio limit of 16 for a single filepath to avoid overflowing audio sources.
* `NetConnectingArgs.Deny()` can now pass along structured data that will be received by the client.
### Bugfixes
* Fixed cursor position bugs when an empty `TextEdit` has a multi-line place holder.
* Fixed empty `TextEdit` throwing exception if cursor is moved left.
## 213.0.0
### Breaking changes
* Remove obsoleted BaseContainer methods.
### New features
* Add EntityManager.RaiseSharedEvent where the event won't go to the attached client but will be predicted locally on their end.
* Add GetEntitiesInRange override that takes in EntityCoordinates and an EntityUid hashset.
### Bugfixes
* Check if a sprite entity is deleted before drawing in SpriteView.
## 212.2.0
### New features
* Add IsHardCollidable to SharedPhysicsSystem to determine if 2 entities would collide.
### Other
* Double the default maximum replay size.
## 212.1.0
### New features
* Add nullable methods for TryIndex / HasIndex on IPrototypeManager.
### Bugfixes
* Fix TextureRect alignment where the strech mode is KeepCentered.
## 212.0.1
### Bugfixes
* Fix passing array by `this` instead of by `ref`.
## 212.0.0
### Breaking changes
* Change Collapsible controls default orientations to Vertical.
### New features
* Expose the Label control for Collapsible controls.
* Add GetGridPosition that considers physics center-of-mass.
* Add TileToVector methods to get the LocalPosition of tile-coords (taking into account tile size).
* Add some more helper methods to PVS filters around EntityUids.
* Add support for Dictionary AutoNetworkedFields.
* Add EnsureLength method for arrays.
* Add PushMarkup to FormattedMessage.
* Add DrawPrimitives overload for `List<Vector2>`
* Add more ValueList ctors that are faster.
* Add ToMapCoordinates method for NetCoordinates.
### Other
* Remove ISerializationHooks obsoletion as they are useful in some rare cases.
### Internal
* Bump max pool size for robust jobs.
## 211.0.2
### Bugfixes
* Fix TextureRect scaling not handling UIScale correctly.
## 211.0.1
### Bugfixes
* Fix GridChunkEnumerator on maps.
## 211.0.0
### Breaking changes
* Moved ChunkIndicesEnumerator to engine and to a re-useable namespace at Robust.Shared/Maps.
### New features
* Added an Enlarged method for Box2Rotated.
### Internal
* Significantly optimise ChunkEnumerator / FindGridsIntersecting in certain use cases by intersecting the grid's AABB with the local AABB to avoid iterating dummy chunks.
## 210.1.1
### Bugfixes
* Fixed multiple recent bugs with key binding storage.
### Other
* Change default of `ButtonGroup.IsNoneSetAllowed` to `true`. This makes it default again to the previous (unintentional) behavior.
## 210.1.0
### New features
* `NetUserId` implements `ISelfSerialize` so can be used in data fields.
* `ButtonGroup.IsNoneSetAllowed` to allow a button group to have no buttons pressed by default.
## 210.0.3
## 210.0.2
### Bugfixes
* Revert changes to `TextureRect` too.
## 210.0.1
### Bugfixes
* Revert changes to `TextureButton` that broke style property handling.
## 210.0.0
### New features
* Controls can now hook before, after, and during rendering of their children.
* IRenderHandle is now a public API, with the caveat that it's properties and methods are unstable.
* ButtonGroup now exposes what buttons it contains, alongside which is currently pressed.
* OptionButton has additional styleclasses, and has a hook for modifying it's internal buttons.
* PanelContainer.GetStyleBox() is now protected rather than private.
* TextureButton now uses a TextureRect instead of custom drawing code.
* TextureRect has additional style properties exposed.
* A new property, TextureSizeTarget, was added, which allows specifying a size in virtual pixels that the control should attempt to draw at.
* Stretch mode is now a style property.
* Scale is now a style property.
* Avalonia.Metadata.XmlnsDefinitionAttribute is now permitted by the sandbox.
* Add MaxDimension property to Box2 to return the higher of the Width or Height.
* Add GetLocalPosition to convert ScreenCoordinates to coordinates relative to the control. Ignores window.
* Add GlobalRect and GlobalPixelRect for controls to get their UIBox2i in screen terms.
* Add dotted line drawing to DrawingHandleScreen.
* You can use `Subs.CVar()` from an entity systems to subscribe to CVar changes. This is more convenient than `IConfigurationManager.OnValueChanged` as it automatically unsubscribes on system shutdown.
* There is now a built-in type serializer for `DateTime`, so you can put `DateTime`s in your data fields.
* `System.Text.Unicode.UnicodeRange` and `UnicodeRanges` are now available in the sandbox.
### Bugfixes
* UI drawing now properly accounts for a control's draw routine potentially mangling the current matrix.
* UI roots now properly update when the global stylesheet is changed. They previously only did so if they had a dedicated stylesheet (which is the one case where they would be unaffected by a global sheet update.
## 209.0.1
### Bugfixes

View File

@@ -2,4 +2,7 @@
id: Audio
name: Audio
description: Audio entity used by engine
save: false
save: false
components:
- type: Transform
gridTraversal: false

View File

@@ -1,7 +1,7 @@
- type: entity
id: debugRotation
abstract: true
categories: [ debug ]
categories: [ Debug ]
components:
- type: Sprite
netsync: false

View File

@@ -1,17 +1,20 @@
# debug related entities
- type: entityCategory
id: debug
id: Debug
name: entity-category-name-debug
description: entity-category-desc-debug
suffix: entity-category-suffix-debug
# entities that spawn other entities
- type: entityCategory
id: spawner
id: Spawner
name: entity-category-name-spawner
description: entity-category-desc-spawner
# entities that should be hidden from the spawn menu
# simple category that just exists to hide prototypes in spawn menus
- type: entityCategory
id: hideSpawnMenu
id: HideSpawnMenu
name: entity-category-name-hide
description: entity-category-desc-hide
hideSpawnMenu: true
inheritable: false

View File

@@ -9,8 +9,10 @@ cmd-parse-failure-float = {$arg} is not a valid float.
cmd-parse-failure-bool = {$arg} is not a valid bool.
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
cmd-parse-failure-enum = {$arg} is not a {$enum} Enum.
cmd-parse-failure-grid = {$arg} is not a valid grid.
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
cmd-parse-failure-session = There is no session with username: {$username}
cmd-error-file-not-found = Could not find file: {$file}.
cmd-error-dir-not-found = Could not find directory: {$dir}.
@@ -42,6 +44,13 @@ cmd-cvar-compl-list = List available CVars
cmd-cvar-arg-name = <name | ?>
cmd-cvar-value-hidden = <value hidden>
## 'cvar_subs' command
cmd-cvar_subs-desc = Lists the OnValueChanged subscriptions for a CVar.
cmd-cvar_subs-help = Usage: cvar_subs <name>
cmd-cvar_subs-invalid-args = Must provide exactly one argument.
cmd-cvar_subs-arg-name = <name>
## 'list' command
cmd-list-desc = Lists available commands, with optional search filter
cmd-list-help = Usage: list [filter]
@@ -244,9 +253,6 @@ cmd-bind-arg-command = <InputCommand>
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
cmd-net-draw-interp-help = Usage: net_draw_interp
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
cmd-net-draw-interp-help = Usage: net_draw_interp
cmd-net-watch-ent-desc = Dumps all network updates for an EntityId to the console.
cmd-net-watch-ent-help = Usage: net_watchent <0|EntityUid>
@@ -298,16 +304,9 @@ cmd-savegrid-help = savegrid <gridID> <Path>
cmd-testbed-desc = Loads a physics testbed on the specified map.
cmd-testbed-help = testbed <mapid> <test>
cmd-saveconfig-desc = Saves the client configuration to the config file.
cmd-saveconfig-help = saveconfig
## 'flushcookies' command
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
cmd-flushcookies-desc = Flush CEF cookie storage to disk
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
Note that the actual operation is asynchronous.
## 'addcomp' command
cmd-addcomp-desc = Adds a component to an entity.
cmd-addcomp-help = addcomp <uid> <componentName>
@@ -383,9 +382,9 @@ cmd-tp-desc = Teleports a player to any location in the round.
cmd-tp-help = tp <x> <y> [<mapID>]
cmd-tpto-desc = Teleports the current player or the specified players/entities to the location of the first player/entity.
cmd-tpto-help = tpto <username|uid> [username|uid]...
cmd-tpto-destination-hint = destination (uid or username)
cmd-tpto-victim-hint = entity to teleport (uid or username)
cmd-tpto-help = tpto <username|uid> [username|NetEntity]...
cmd-tpto-destination-hint = destination (NetEntity or username)
cmd-tpto-victim-hint = entity to teleport (NetEntity or username)
cmd-tpto-parse-error = Cant resolve entity or player: {$str}
cmd-listplayers-desc = Lists all players currently connected.
@@ -445,9 +444,6 @@ cmd-showanchored-help = Usage: showanchored
cmd-dmetamem-desc = Dumps a type's members in a format suitable for the sandbox configuration file.
cmd-dmetamem-help = Usage: dmetamem <type>
cmd-dmetamem-desc = Displays chunk bounds for the purposes of rendering.
cmd-dmetamem-help = Usage: showchunkbb <type>
cmd-launchauth-desc = Load authentication tokens from launcher data to aid in testing of live servers.
cmd-launchauth-help = Usage: launchauth <account name>
@@ -490,7 +486,7 @@ cmd-net_entityreport-help = Usage: net_entityreport
cmd-net_refresh-desc = Requests a full server state.
cmd-net_refresh-help = Usage: net_refresh
cmd-net_graph-desc = Toggles the net statistics pannel.
cmd-net_graph-desc = Toggles the net statistics panel.
cmd-net_graph-help = Usage: net_graph
cmd-net_watchent-desc = Dumps all network updates for an EntityId to the console.
@@ -514,9 +510,6 @@ cmd-profsnap-help = Usage: profsnap
cmd-devwindow-desc = Dev Window
cmd-devwindow-help = Usage: devwindow
cmd-devwindow-desc = Open file
cmd-devwindow-help = Usage: testopenfile
cmd-scene-desc = Immediately changes the UI scene/state.
cmd-scene-help = Usage: scene <className>
@@ -527,14 +520,11 @@ cmd-hwid-desc = Returns the current HWID (HardWare ID).
cmd-hwid-help = Usage: hwid
cmd-vvread-desc = Retrieve a path's value using VV (View Variables).
cmd-vvread-desc = Usage: vvread <path>
cmd-vvread-help = Usage: vvread <path>
cmd-vvwrite-desc = Modify a path's value using VV (View Variables).
cmd-vvwrite-help = Usage: vvwrite <path>
cmd-vv-desc = Opens View Variables (VV).
cmd-vv-help = Usage: vv <path|entity ID|guihover>
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
cmd-vvinvoke-help = Usage: vvinvoke <path> [arguments...]
@@ -566,3 +556,9 @@ cmd-reloadtiletextures-help = Usage: reloadtiletextures
cmd-audio_length-desc = Shows the length of an audio file
cmd-audio_length-help = Usage: audio_length { cmd-audio_length-arg-file-name }
cmd-audio_length-arg-file-name = <file name>
## PVS
cmd-pvs-override-info-desc = Prints information about any PVS overrides associated with an entity.
cmd-pvs-override-info-empty = Entity {$nuid} has no PVS overrides.
cmd-pvs-override-info-global = Entity {$nuid} has a global override.
cmd-pvs-override-info-clients = Entity {$nuid} has a session override for {$clients}.

View File

@@ -1,8 +1,9 @@
entity-category-name-debug = Debug
entity-category-desc-debug = Entity prototypes intended for debugging & testing.
entity-category-suffix-debug = Debug
entity-category-name-spawner = Spawner
entity-category-desc-spawner = Entity prototypes that spawn other entities.
entity-category-name-hide = Hidden
entity-category-desc-hide = Entity prototypes that should be hidden from the spawn menu
entity-category-desc-hide = Entity prototypes that should be hidden from entity spawn menus

View File

@@ -70,9 +70,9 @@ command-description-ls-in =
command-description-methods-get =
Returns all methods associated with the input type.
command-description-methods-overrides =
Returns all methods overriden on the input type.
Returns all methods overridden on the input type.
command-description-methods-overridesfrom =
Returns all methods overriden from the given type on the input type.
Returns all methods overridden from the given type on the input type.
command-description-cmd-moo =
Asks the important questions.
command-description-cmd-descloc =
@@ -418,6 +418,6 @@ command-description-tee =
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.
On its own, this means it'll print the command's help message.
command-description-comp-rm =
Removes the given component from the entity.

View File

@@ -10,3 +10,18 @@ view-variable-instance-entity-client-components-search-bar-placeholder = Search
view-variable-instance-entity-server-components-search-bar-placeholder = Search
view-variable-instance-entity-add-window-server-components = Add Component [S]
view-variable-instance-entity-add-window-client-components = Add Component [C]
## SoundSpecifier
vv-sound-none = None
vv-sound-path = Path
vv-sound-collection = Collection
vv-sound-volume = volume
vv-sound-pitch = Pitch
vv-sound-max-distance = Max Distance
vv-sound-rolloff-factor = Rolloff Factor
vv-sound-reference-distance = Reference Distance
vv-sound-loop = Loop
vv-sound-play-offset = Play Offset (s)
vv-sound-variation = Pitch variation

View File

@@ -20,11 +20,16 @@ public sealed class AccessAnalyzer_Test
{
TestState =
{
AdditionalReferences = { typeof(AccessAnalyzer).Assembly },
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.AccessAttribute.cs",
"Robust.Shared.Analyzers.AccessPermissions.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);

View File

@@ -0,0 +1,8 @@
// OH BOY. TURNS OUT IT GETS EVEN MORE CURSED.
//
// So because we're compiling a copy of Robust.Roslyn.Shared into every analyzer project,
// the test project sees multiple copies of it. This would make it impossible to use.
// UNLESS you use this obscure C# feature called "extern alias"
// that I guarantee you you've never heard of before, and are now concerned about.
extern alias SerializationGenerator;

View File

@@ -0,0 +1,340 @@
extern alias SerializationGenerator;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using NUnit.Framework;
using SerializationGenerator::Robust.Roslyn.Shared;
using SerializationGenerator::Robust.Serialization.Generator;
namespace Robust.Analyzers.Tests;
[TestFixture]
[TestOf(typeof(ComponentPauseGenerator))]
[Parallelizable(ParallelScope.All)]
public sealed class ComponentPauseGeneratorTest
{
private const string TypesCode = """
global using System;
global using Robust.Shared.Analyzers;
global using Robust.Shared.GameObjects;
namespace Robust.Shared.Analyzers
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class AutoGenerateComponentPauseAttribute : Attribute
{
public bool Dirty = false;
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class AutoPausedFieldAttribute : Attribute;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class AutoNetworkedFieldAttribute : Attribute
{
}
}
namespace Robust.Shared.GameObjects
{
public interface IComponent;
}
""";
[Test]
public void TestBasic()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
component.Foo += args.PausedTime;
}
}
}
""");
}
[Test]
public void TestNullable()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan? Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
if (component.Foo.HasValue)
component.Foo = component.Foo.Value + args.PausedTime;
}
}
}
""");
}
[Test]
public void TestAutoState()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField, AutoNetworkedField]
public TimeSpan Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
component.Foo += args.PausedTime;
Dirty(uid, component);
}
}
}
""");
}
[Test]
public void TestExplicitDirty()
{
var result = RunGenerator("""
[AutoGenerateComponentPause(Dirty = true)]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
component.Foo += args.PausedTime;
Dirty(uid, component);
}
}
}
""");
}
[Test]
public void TestDiagnosticNotIComponent()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent
{
[AutoPausedField]
public TimeSpan Foo;
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseNotComponent, new LinePositionSpan(new LinePosition(1, 28), new LinePosition(1, 40)))
]);
}
[Test]
public void TestDiagnosticNoFields()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
public TimeSpan Foo;
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseNoFields, new LinePositionSpan(new LinePosition(1, 28), new LinePosition(1, 40)))
]);
}
[Test]
public void TestDiagnosticNoParentAttribute()
{
var result = RunGenerator("""
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan Foo, Fooz;
[AutoPausedField]
public TimeSpan Bar { get; set; }
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(3, 20), new LinePosition(3, 23))),
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(3, 25), new LinePosition(3, 29))),
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(6, 20), new LinePosition(6, 23)))
]);
}
[Test]
public void TestDiagnosticWrongType()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public int Foo, Fooz;
[AutoPausedField]
public int Bar { get; set; }
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(4, 15), new LinePosition(4, 18))),
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(4, 20), new LinePosition(4, 24))),
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(7, 15), new LinePosition(7, 18)))
]);
}
private static void ExpectSource(GeneratorRunResult result, string expected)
{
Assert.That(result.GeneratedSources, Has.Length.EqualTo(1));
var source = result.GeneratedSources[0];
Assert.That(source.SourceText.ToString(), Is.EqualTo(expected));
}
private static void ExpectNoSource(GeneratorRunResult result)
{
Assert.That(result.GeneratedSources, Is.Empty);
}
private static void ExpectNoDiagnostics(GeneratorRunResult result)
{
Assert.That(result.Diagnostics, Is.Empty);
}
private static void ExpectDiagnostics(GeneratorRunResult result, (string code, LinePositionSpan span)[] diagnostics)
{
Assert.Multiple(() =>
{
Assert.That(result.Diagnostics, Has.Length.EqualTo(diagnostics.Length));
foreach (var (code, span) in diagnostics)
{
Assert.That(
result.Diagnostics.Any(x => x.Id == code && x.Location.GetLineSpan().Span == span),
$"Expected diagnostic with code {code} and location {span}");
}
});
}
private static GeneratorRunResult RunGenerator(string source)
{
var compilation = (Compilation)CSharpCompilation.Create("compilation",
new[]
{
CSharpSyntaxTree.ParseText(source, path: "Source.cs"),
CSharpSyntaxTree.ParseText(TypesCode, path: "Types.cs")
},
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
var generator = new ComponentPauseGenerator();
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out _);
var result = driver.GetRunResult();
return result.Results[0];
}
}

View File

@@ -0,0 +1,58 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.DependencyAssignAnalyzer>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class DependencyAssignAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<DependencyAssignAnalyzer, NUnitVerifier>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.IoC.DependencyAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.IoC;
public sealed class Foo
{
[Dependency]
private object? Field;
public Foo()
{
Field = "A";
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,9): warning RA0025: Tried to assign to [Dependency] field 'Field'. Remove [Dependency] or inject it via field injection instead.
VerifyCS.Diagnostic().WithSpan(10, 9, 10, 20).WithArguments("Field"));
}
}

View File

@@ -0,0 +1,57 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.NoUncachedRegexAnalyzer>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class NoUncachedRegexAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<NoUncachedRegexAnalyzer, NUnitVerifier>()
{
TestState =
{
Sources = { code }
},
};
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using System.Text.RegularExpressions;
public static class Foo
{
public static void Bad()
{
Regex.Replace("foo", "bar", "baz");
}
public static void Good()
{
var r = new Regex("bar");
r.Replace("foo", "baz");
}
}
""";
await Verifier(code,
// /0/Test0.cs(7,9): warning RA0026: Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.
VerifyCS.Diagnostic().WithSpan(7, 9, 7, 43)
);
}
}

View File

@@ -1,7 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<SkipRobustAnalyzer>true</SkipRobustAnalyzer>
</PropertyGroup>
<Import Project="..\MSBuild\Robust.Properties.targets"/>
<Import Project="..\MSBuild\Robust.Engine.props"/>
<!-- Engine source files needed to make the tests work -->
<ItemGroup>
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LogicalName="Robust.Shared.Analyzers.AccessAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LogicalName="Robust.Shared.Analyzers.AccessPermissions.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
@@ -23,5 +34,6 @@
<ItemGroup>
<ProjectReference Include="..\Robust.Analyzers\Robust.Analyzers.csproj"/>
<ProjectReference Include="..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" Aliases="SerializationGenerator" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
namespace Robust.Analyzers.Tests;
public static class TestHelper
{
public static void AddEmbeddedSources(SolutionState state, params string[] embeddedFiles)
{
AddEmbeddedSources(state, (IEnumerable<string>) embeddedFiles);
}
public static void AddEmbeddedSources(SolutionState state, IEnumerable<string> embeddedFiles)
{
foreach (var fileName in embeddedFiles)
{
using var stream = typeof(AccessAnalyzer_Test).Assembly.GetManifestResourceStream(fileName)!;
state.Sources.Add((fileName, SourceText.From(stream)));
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
using Robust.Shared.Analyzers.Implementation;
namespace Robust.Analyzers

View File

@@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
using static Microsoft.CodeAnalysis.SymbolEqualityComparer;
namespace Robust.Analyzers;
@@ -16,7 +17,7 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor ByRefEventSubscribedByValueRule = new(
Diagnostics.IdByRefEventSubscribedByValue,
"By-ref event subscribed to by value",
"Tried to subscribe to a by-ref event '{0}' by value.",
"Tried to subscribe to a by-ref event '{0}' by value",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -26,7 +27,7 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor ByRefEventRaisedByValueRule = new(
Diagnostics.IdByRefEventRaisedByValue,
"By-ref event raised by value",
"Tried to raise a by-ref event '{0}' by value.",
"Tried to raise a by-ref event '{0}' by value",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -36,7 +37,7 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor ByValueEventRaisedByRefRule = new(
Diagnostics.IdValueEventRaisedByRef,
"Value event raised by-ref",
"Tried to raise a value event '{0}' by-ref.",
"Tried to raise a value event '{0}' by-ref",
"Usage",
DiagnosticSeverity.Error,
true,

View File

@@ -5,6 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
using Robust.Shared.Serialization.Manager.Definition;
namespace Robust.Analyzers;
@@ -18,7 +20,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
Diagnostics.IdDataDefinitionPartial,
"Type must be partial",
"Type {0} is a DataDefinition but is not partial.",
"Type {0} is a DataDefinition but is not partial",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -28,7 +30,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
Diagnostics.IdNestedDataDefinitionPartial,
"Type must be partial",
"Type {0} contains nested data definition {1} but is not partial.",
"Type {0} contains nested data definition {1} but is not partial",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -38,7 +40,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
Diagnostics.IdDataFieldWritable,
"Data field must not be readonly",
"Data field {0} in data definition {1} is readonly.",
"Data field {0} in data definition {1} is readonly",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -48,15 +50,25 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
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.",
"Data field property {0} in data definition {1} does not have a setter",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to add a setter."
);
private static readonly DiagnosticDescriptor DataFieldRedundantTagRule = new(
Diagnostics.IdDataFieldRedundantTag,
"Data field has redundant tag specified",
"Data field {0} in data definition {1} has an explicitly set tag that matches autogenerated tag",
"Usage",
DiagnosticSeverity.Info,
true,
"Make sure to remove the tag string from the data field attribute."
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule,
DataFieldRedundantTagRule
);
public override void Initialize(AnalysisContext context)
@@ -124,6 +136,11 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
}
if (HasRedundantTag(fieldSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
}
}
}
@@ -148,6 +165,11 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
}
if (HasRedundantTag(propertySymbol))
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
}
}
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
@@ -247,6 +269,29 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
return false;
}
private static bool HasRedundantTag(ISymbol symbol)
{
if (!IsDataField(symbol, out var _, out var attribute))
return false;
// No args, no problem
if (attribute.ConstructorArguments.Length == 0)
return false;
// If a tag is explicitly specified, it will be the first argument...
var tagArgument = attribute.ConstructorArguments[0];
// ...but the first arg could also something else, since tag is optional
// so we make sure that it's a string
if (tagArgument.Value is not string explicitName)
return false;
// Get the name that sourcegen would provide
var automaticName = DataDefinitionUtility.AutoGenerateTag(symbol.Name);
// If the explicit name matches the sourcegen name, we have a redundancy
return explicitName == automaticName;
}
private static bool IsImplicitDataDefinition(ITypeSymbol type)
{
if (HasAttribute(type, ImplicitDataDefinitionNamespace))

View File

@@ -1,23 +1,23 @@
#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;
using static Robust.Roslyn.Shared.Diagnostics;
namespace Robust.Analyzers;
[ExportCodeFixProvider(LanguageNames.CSharp)]
public sealed class DefinitionFixer : CodeFixProvider
{
private const string DataFieldAttributeName = "DataField";
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable,
IdDataFieldRedundantTag
);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
@@ -34,6 +34,8 @@ public sealed class DefinitionFixer : CodeFixProvider
return RegisterDataFieldFix(context, diagnostic);
case IdDataFieldPropertyWritable:
return RegisterDataFieldPropertyFix(context, diagnostic);
case IdDataFieldRedundantTag:
return RegisterRedundantTagFix(context, diagnostic);
}
}
@@ -72,6 +74,68 @@ public sealed class DefinitionFixer : CodeFixProvider
return document.WithSyntaxRoot(root);
}
private static async Task RegisterRedundantTagFix(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
var span = diagnostic.Location.SourceSpan;
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<MemberDeclarationSyntax>().First();
if (token == null)
return;
// Find the DataField attribute
AttributeSyntax? dataFieldAttribute = null;
foreach (var attributeList in token.AttributeLists)
{
foreach (var attribute in attributeList.Attributes)
{
if (attribute.Name.ToString() == DataFieldAttributeName)
{
dataFieldAttribute = attribute;
break;
}
}
if (dataFieldAttribute != null)
break;
}
if (dataFieldAttribute == null)
return;
context.RegisterCodeFix(CodeAction.Create(
"Remove explicitly set tag",
c => RemoveRedundantTag(context.Document, dataFieldAttribute, c),
"Remove explicitly set tag"
), diagnostic);
}
private static async Task<Document> RemoveRedundantTag(Document document, AttributeSyntax syntax, CancellationToken cancellation)
{
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
if (syntax.ArgumentList == null)
return document;
AttributeSyntax? newSyntax;
if (syntax.ArgumentList.Arguments.Count == 1)
{
// If this is the only argument, delete the ArgumentList so we don't leave empty parentheses
newSyntax = syntax.RemoveNode(syntax.ArgumentList, SyntaxRemoveOptions.KeepNoTrivia);
}
else
{
// Remove the first argument, which is the tag
var newArgs = syntax.ArgumentList.Arguments.RemoveAt(0);
var newArgList = syntax.ArgumentList.WithArguments(newArgs);
// Construct a new attribute with the tag removed
newSyntax = syntax.WithArgumentList(newArgList);
}
root = root!.ReplaceNode(syntax, newSyntax!);
return document.WithSyntaxRoot(root);
}
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);

View File

@@ -0,0 +1,61 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DependencyAssignAnalyzer : DiagnosticAnalyzer
{
private const string DependencyAttributeType = "Robust.Shared.IoC.DependencyAttribute";
private static readonly DiagnosticDescriptor Rule = new (
Diagnostics.IdDependencyFieldAssigned,
"Assignment to dependency field",
"Tried to assign to [Dependency] field '{0}'. Remove [Dependency] or inject it via field injection instead.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterOperationAction(CheckAssignment, OperationKind.SimpleAssignment);
}
private static void CheckAssignment(OperationAnalysisContext context)
{
if (context.Operation is not ISimpleAssignmentOperation assignment)
return;
if (assignment.Target is not IFieldReferenceOperation fieldRef)
return;
var field = fieldRef.Field;
var attributes = field.GetAttributes();
if (attributes.Length == 0)
return;
var depAttribute = context.Compilation.GetTypeByMetadataName(DependencyAttributeType);
if (!HasAttribute(attributes, depAttribute))
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, assignment.Syntax.GetLocation(), field.Name));
}
private static bool HasAttribute(ImmutableArray<AttributeData> attributes, ISymbol symbol)
{
foreach (var attribute in attributes)
{
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, symbol))
return true;
}
return false;
}
}

View File

@@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
using Document = Microsoft.CodeAnalysis.Document;
namespace Robust.Analyzers

View File

@@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;

View File

@@ -2,6 +2,7 @@ using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers
{

View File

@@ -0,0 +1,66 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class NoUncachedRegexAnalyzer : DiagnosticAnalyzer
{
private const string RegexTypeName = "Regex";
private const string RegexType = $"System.Text.RegularExpressions.{RegexTypeName}";
private static readonly DiagnosticDescriptor Rule = new (
Diagnostics.IdUncachedRegex,
"Use of uncached static Regex function",
"Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public static readonly HashSet<string> BadFunctions =
[
"Count",
"EnumerateMatches",
"IsMatch",
"Match",
"Matches",
"Replace",
"Split"
];
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterOperationAction(CheckInvocation, OperationKind.Invocation);
}
private static void CheckInvocation(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation invocation)
return;
// All Regex functions we care about are static.
var targetMethod = invocation.TargetMethod;
if (!targetMethod.IsStatic)
return;
// Bail early.
if (targetMethod.ContainingType.Name != "Regex")
return;
var regexType = context.Compilation.GetTypeByMetadataName(RegexType);
if (!SymbolEqualityComparer.Default.Equals(regexType, targetMethod.ContainingType))
return;
if (!BadFunctions.Contains(targetMethod.Name))
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.Syntax.GetLocation()));
}
}

View File

@@ -3,6 +3,7 @@ using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
@@ -31,7 +32,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor InvalidNotNullableImplementationRule = new (
Diagnostics.IdInvalidNotNullableFlagImplementation,
"Invalid NotNullable flag implementation.",
"Invalid NotNullable flag implementation",
"NotNullable flag is either not typed as bool, or does not have a default value equaling false",
"Usage",
DiagnosticSeverity.Error,
@@ -41,7 +42,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor InvalidNotNullableTypeRule = new (
Diagnostics.IdInvalidNotNullableFlagType,
"Failed to resolve type parameter",
"Failed to resolve type parameter \"{0}\".",
"Failed to resolve type parameter \"{0}\"",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -49,7 +50,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor NotNullableFlagValueTypeRule = new (
Diagnostics.IdNotNullableFlagValueType,
"NotNullable flag not supported for value types.",
"NotNullable flag not supported for value types",
"Value types as generic arguments are not supported for NotNullable flags",
"Usage",
DiagnosticSeverity.Error,

View File

@@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;

View File

@@ -1,31 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" />
</ItemGroup>
<ItemGroup>
<!-- Needed for NotNullableFlagAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" />
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<ItemGroup>
<!-- Needed for FriendAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" />
<Compile Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" />
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LinkBase="Implementations" />
<Compile Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LinkBase="Implementations" />
</ItemGroup>
<ItemGroup>
<!-- Needed for PreferGenericVariantAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" />
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<ItemGroup>
<!-- Needed for DataDefinitionAnalyzer. -->
<Compile Include="..\Robust.Shared\Serialization\Manager\Definition\DataDefinitionUtility.cs" LinkBase="Implementations" />
</ItemGroup>
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
<PropertyGroup>
<Nullable>disable</Nullable>
<!--
Rider seems to get really confused with hot reload if we directly compile in the above-linked classes.
As such, they have an #if to change their namespace in this project.
-->
<DefineConstants>$(DefineConstants);ROBUST_ANALYZERS_IMPL</DefineConstants>
</PropertyGroup>
</Project>

View File

@@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers
{

View File

@@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using Robust.Shared.Analyzers;
using Robust.Shared.Collections;
namespace Robust.Benchmarks.Collections;
[Virtual]
public class ValueListEnumerationBenchmarks
{
[Params(4, 16, 64)]
public int N { get; set; }
private sealed class Data(int i)
{
public readonly int I = i;
}
private ValueList<Data> _valueList;
private Data[] _array = default!;
[GlobalSetup]
public void Setup()
{
var list = new List<Data>(N);
for (var i = 0; i < N; i++)
{
list.Add(new(i));
}
_array = list.ToArray();
_valueList = new(list.ToArray());
}
[Benchmark]
public int ValueList()
{
var total = 0;
foreach (var ev in _valueList)
{
total += ev.I;
}
return total;
}
[Benchmark]
public int ValueListSpan()
{
var total = 0;
foreach (var ev in _valueList.Span)
{
total += ev.I;
}
return total;
}
[Benchmark]
public int Array()
{
var total = 0;
foreach (var ev in _array)
{
total += ev.I;
}
return total;
}
[Benchmark]
public int Span()
{
var total = 0;
foreach (var ev in _array.AsSpan())
{
total += ev.I;
}
return total;
}
}

View File

@@ -26,7 +26,8 @@ public sealed class DefaultSQLConfig : IConfig
public IEnumerable<IExporter> GetExporters()
{
yield return SQLExporter.Default;
//yield return SQLExporter.Default;
yield break;
}
public IEnumerable<IColumnProvider> GetColumnProviders() => DefaultConfig.Instance.GetColumnProviders();

View File

@@ -26,9 +26,8 @@ public partial class AddRemoveComponentBenchmark
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
var map = _simulation.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
for (var i = 0; i < N; i++)
{

View File

@@ -29,8 +29,8 @@ public partial class ComponentIteratorBenchmark
Comps = new A[N+2];
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
var map = _simulation.CreateMap().MapId;
var coords = new MapCoordinates(default, map);
for (var i = 0; i < N; i++)
{

View File

@@ -31,8 +31,8 @@ public partial class GetComponentBenchmark
Comps = new A[N+2];
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
var map = _simulation.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
for (var i = 0; i < N; i++)
{

View File

@@ -29,10 +29,9 @@ public partial class SpawnDeleteEntityBenchmark
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
_mapCoords = new MapCoordinates(0, 0, new MapId(1));
var uid = _simulation.AddMap(_mapCoords.MapId);
_entCoords = new EntityCoordinates(uid, 0, 0);
var (map, mapId) = _simulation.CreateMap();
_mapCoords = new MapCoordinates(default, mapId);
_entCoords = new EntityCoordinates(map, 0, 0);
}
[Benchmark(Baseline = true)]

View File

@@ -15,11 +15,10 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Npgsql;
using Npgsql.Internal;
using Npgsql.Internal.TypeHandlers;
using Npgsql.Internal.TypeHandling;
namespace Robust.Benchmarks.Exporters;
/*
public sealed class SQLExporter : IExporter
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
@@ -98,7 +97,9 @@ public sealed class SQLExporter : IExporter
public string Name => "sql";
}
*/
/*
// https://github.com/npgsql/efcore.pg/issues/1107#issuecomment-945126627
class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
{
@@ -138,6 +139,7 @@ class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
=> null; // Let the built-in resolver do this
}
}
*/
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<BenchmarkContext>
{

View File

@@ -91,8 +91,7 @@ public class RecursiveMoveBenchmark : RobustIntegrationTest
// Set up map and spawn player
server.WaitPost(() =>
{
var mapId = mapMan.CreateMap();
var map = mapMan.GetMapEntityId(mapId);
var map = server.ResolveDependency<SharedMapSystem>().CreateMap(out var mapId);
var gridComp = mapMan.CreateGridEntity(mapId);
var grid = gridComp.Owner;
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));

View File

@@ -239,6 +239,7 @@ namespace Robust.Build.Tasks
engine.LogErrorEvent(new BuildErrorEventArgs("XAMLIL", "", res.FilePath, 0, 0, 0, 0,
$"{res.FilePath}: {e.Message}", "", "CompileRobustXaml"));
}
res.Remove();
}
return true;
}

View File

@@ -1,19 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Compile Link="XamlX\filename" Include="../XamlX/src/XamlX/**/*.cs" />
<Compile Remove="../XamlX/src/XamlX/**/SreTypeSystem.cs" />
<Compile Remove="../XamlX/src/XamlX/obj/**" />
<Compile Include="..\Robust.Client\UserInterface\ControlPropertyAccess.cs" />
</ItemGroup>
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
<PropertyGroup>
<!-- XamlX doesn't do NRTs. -->
<Nullable>disable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -39,7 +39,7 @@ public sealed class AudioOverlay : Overlay
protected internal override void Draw(in OverlayDrawArgs args)
{
var localPlayer = _playerManager.LocalPlayer?.ControlledEntity;
var localPlayer = _playerManager.LocalEntity;
if (args.ViewportControl == null || localPlayer == null)
return;
@@ -74,11 +74,13 @@ public sealed class AudioOverlay : Overlay
output.Clear();
output.AppendLine("Audio Source");
output.AppendLine("Runtime:");
output.AppendLine($"- Distance: {_audio.GetAudioDistance(distance.Length()):0.00}");
output.AppendLine($"- Occlusion: {posOcclusion:0.0000}");
output.AppendLine("Params:");
output.AppendLine($"- RolloffFactor: {comp.RolloffFactor:0.0000}");
output.AppendLine($"- Volume: {comp.Volume:0.0000}");
output.AppendLine($"- Reference distance: {comp.ReferenceDistance}");
output.AppendLine($"- Max distance: {comp.MaxDistance}");
output.AppendLine($"- Reference distance: {comp.ReferenceDistance:0.00}");
output.AppendLine($"- Max distance: {comp.MaxDistance:0.00}");
var outputText = output.ToString().Trim();
var dimensions = screenHandle.GetDimensions(_font, outputText, 1f);
var buffer = new Vector2(3f, 3f);

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Robust.Shared;
using Robust.Shared.GameObjects;
namespace Robust.Client.Audio;
public sealed partial class AudioSystem
{
/*
* Handles limiting concurrent sounds for audio to avoid blowing the source budget on one sound getting spammed.
*/
private readonly Dictionary<string, int> _playingCount = new();
private int _maxConcurrent;
private void InitializeLimit()
{
Subs.CVar(CfgManager, CVars.AudioDefaultConcurrent, SetConcurrentLimit, true);
}
private void SetConcurrentLimit(int obj)
{
_maxConcurrent = obj;
}
private bool TryAudioLimit(string sound)
{
if (string.IsNullOrEmpty(sound))
return true;
ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(_playingCount, sound, out _);
if (count >= _maxConcurrent)
return false;
count++;
return true;
}
private void RemoveAudioLimit(string sound)
{
if (!_playingCount.TryGetValue(sound, out var count))
return;
count--;
if (count <= 0)
{
_playingCount.Remove(sound);
return;
}
_playingCount[sound] = count;
}
}

View File

@@ -41,6 +41,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
[Dependency] private readonly IParallelManager _parMan = default!;
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
[Dependency] private readonly IAudioInternal _audio = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedTransformSystem _xformSys = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
@@ -51,6 +52,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
private EntityUid? _listenerGrid;
private UpdateAudioJob _updateAudioJob;
private EntityQuery<PhysicsComponent> _physicsQuery;
private float _maxRayLength;
@@ -106,8 +108,9 @@ public sealed partial class AudioSystem : SharedAudioSystem
SubscribeNetworkEvent<PlayAudioEntityMessage>(OnEntityAudio);
SubscribeNetworkEvent<PlayAudioPositionalMessage>(OnEntityCoordinates);
CfgManager.OnValueChanged(CVars.AudioAttenuation, OnAudioAttenuation, true);
CfgManager.OnValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged, true);
Subs.CVar(CfgManager, CVars.AudioAttenuation, OnAudioAttenuation, true);
Subs.CVar(CfgManager, CVars.AudioRaycastLength, OnRaycastLengthChanged, true);
InitializeLimit();
}
private void OnAudioState(EntityUid uid, AudioComponent component, ref AfterAutoHandleStateEvent args)
@@ -123,6 +126,33 @@ public sealed partial class AudioSystem : SharedAudioSystem
{
component.Source.SetAuxiliary(null);
}
switch (component.State)
{
case AudioState.Playing:
component.StartPlaying();
break;
case AudioState.Paused:
component.Pause();
break;
case AudioState.Stopped:
component.StopPlaying();
component.PlaybackPosition = 0f;
break;
}
// If playback position changed then update it.
if (!string.IsNullOrEmpty(component.FileName))
{
var position = (float) ((component.PauseTime ?? Timing.CurTime) - component.AudioStart).TotalSeconds;
var currentPosition = component.Source.PlaybackPosition;
var diff = Math.Abs(position - currentPosition);
if (diff > 0.1f)
{
component.PlaybackPosition = position;
}
}
}
/// <summary>
@@ -133,13 +163,6 @@ public sealed partial class AudioSystem : SharedAudioSystem
_audio.SetMasterGain(value);
}
public override void Shutdown()
{
CfgManager.UnsubValueChanged(CVars.AudioAttenuation, OnAudioAttenuation);
CfgManager.UnsubValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged);
base.Shutdown();
}
private void OnAudioPaused(EntityUid uid, AudioComponent component, ref EntityPausedEvent args)
{
component.Pause();
@@ -170,26 +193,32 @@ public sealed partial class AudioSystem : SharedAudioSystem
return;
}
SetupSource(component, audioResource);
SetupSource((uid, component), audioResource);
component.Loaded = true;
}
private void SetupSource(AudioComponent component, AudioResource audioResource, TimeSpan? length = null)
private void SetupSource(Entity<AudioComponent> entity, AudioResource audioResource, TimeSpan? length = null)
{
var source = _audio.CreateAudioSource(audioResource);
var component = entity.Comp;
if (source == null)
if (TryAudioLimit(component.FileName))
{
Log.Error($"Error creating audio source for {audioResource}");
DebugTools.Assert(false);
source = component.Source;
}
var newSource = _audio.CreateAudioSource(audioResource);
component.Source = source;
if (newSource == null)
{
Log.Error($"Error creating audio source for {audioResource}");
DebugTools.Assert(false);
}
else
{
component.Source = newSource;
}
}
// Need to set all initial data for first frame.
ApplyAudioParams(component.Params, component);
source.Global = component.Global;
component.Source.Global = component.Global;
// Don't play until first frame so occlusion etc. are correct.
component.Gain = 0f;
@@ -209,6 +238,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
{
// Breaks with prediction?
component.Source.Dispose();
RemoveAudioLimit(component.FileName);
}
private void OnAudioAttenuation(int obj)
@@ -300,59 +331,26 @@ public sealed partial class AudioSystem : SharedAudioSystem
}
Vector2 worldPos;
var gridUid = xform.ParentUid;
component.Volume = component.Params.Volume;
Vector2 delta;
// Handle grid audio differently by using nearest-edge instead of entity centre.
// Handle grid audio differently by using grid position.
if ((component.Flags & AudioFlags.GridAudio) != 0x0)
{
// It's our grid so max volume.
if (_listenerGrid == gridUid)
{
component.Volume = component.Params.Volume;
component.Occlusion = 0f;
component.Position = listener.Position;
return;
}
// TODO: Need a grid-optimised version because this is gonna be expensive.
// Just to avoid clipping on and off grid or nearestPoint changing we'll
// always set the sound to listener's pos, we'll just manually do gain ourselves.
if (_physics.TryGetNearest(gridUid, listener, out _, out var gridDistance))
{
// Out of range
if (gridDistance > component.MaxDistance)
{
component.Gain = 0f;
return;
}
var paramsGain = VolumeToGain(component.Params.Volume);
// Thought I'd never have to manually calculate gain again but this is the least
// unpleasant audio I could get at the moment.
component.Gain = paramsGain * _audio.GetAttenuationGain(
gridDistance,
component.Params.RolloffFactor,
component.Params.ReferenceDistance,
component.Params.MaxDistance);
component.Position = listener.Position;
return;
}
// Can't get nearest point so don't play anymore.
component.Gain = 0f;
return;
var parentUid = xform.ParentUid;
worldPos = _maps.GetGridPosition(parentUid);
}
else
{
worldPos = _xformSys.GetWorldPosition(entity);
}
worldPos = _xformSys.GetWorldPosition(entity);
component.Volume = component.Params.Volume;
// Max distance check
var delta = worldPos - listener.Position;
delta = worldPos - listener.Position;
var distance = delta.Length();
// Out of range so just clip it for us.
if (distance > component.MaxDistance)
if (GetAudioDistance(distance) > component.MaxDistance)
{
// Still keeps the source playing, just with no volume.
component.Gain = 0f;
@@ -367,8 +365,15 @@ public sealed partial class AudioSystem : SharedAudioSystem
}
// Update audio occlusion
var occlusion = GetOcclusion(listener, delta, distance, entity);
component.Occlusion = occlusion;
if ((component.Flags & AudioFlags.NoOcclusion) == AudioFlags.NoOcclusion)
{
component.Occlusion = 0f;
}
else
{
var occlusion = GetOcclusion(listener, delta, distance, entity);
component.Occlusion = occlusion;
}
// Update audio positions.
component.Position = worldPos;
@@ -418,13 +423,13 @@ public sealed partial class AudioSystem : SharedAudioSystem
return false;
}
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string filename, EntityCoordinates coordinates,
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityCoordinates coordinates,
AudioParams? audioParams = null)
{
return PlayStatic(filename, Filter.Local(), coordinates, true, audioParams);
}
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string filename, EntityUid uid, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityUid uid, AudioParams? audioParams = null)
{
return PlayEntity(filename, Filter.Local(), uid, true, audioParams);
}
@@ -451,8 +456,11 @@ public sealed partial class AudioSystem : SharedAudioSystem
/// </summary>
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="audioParams"></param>
private (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, AudioParams? audioParams = null, bool recordReplay = true)
private (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, AudioParams? audioParams = null, bool recordReplay = true)
{
if (string.IsNullOrEmpty(filename))
return null;
if (recordReplay && _replayRecording.IsRecording)
{
_replayRecording.RecordReplayMessage(new PlayAudioGlobalMessage
@@ -484,8 +492,11 @@ public sealed partial class AudioSystem : SharedAudioSystem
/// </summary>
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="entity">The entity "emitting" the audio.</param>
private (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, EntityUid entity, AudioParams? audioParams = null, bool recordReplay = true)
private (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, EntityUid entity, AudioParams? audioParams = null, bool recordReplay = true)
{
if (string.IsNullOrEmpty(filename))
return null;
if (recordReplay && _replayRecording.IsRecording)
{
_replayRecording.RecordReplayMessage(new PlayAudioEntityMessage
@@ -525,8 +536,11 @@ public sealed partial class AudioSystem : SharedAudioSystem
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="coordinates">The coordinates at which to play the audio.</param>
/// <param name="audioParams"></param>
private (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, EntityCoordinates coordinates, AudioParams? audioParams = null, bool recordReplay = true)
private (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, EntityCoordinates coordinates, AudioParams? audioParams = null, bool recordReplay = true)
{
if (string.IsNullOrEmpty(filename))
return null;
if (recordReplay && _replayRecording.IsRecording)
{
_replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage
@@ -560,65 +574,65 @@ public sealed partial class AudioSystem : SharedAudioSystem
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null)
{
return PlayGlobal(filename, audioParams);
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null)
{
return PlayEntity(filename, entity, audioParams);
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null)
{
return PlayStatic(filename, coordinates, audioParams);
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, ICommonSession recipient, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, ICommonSession recipient, AudioParams? audioParams = null)
{
return PlayGlobal(filename, audioParams);
}
public override void LoadStream<T>(AudioComponent component, T stream)
public override void LoadStream<T>(Entity<AudioComponent> entity, T stream)
{
if (stream is AudioStream audioStream)
{
TryGetAudio(audioStream, out var audio);
SetupSource(component, audio!, audioStream.Length);
component.Loaded = true;
SetupSource(entity, audio!, audioStream.Length);
entity.Comp.Loaded = true;
}
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, EntityUid recipient, AudioParams? audioParams = null)
{
return PlayGlobal(filename, audioParams);
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null)
{
return PlayEntity(filename, uid, audioParams);
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null)
{
return PlayEntity(filename, uid, audioParams);
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
{
return PlayStatic(filename, coordinates, audioParams);
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
{
return PlayStatic(filename, coordinates, audioParams);
}
@@ -626,10 +640,10 @@ public sealed partial class AudioSystem : SharedAudioSystem
private (EntityUid Entity, AudioComponent Component) CreateAndStartPlayingStream(AudioParams? audioParams, AudioStream stream)
{
var audioP = audioParams ?? AudioParams.Default;
var entity = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace);
var comp = SetupAudio(entity, null, audioP, stream.Length);
LoadStream(comp, stream);
var entity = SetupAudio(null, audioP, initialize: false, length: stream.Length);
LoadStream(entity, stream);
EntityManager.InitializeAndStartEntity(entity);
var comp = entity.Comp;
var source = comp.Source;
// TODO clamp the offset inside of SetPlaybackPosition() itself.

View File

@@ -114,7 +114,7 @@ internal sealed partial class MidiManager : IMidiManager
"/usr/share/sounds/sf2/TimGM6mb.sf2",
};
private const string WindowsSoundfont = @"C:\WINDOWS\system32\drivers\gm.dls";
private static readonly string WindowsSoundfont = $@"{Environment.GetEnvironmentVariable("SystemRoot")}\system32\drivers\gm.dls";
private const string OsxSoundfont =
"/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls";

View File

@@ -314,6 +314,8 @@ public abstract class BaseAudioSource : IAudioSource
set
{
_checkDisposed();
value = MathF.Max(value, 0f);
AL.Source(SourceHandle, ALSourcef.SecOffset, value);
Master._checkAlError($"Tried to set invalid playback position of {value:0.00}");
}

View File

@@ -229,7 +229,7 @@ namespace Robust.Client
// Don't invoke PlayerLeaveServer if PlayerJoinedServer & GameStartedSetup hasn't been called yet.
if (RunLevel > ClientRunLevel.Connecting)
PlayerLeaveServer?.Invoke(this, new PlayerEventArgs(_playMan.LocalPlayer?.Session));
PlayerLeaveServer?.Invoke(this, new PlayerEventArgs(_playMan.LocalSession));
LastDisconnectReason = args.Reason;
GameStoppedReset();
@@ -285,6 +285,7 @@ namespace Robust.Client
/// <summary>
/// Enumeration of the run levels of the BaseClient.
/// </summary>
/// <seealso cref="ClientRunLevelExt"/>
public enum ClientRunLevel : byte
{
Error = 0,
@@ -315,6 +316,21 @@ namespace Robust.Client
SinglePlayerGame,
}
/// <summary>
/// Helper functions for working with <see cref="ClientRunLevel"/>.
/// </summary>
public static class ClientRunLevelExt
{
/// <summary>
/// Check if a <see cref="ClientRunLevel"/> is <see cref="ClientRunLevel.InGame"/>
/// or <see cref="ClientRunLevel.SinglePlayerGame"/>.
/// </summary>
public static bool IsInGameLike(this ClientRunLevel runLevel)
{
return runLevel is ClientRunLevel.InGame or ClientRunLevel.SinglePlayerGame;
}
}
/// <summary>
/// Event arguments for when something changed with the player.
/// </summary>

View File

@@ -188,7 +188,7 @@ namespace Robust.Client.Console
}
args.RemoveAt(0);
var shell = new ConsoleShell(this, session ?? _player.LocalPlayer?.Session, session == null);
var shell = new ConsoleShell(this, session ?? _player.LocalSession, session == null);
var cmdArgs = args.ToArray();
AnyCommandExecuted?.Invoke(shell, commandName, command, cmdArgs);
@@ -200,8 +200,7 @@ namespace Robust.Client.Console
// When not connected to a server, you can run all local commands.
// When connected to a server, you can only run commands according to the con group controller.
return _player.LocalPlayer == null
|| _player.LocalPlayer.Session.Status <= SessionStatus.Connecting
return _player.LocalSession is not { Status: > SessionStatus.Connecting }
|| _conGroup.CanCommand(cmdName);
}

View File

@@ -16,8 +16,7 @@ namespace Robust.Client.Console.Commands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var controlled = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
if (controlled == EntityUid.Invalid)
if (_playerManager.LocalEntity is not { } controlled)
{
shell.WriteLine("You don't have an attached entity.");
return;

View File

@@ -294,7 +294,6 @@ namespace Robust.Client.Console.Commands
internal sealed class SnapGridGetCell : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _map = default!;
public override string Command => "sggcell";
@@ -320,7 +319,7 @@ namespace Robust.Client.Console.Commands
return;
}
if (_map.TryGetGrid(_entManager.GetEntity(gridNet), out var grid))
if (_entManager.TryGetComponent<MapGridComponent>(_entManager.GetEntity(gridNet), out var grid))
{
foreach (var entity in grid.GetAnchoredEntities(new Vector2i(
int.Parse(indices.Split(',')[0], CultureInfo.InvariantCulture),
@@ -429,7 +428,6 @@ namespace Robust.Client.Console.Commands
internal sealed class GridTileCount : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _map = default!;
public override string Command => "gridtc";
@@ -448,7 +446,7 @@ namespace Robust.Client.Console.Commands
return;
}
if (_map.TryGetGrid(gridUid, out var grid))
if (_entManager.TryGetComponent<MapGridComponent>(gridUid, out var grid))
{
shell.WriteLine(grid.GetAllTiles().Count().ToString());
}
@@ -555,7 +553,7 @@ namespace Robust.Client.Console.Commands
if (type != typeof(Control))
cname = $"Control > {cname}";
returnVal.GetOrNew(cname).Add((member.Name, member.GetValue(control)?.ToString() ?? "null"));
returnVal.GetOrNew(cname).Add((member.Name, GetMemberValue(member, control, ", ")));
}
foreach (var (attachedProperty, value) in control.AllAttachedProperties)
@@ -570,6 +568,28 @@ namespace Robust.Client.Console.Commands
}
return returnVal;
}
internal static string PropertyValuesString(Control control, string key)
{
var member = GetAllMembers(control).Find(m => m.Name == key);
return GetMemberValue(member, control, "\n", "\"{0}\"");
}
private static string GetMemberValue(MemberInfo? member, Control control, string separator, string
wrap = "{0}")
{
var value = member?.GetValue(control);
var o = value switch
{
ICollection<Control> controls => string.Join(separator,
controls.Select(ctrl => $"{ctrl.Name}({ctrl.GetType()})")),
ICollection<string> list => string.Join(separator, list),
null => null,
_ => value.ToString()
};
// Convert to quote surrounded string or null with no quotes
return o is not null ? string.Format(wrap, o) : "null";
}
}
internal sealed class SetClipboardCommand : LocalizedCommands

View File

@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var type = Type.GetType(args[0]);
var type = GetType(args[0]);
if (type == null)
{
@@ -25,6 +25,17 @@ namespace Robust.Client.Console.Commands
shell.WriteLine(sig);
}
}
private Type? GetType(string name)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.GetType(name) is { } type)
return type;
}
return null;
}
}
#endif
}

View File

@@ -165,7 +165,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
{
var textEdit = new TextEdit
{
Placeholder = new Rope.Leaf("You deleted the lipsum OwO")
Placeholder = new Rope.Leaf("You deleted the lipsum\nOwO")
};
TabContainer.SetTabTitle(textEdit, "TextEdit");

View File

@@ -225,7 +225,7 @@ namespace Robust.Client.Debugging
{
var viewBounds = args.WorldBounds;
var viewAABB = args.WorldAABB;
var mapId = _eyeManager.CurrentMap;
var mapId = args.MapId;
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0)
{
@@ -368,12 +368,12 @@ namespace Robust.Client.Debugging
}
worldHandle.UseShader(null);
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.SetTransform(Matrix3x2.Identity);
}
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
{
var mapId = _eyeManager.CurrentMap;
var mapId = args.MapId;
var mousePos = _inputManager.MouseScreenPosition;
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.ShapeInfo) != 0x0)
@@ -420,7 +420,7 @@ namespace Robust.Client.Debugging
if (mapPos.MapId != args.MapId)
return;
var player = _playerManager.LocalPlayer?.ControlledEntity;
var player = _playerManager.LocalEntity;
if (!_entityManager.TryGetComponent<TransformComponent>(player, out var playerXform) ||
playerXform.MapID != args.MapId)
@@ -443,7 +443,7 @@ namespace Robust.Client.Debugging
}
screenHandle.UseShader(null);
screenHandle.SetTransform(Matrix3.Identity);
screenHandle.SetTransform(Matrix3x2.Identity);
}
protected internal override void Draw(in OverlayDrawArgs args)
@@ -520,11 +520,11 @@ namespace Robust.Client.Debugging
var matrix1 = xform1.WorldMatrix;
var matrix2 = xform2.WorldMatrix;
var xf1 = new Vector2(matrix1.R0C2, matrix1.R1C2);
var xf2 = new Vector2(matrix2.R0C2, matrix2.R1C2);
var xf1 = new Vector2(matrix1.M31, matrix1.M32);
var xf2 = new Vector2(matrix2.M31, matrix2.M32);
var p1 = matrix1.Transform(joint.LocalAnchorA);
var p2 = matrix2.Transform(joint.LocalAnchorB);
var p1 = Vector2.Transform(joint.LocalAnchorA, matrix1);
var p2 = Vector2.Transform(joint.LocalAnchorB, matrix2);
var xfa = new Transform(xf1, xform1.WorldRotation);
var xfb = new Transform(xf2, xform2.WorldRotation);

View File

@@ -612,6 +612,8 @@ namespace Robust.Client
{
_modLoader.BroadcastUpdate(ModUpdateLevel.FramePostEngine, frameEventArgs);
}
_audio.FlushALDisposeQueues();
}
internal static void SetupLogging(

View File

@@ -42,6 +42,8 @@ public sealed partial class ClientEntityManager
var pending = PendingNetEntityStates.GetOrNew(nEntity);
pending.Add((typeof(T), callerEntity));
return entity.Item1;
}

View File

@@ -8,6 +8,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Player;
using Robust.Shared.Replays;
using Robust.Shared.Utility;
@@ -134,6 +135,24 @@ namespace Robust.Client.GameObjects
EventBus.RaiseEvent(EventSource.Local, new EntitySessionMessage<T>(eventArgs, msg));
}
/// <inheritdoc />
public override void RaiseSharedEvent<T>(T message, EntityUid? user = null)
{
if (user == null || user != _playerManager.LocalEntity || !_gameTiming.IsFirstTimePredicted)
return;
EventBus.RaiseEvent(EventSource.Local, ref message);
}
/// <inheritdoc />
public override void RaiseSharedEvent<T>(T message, ICommonSession? user = null)
{
if (user == null || user != _playerManager.LocalSession || !_gameTiming.IsFirstTimePredicted)
return;
EventBus.RaiseEvent(EventSource.Local, ref message);
}
#region IEntityNetworkManager impl
public override IEntityNetworkManager EntityNetManager => this;
@@ -221,7 +240,7 @@ namespace Robust.Client.GameObjects
public void DispatchReceivedNetworkMsg(EntityEventArgs msg)
{
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(_playerManager.LocalPlayer!.Session), msg)!;
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(_playerManager.LocalSession!), msg)!;
ReceivedSystemMessage?.Invoke(this, msg);
ReceivedSystemMessage?.Invoke(this, sessionMsg);
}

View File

@@ -6,13 +6,12 @@ using System.Linq;
using System.Numerics;
using System.Text;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Clyde;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared;
using Robust.Shared.Animations;
using Robust.Shared.ComponentTrees;
using Robust.Shared.GameObjects;
using Robust.Shared.Graphics;
using Robust.Shared.Graphics.RSI;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -30,6 +29,7 @@ using static Robust.Client.ComponentTrees.SpriteTreeSystem;
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteSpecifierSerializer;
using Direction = Robust.Shared.Maths.Direction;
using Vector4 = Robust.Shared.Maths.Vector4;
namespace Robust.Client.GameObjects
{
@@ -150,7 +150,7 @@ namespace Robust.Client.GameObjects
[DataField("color")]
private Color color = Color.White;
public Matrix3 LocalMatrix = Matrix3.Identity;
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
[Animatable]
[ViewVariables(VVAccess.ReadWrite)]
@@ -389,10 +389,10 @@ namespace Robust.Client.GameObjects
internal void UpdateLocalMatrix()
{
LocalMatrix = Matrix3.CreateTransform(in offset, in rotation, in scale);
LocalMatrix = Matrix3Helpers.CreateTransform(in offset, in rotation, in scale);
}
public Matrix3 GetLocalMatrix()
public Matrix3x2 GetLocalMatrix()
{
return LocalMatrix;
}
@@ -772,15 +772,7 @@ namespace Robust.Client.GameObjects
{
foreach (var keyString in layerDatum.MapKeys)
{
object key;
if (reflection.TryParseEnumReference(keyString, out var @enum))
{
key = @enum;
}
else
{
key = keyString;
}
var key = ParseKey(keyString);
if (LayerMap.TryGetValue(key, out var mappedIndex))
{
@@ -806,9 +798,30 @@ namespace Robust.Client.GameObjects
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
layer.Visible = layerDatum.Visible ?? layer.Visible;
if (layerDatum.CopyToShaderParameters is { } copyParameters)
{
layer.CopyToShaderParameters = new CopyToShaderParameters(ParseKey(copyParameters.LayerKey))
{
ParameterTexture = copyParameters.ParameterTexture,
ParameterUV = copyParameters.ParameterUV
};
}
else
{
layer.CopyToShaderParameters = null;
}
RebuildBounds();
}
private object ParseKey(string keyString)
{
if (reflection.TryParseEnumReference(keyString, out var @enum))
return @enum;
return keyString;
}
public void LayerSetData(object layerKey, PrototypeLayerData data)
{
if (!LayerMapTryGet(layerKey, out var layer, true))
@@ -1237,9 +1250,9 @@ namespace Robust.Client.GameObjects
public IEnumerable<ISpriteLayer> AllLayers => Layers;
// Lobby SpriteView rendering path
public void Render(DrawingHandleWorld drawingHandle, Angle eyeRotation, Angle worldRotation, Direction? overrideDirection = null)
public void Render(DrawingHandleWorld drawingHandle, Angle eyeRotation, Angle worldRotation, Direction? overrideDirection = null, Vector2 position = default)
{
RenderInternal(drawingHandle, eyeRotation, worldRotation, Vector2.Zero, overrideDirection);
RenderInternal(drawingHandle, eyeRotation, worldRotation, position, overrideDirection);
}
[DataField("noRot")] private bool _screenLock = false;
@@ -1291,22 +1304,21 @@ namespace Robust.Client.GameObjects
// worldRotation + eyeRotation should be the angle of the entity on-screen. If no-rot is enabled this is just set to zero.
// However, at some point later the eye-matrix is applied separately, so we subtract -eye rotation for now:
var entityMatrix = Matrix3.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
var entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSprite);
var transformSprite = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
if (GranularLayersRendering)
{
//Default rendering
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation);
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformDefault);
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation);
var transformDefault = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
//Snap to cardinals
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSnap);
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
var transformSnap = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
//No rotation
entityMatrix = Matrix3.CreateTransform(worldPosition, -eyeRotation);
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformNoRot);
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, -eyeRotation);
var transformNoRot = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
foreach (var layer in Layers) {
switch (layer.RenderingStrategy)
@@ -1367,7 +1379,7 @@ namespace Robust.Client.GameObjects
// TODO whenever sprite comp gets ECS'd , just make this a direct method call.
var ev = new QueueSpriteTreeUpdateEvent(entities.GetComponent<TransformComponent>(Owner));
entities.EventBus.RaiseComponentEvent(this, ref ev);
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
}
private void QueueUpdateIsInert()
@@ -1377,7 +1389,7 @@ namespace Robust.Client.GameObjects
// TODO whenever sprite comp gets ECS'd , just make this a direct method call.
var ev = new SpriteUpdateInertEvent();
entities.EventBus.RaiseComponentEvent(this, ref ev);
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
}
[Obsolete("Use SpriteSystem instead.")]
@@ -1533,7 +1545,7 @@ namespace Robust.Client.GameObjects
private RSI.State? _actualState;
[ViewVariables] public RSI.State? ActualState => _actualState;
public Matrix3 LocalMatrix = Matrix3.Identity;
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
[ViewVariables(VVAccess.ReadWrite)]
public Vector2 Scale
@@ -1637,6 +1649,9 @@ namespace Robust.Client.GameObjects
[ViewVariables]
public LayerRenderingStrategy RenderingStrategy = LayerRenderingStrategy.UseSpriteStrategy;
[ViewVariables(VVAccess.ReadWrite)]
public CopyToShaderParameters? CopyToShaderParameters;
public Layer(SpriteComponent parent)
{
_parent = parent;
@@ -1665,6 +1680,8 @@ namespace Robust.Client.GameObjects
DirOffset = toClone.DirOffset;
_autoAnimated = toClone._autoAnimated;
RenderingStrategy = toClone.RenderingStrategy;
if (toClone.CopyToShaderParameters is { } copyToShaderParameters)
CopyToShaderParameters = new CopyToShaderParameters(copyToShaderParameters);
}
void ISerializationHooks.AfterDeserialization()
@@ -1674,7 +1691,7 @@ namespace Robust.Client.GameObjects
internal void UpdateLocalMatrix()
{
LocalMatrix = Matrix3.CreateTransform(in _offset, in _rotation, in _scale);
LocalMatrix = Matrix3Helpers.CreateTransform(in _offset, in _rotation, in _scale);
}
RSI? ISpriteLayer.Rsi { get => RSI; set => SetRsi(value); }
@@ -1946,27 +1963,27 @@ namespace Robust.Client.GameObjects
/// Given the apparent rotation of an entity on screen (world + eye rotation), get layer's matrix for drawing &
/// relevant RSI direction.
/// </summary>
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3 layerDrawMatrix)
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3x2 layerDrawMatrix)
{
if (_parent.NoRotation || dir == RsiDirection.South)
layerDrawMatrix = LocalMatrix;
else
{
Matrix3.Multiply(in _rsiDirectionMatrices[(int)dir], in LocalMatrix, out layerDrawMatrix);
layerDrawMatrix = Matrix3x2.Multiply(_rsiDirectionMatrices[(int)dir], LocalMatrix);
}
}
private static Matrix3[] _rsiDirectionMatrices = new Matrix3[]
private static Matrix3x2[] _rsiDirectionMatrices = new Matrix3x2[]
{
// array order chosen such that this array can be indexed by casing an RSI direction to an int
Matrix3.Identity, // should probably just avoid matrix multiplication altogether if the direction is south.
Matrix3.CreateRotation(-Direction.North.ToAngle()),
Matrix3.CreateRotation(-Direction.East.ToAngle()),
Matrix3.CreateRotation(-Direction.West.ToAngle()),
Matrix3.CreateRotation(-Direction.SouthEast.ToAngle()),
Matrix3.CreateRotation(-Direction.SouthWest.ToAngle()),
Matrix3.CreateRotation(-Direction.NorthEast.ToAngle()),
Matrix3.CreateRotation(-Direction.NorthWest.ToAngle())
Matrix3x2.Identity, // should probably just avoid matrix multiplication altogether if the direction is south.
Matrix3Helpers.CreateRotation(-Direction.North.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.East.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.West.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.SouthEast.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.SouthWest.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.NorthEast.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.NorthWest.ToAngle())
};
/// <summary>
@@ -2000,7 +2017,7 @@ namespace Robust.Client.GameObjects
/// <summary>
/// Render a layer. This assumes that the input angle is between 0 and 2pi.
/// </summary>
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3 spriteMatrix, Angle angle, Direction? overrideDirection)
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatrix, Angle angle, Direction? overrideDirection)
{
if (!Visible || Blank)
return;
@@ -2009,8 +2026,6 @@ namespace Robust.Client.GameObjects
// Set the drawing transform for this layer
GetLayerDrawMatrix(dir, out var layerMatrix);
Matrix3.Multiply(in layerMatrix, in spriteMatrix, out var transformMatrix);
drawingHandle.SetTransform(in transformMatrix);
// The direction used to draw the sprite can differ from the one that the angle would naively suggest,
// due to direction overrides or offsets.
@@ -2020,7 +2035,41 @@ namespace Robust.Client.GameObjects
// Get the correct directional texture from the state, and draw it!
var texture = GetRenderTexture(_actualState, dir);
RenderTexture(drawingHandle, texture);
if (CopyToShaderParameters == null)
{
// Set the drawing transform for this layer
var transformMatrix = Matrix3x2.Multiply(layerMatrix, spriteMatrix);
drawingHandle.SetTransform(in transformMatrix);
RenderTexture(drawingHandle, texture);
}
else
{
// Multiple atrocities to god being committed right here.
var otherLayerIdx = _parent.LayerMap[CopyToShaderParameters.LayerKey!];
var otherLayer = _parent.Layers[otherLayerIdx];
if (otherLayer.Shader is not { } shader)
{
// No shader set apparently..?
return;
}
if (!shader.Mutable)
otherLayer.Shader = shader = shader.Duplicate();
var clydeTexture = Clyde.RenderHandle.ExtractTexture(texture, null, out var csr);
var sr = Clyde.RenderHandle.WorldTextureBoundsToUV(clydeTexture, csr);
if (CopyToShaderParameters.ParameterTexture is { } paramTexture)
shader.SetParameter(paramTexture, clydeTexture);
if (CopyToShaderParameters.ParameterUV is { } paramUV)
{
var uv = new Vector4(sr.Left, sr.Bottom, sr.Right, sr.Top);
shader.SetParameter(paramUV, uv);
}
}
}
private void RenderTexture(DrawingHandleWorld drawingHandle, Texture texture)
@@ -2098,6 +2147,23 @@ namespace Robust.Client.GameObjects
}
}
/// <summary>
/// Instantiated version of <see cref="PrototypeCopyToShaderParameters"/>.
/// Has <see cref="LayerKey"/> actually resolved to a a real key.
/// </summary>
public sealed class CopyToShaderParameters(object layerKey)
{
public object LayerKey = layerKey;
public string? ParameterTexture;
public string? ParameterUV;
public CopyToShaderParameters(CopyToShaderParameters toClone) : this(toClone.LayerKey)
{
ParameterTexture = toClone.ParameterTexture;
ParameterUV = toClone.ParameterUV;
}
}
void IAnimationProperties.SetAnimatableProperty(string name, object value)
{
if (!name.StartsWith("layer/"))

View File

@@ -11,6 +11,7 @@ namespace Robust.Client.GameObjects
{
private readonly List<Entity<AnimationPlayerComponent>> _activeAnimations = new();
private EntityQuery<AnimationPlayerComponent> _playerQuery;
private EntityQuery<MetaDataComponent> _metaQuery;
[Dependency] private readonly IComponentFactory _compFact = default!;
@@ -18,6 +19,7 @@ namespace Robust.Client.GameObjects
public override void Initialize()
{
base.Initialize();
_playerQuery = GetEntityQuery<AnimationPlayerComponent>();
_metaQuery = GetEntityQuery<MetaDataComponent>();
}
@@ -171,28 +173,32 @@ namespace Robust.Client.GameObjects
return component.PlayingAnimations.ContainsKey(key);
}
[Obsolete]
public void Stop(AnimationPlayerComponent component, string key)
{
component.PlayingAnimations.Remove(key);
Stop((component.Owner, component), key);
}
public void Stop(EntityUid uid, string key)
public void Stop(Entity<AnimationPlayerComponent?> entity, string key)
{
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
if (!_playerQuery.Resolve(entity.Owner, ref entity.Comp, false) ||
!entity.Comp.PlayingAnimations.Remove(key))
{
return;
}
player.PlayingAnimations.Remove(key);
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, new AnimationCompletedEvent {Uid = entity.Owner, Key = key}, true);
}
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
{
if (!Resolve(uid, ref component, false))
return;
component.PlayingAnimations.Remove(key);
Stop((uid, component), key);
}
}
/// <summary>
/// Raised whenever an animation stops, either due to running its course or being stopped manually.
/// </summary>
public sealed class AnimationCompletedEvent : EntityEventArgs
{
public EntityUid Uid { get; init; }

View File

@@ -107,7 +107,7 @@ namespace Robust.Client.GameObjects
toDelete.Add(id);
}
foreach (var dead in toDelete)
foreach (var dead in toDelete.Span)
{
component.Containers.Remove(dead);
}
@@ -120,7 +120,7 @@ namespace Robust.Client.GameObjects
{
var type = _serializer.FindSerializedType(typeof(BaseContainer), data.ContainerType);
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type!, inject:false);
InitContainer(container, (uid, component), id);
container.Init(this, id, (uid, component));
component.Containers.Add(id, container);
}
@@ -142,7 +142,7 @@ namespace Robust.Client.GameObjects
toRemove.Add(entity);
}
foreach (var entity in toRemove)
foreach (var entity in toRemove.Span)
{
Remove(
(entity, TransformQuery.GetComponent(entity), MetaQuery.GetComponent(entity)),
@@ -162,7 +162,7 @@ namespace Robust.Client.GameObjects
removedExpected.Add(netEntity);
}
foreach (var entityUid in removedExpected)
foreach (var entityUid in removedExpected.Span)
{
RemoveExpectedEntity(entityUid, out _);
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Shared.Console;
using Robust.Shared.Containers;
@@ -118,7 +119,7 @@ public sealed class EntityLookupOverlay : Overlay
//DebugTools.Assert(!ent.IsInContainer(_entityManager));
var (entPos, entRot) = _transform.GetWorldPositionRotation(ent);
var lookupPos = invMatrix.Transform(entPos);
var lookupPos = Vector2.Transform(entPos, invMatrix);
var lookupRot = entRot - rotation;
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, _xformQuery);
@@ -127,6 +128,6 @@ public sealed class EntityLookupOverlay : Overlay
}
});
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.SetTransform(Matrix3x2.Identity);
}
}

View File

@@ -63,7 +63,7 @@ namespace Robust.Client.GameObjects
protected internal override void Draw(in OverlayDrawArgs args)
{
var map = _eyeManager.CurrentMap;
var map = args.MapId;
if (map == MapId.Nullspace) return;
foreach (var (_, treeComp) in _trees.GetIntersectingTrees(map, args.WorldBounds))

View File

@@ -70,7 +70,7 @@ namespace Robust.Client.GameObjects
protected internal override void Draw(in OverlayDrawArgs args)
{
var currentMap = _eyeManager.CurrentMap;
var currentMap = args.MapId;
var viewport = args.WorldBounds;
var worldHandle = args.WorldHandle;
@@ -109,7 +109,7 @@ namespace Robust.Client.GameObjects
}
}
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.SetTransform(Matrix3x2.Identity);
}
}
}

View File

@@ -20,12 +20,14 @@ namespace Robust.Client.GameObjects
/// </summary>
public sealed class InputSystem : SharedInputSystem, IPostInjectInit
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IClientGameStateManager _stateManager = default!;
[Dependency] private readonly IConsoleHost _conHost = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private ISawmill _sawmillInputContext = default!;
@@ -81,18 +83,35 @@ namespace Robust.Client.GameObjects
}
}
// send it off to the server
var clientMsg = (ClientFullInputCmdMessage)message;
var fullMsg = new FullInputCmdMessage(
clientMsg.Tick,
clientMsg.SubTick,
(int)clientMsg.InputSequence,
clientMsg.InputFunctionId,
clientMsg.State,
GetNetCoordinates(clientMsg.Coordinates),
clientMsg.ScreenCoordinates)
var clientMsg = message switch
{
Uid = GetNetEntity(clientMsg.Uid)
ClientFullInputCmdMessage clientInput => clientInput,
FullInputCmdMessage fullInput => new ClientFullInputCmdMessage(
fullInput.Tick,
fullInput.SubTick,
fullInput.InputFunctionId,
GetCoordinates(fullInput.Coordinates),
fullInput.ScreenCoordinates,
fullInput.State,
GetEntity(fullInput.Uid)),
_ => throw new ArgumentOutOfRangeException()
};
var fullMsg = message switch
{
FullInputCmdMessage fullInput => fullInput,
ClientFullInputCmdMessage client => new FullInputCmdMessage(
client.Tick,
client.SubTick,
client.InputFunctionId,
clientMsg.State,
GetNetCoordinates(client.Coordinates),
clientMsg.ScreenCoordinates,
GetNetEntity(clientMsg.Uid)
),
_ => throw new ArgumentOutOfRangeException()
};
DispatchInputCommand(clientMsg, fullMsg);
@@ -105,12 +124,10 @@ namespace Robust.Client.GameObjects
/// <param name="inputCmd">Input command to handle as predicted.</param>
public void PredictInputCommand(IFullInputCmdMessage inputCmd)
{
DebugTools.AssertNotNull(_playerManager.LocalPlayer);
var keyFunc = _inputManager.NetworkBindMap.KeyFunctionName(inputCmd.InputFunctionId);
Predicted = true;
var session = _playerManager.LocalPlayer!.Session;
var session = _playerManager.LocalSession;
foreach (var handler in BindRegistry.GetHandlers(keyFunc))
{
if (handler.HandleCmdMessage(EntityManager, session, inputCmd))
@@ -132,7 +149,7 @@ namespace Robust.Client.GameObjects
_conHost.RegisterCommand("incmd",
"Inserts an input command into the simulation",
"incmd <KeyFunction> <d|u KeyState> <wxPos> <wyPos>",
"incmd <KeyFunction> <KeyState> [wxPos] [wyPos]",
GenerateInputCommand);
}
@@ -145,27 +162,52 @@ namespace Robust.Client.GameObjects
private void GenerateInputCommand(IConsoleShell shell, string argstr, string[] args)
{
var localPlayer = _playerManager.LocalPlayer;
if(localPlayer is null)
if (_playerManager.LocalEntity is not { } pent)
return;
var pent = localPlayer.ControlledEntity;
if(pent is null)
if (args.Length is not (2 or 4))
{
shell.WriteLine(Loc.GetString($"cmd-invalid-arg-number-error"));
return;
}
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
var keyFunction = new BoundKeyFunction(args[0]);
if (!Enum.TryParse<BoundKeyState>(args[1], out var state))
{
shell.WriteLine(Loc.GetString("cmd-parse-failure-enum", ("arg", args[1]), ("enum", nameof(BoundKeyState))));
return;
}
var pxform = Transform(pent.Value);
var wPos = pxform.WorldPosition + new Vector2(float.Parse(args[2]), float.Parse(args[3]));
var coords = EntityCoordinates.FromMap(EntityManager, pent.Value, new MapCoordinates(wPos, pxform.MapID));
var wOffset = Vector2.Zero;
if (args.Length == 4)
{
if (!float.TryParse(args[2], out var wX))
{
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[2])));
return;
}
if (!float.TryParse(args[3], out var wY))
{
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[3])));
return;
}
wOffset = new Vector2(wX, wY);
}
var coords = EntityCoordinates.FromMap(pent, _transform.GetMapCoordinates(pent).Offset(wOffset), _transform, EntityManager);
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
var message = new ClientFullInputCmdMessage(_timing.CurTick,
_timing.TickFraction,
funcId,
coords,
new ScreenCoordinates(0, 0, default),
state,
EntityUid.Invalid);
HandleInputCommand(localPlayer.Session, keyFunction, message);
HandleInputCommand(_playerManager.LocalSession, keyFunction, message);
}
private void OnAttachedEntityChanged(LocalPlayerAttachedEvent message)
@@ -208,11 +250,8 @@ namespace Robust.Client.GameObjects
/// </summary>
public void SetEntityContextActive()
{
var controlled = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
if (controlled == EntityUid.Invalid)
{
if (_playerManager.LocalEntity is not { } controlled)
return;
}
SetEntityContextActive(_inputManager, controlled);
}

View File

@@ -1,12 +1,9 @@
using Robust.Client.Graphics;
using Robust.Client.Map;
using Robust.Client.Physics;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Dynamics;
namespace Robust.Client.GameObjects;
@@ -16,6 +13,17 @@ public sealed class MapSystem : SharedMapSystem
[Dependency] private readonly IResourceCache _resource = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
protected override MapId GetNextMapId()
{
// Client-side map entities use negative map Ids to avoid conflict with server-side maps.
var id = new MapId(--LastMapId);
while (MapManager.MapExists(id))
{
id = new MapId(--LastMapId);
}
return id;
}
public override void Initialize()
{
base.Initialize();
@@ -27,9 +35,4 @@ public sealed class MapSystem : SharedMapSystem
base.Shutdown();
_overlayManager.RemoveOverlay<TileEdgeOverlay>();
}
protected override void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args)
{
EnsureComp<PhysicsMapComponent>(uid);
}
}

View File

@@ -62,7 +62,7 @@ namespace Robust.Client.GameObjects
SubscribeLocalEvent<SpriteComponent, SpriteUpdateInertEvent>(QueueUpdateInert);
SubscribeLocalEvent<SpriteComponent, ComponentInit>(OnInit);
_cfg.OnValueChanged(CVars.RenderSpriteDirectionBias, OnBiasChanged, true);
Subs.CVar(_cfg, CVars.RenderSpriteDirectionBias, OnBiasChanged, true);
_sawmill = _logManager.GetSawmill("sprite");
}
@@ -72,12 +72,6 @@ namespace Robust.Client.GameObjects
QueueUpdateInert(uid, component);
}
public override void Shutdown()
{
base.Shutdown();
_cfg.UnsubValueChanged(CVars.RenderSpriteDirectionBias, OnBiasChanged);
}
private void OnBiasChanged(double value)
{
SpriteComponent.DirectionBias = value;

View File

@@ -1,88 +1,8 @@
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using System;
using UserInterfaceComponent = Robust.Shared.GameObjects.UserInterfaceComponent;
namespace Robust.Client.GameObjects
namespace Robust.Client.GameObjects;
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
{
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
{
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<BoundUIWrapMessage>(MessageReceived);
}
private void MessageReceived(BoundUIWrapMessage ev)
{
var uid = GetEntity(ev.Entity);
if (!TryComp<UserInterfaceComponent>(uid, out var cmp))
return;
var uiKey = ev.UiKey;
var message = ev.Message;
// This should probably not happen at this point, but better make extra sure!
if (_playerManager.LocalPlayer != null)
message.Session = _playerManager.LocalPlayer.Session;
message.Entity = GetNetEntity(uid);
message.UiKey = uiKey;
// Raise as object so the correct type is used.
RaiseLocalEvent(uid, (object)message, true);
switch (message)
{
case OpenBoundInterfaceMessage _:
TryOpenUi(uid, uiKey, cmp);
break;
case CloseBoundInterfaceMessage _:
TryCloseUi(message.Session, uid, uiKey, remoteCall: true, uiComp: cmp);
break;
default:
if (cmp.OpenInterfaces.TryGetValue(uiKey, out var bui))
bui.InternalReceiveMessage(message);
break;
}
}
private bool TryOpenUi(EntityUid uid, Enum uiKey, UserInterfaceComponent? uiComp = null)
{
if (!Resolve(uid, ref uiComp))
return false;
if (uiComp.OpenInterfaces.ContainsKey(uiKey))
return false;
var data = uiComp.MappedInterfaceData[uiKey];
// TODO: This type should be cached, but I'm too lazy.
var type = _reflectionManager.LooseGetType(data.ClientType);
var boundInterface =
(BoundUserInterface) _dynamicTypeFactory.CreateInstance(type, new object[] {uid, uiKey});
boundInterface.Open();
uiComp.OpenInterfaces[uiKey] = boundInterface;
var playerSession = _playerManager.LocalPlayer?.Session;
if (playerSession != null)
{
uiComp.Interfaces[uiKey]._subscribedSessions.Add(playerSession);
RaiseLocalEvent(uid, new BoundUIOpenedEvent(uiKey, uid, playerSession), true);
}
return true;
}
}
}

View File

@@ -35,7 +35,7 @@ namespace Robust.Client.GameObjects
return;
}
var player = _playerManager.LocalPlayer?.ControlledEntity;
var player = _playerManager.LocalEntity;
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
{

View File

@@ -54,11 +54,11 @@ namespace Robust.Client.GameStates
private readonly HashSet<NetEntity> _stateEnts = new();
private readonly List<EntityUid> _toDelete = new();
private readonly List<IComponent> _toRemove = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _outputData = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> _outputData = new();
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
private readonly ObjectPool<Dictionary<ushort, IComponentState>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, IComponentState>>(new DictPolicy<ushort, IComponentState>(), 256);
private readonly ObjectPool<Dictionary<ushort, IComponentState?>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, IComponentState?>>(new DictPolicy<ushort, IComponentState?>(), 256);
private uint _metaCompNetId;
@@ -125,6 +125,8 @@ namespace Robust.Client.GameStates
#endif
private bool _resettingPredictedEntities;
private readonly List<EntityUid> _brokenEnts = new();
private readonly List<(EntityUid, NetEntity)> _toStart = new();
/// <inheritdoc />
public void Initialize()
@@ -232,9 +234,9 @@ namespace Robust.Client.GameStates
return default;
}
DebugTools.AssertNotNull(_players.LocalPlayer);
DebugTools.Assert(_players.LocalSession != null);
var evArgs = new EntitySessionEventArgs(_players.LocalPlayer!.Session);
var evArgs = new EntitySessionEventArgs(_players.LocalSession);
_pendingSystemMessages.Enqueue((_nextInputCmdSeq, _timing.CurTick, message,
new EntitySessionMessage<T>(evArgs, message)));
@@ -257,7 +259,7 @@ namespace Robust.Client.GameStates
public void UpdateFullRep(GameState state, bool cloneDelta = false)
=> _processor.UpdateFullRep(state, cloneDelta);
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep()
=> _processor.GetFullRep();
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
@@ -598,8 +600,12 @@ namespace Robust.Client.GameStates
if (_sawmill.Level <= LogLevel.Debug)
_sawmill.Debug($" A component was dirtied: {comp.GetType()}");
var handleState = new ComponentHandleState(compState, null);
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
if (compState != null)
{
var handleState = new ComponentHandleState(compState, null);
_entities.EventBus.RaiseComponentEvent(entity, comp, ref handleState);
}
comp.LastModifiedTick = _timing.LastRealTick;
}
}
@@ -631,8 +637,12 @@ namespace Robust.Client.GameStates
if (_sawmill.Level <= LogLevel.Debug)
_sawmill.Debug($" A component was removed: {comp.GetType()}");
var stateEv = new ComponentHandleState(state, null);
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
if (state != null)
{
var stateEv = new ComponentHandleState(state, null);
_entities.EventBus.RaiseComponentEvent(entity, comp, ref stateEv);
}
comp.ClearCreationTick(); // don't undo the re-adding.
comp.LastModifiedTick = _timing.LastRealTick;
}
@@ -667,7 +677,16 @@ namespace Robust.Client.GameStates
foreach (var netEntity in createdEntities)
{
#if EXCEPTION_TOLERANCE
if (!_entityManager.TryGetEntityData(netEntity, out _, out var meta))
{
_sawmill.Error($"Encountered deleted entity while merging implicit data! NetEntity: {netEntity}");
continue;
}
#else
var (_, meta) = _entityManager.GetEntityData(netEntity);
#endif
var compData = _compDataPool.Get();
_outputData.Add(netEntity, compData);
@@ -676,7 +695,7 @@ namespace Robust.Client.GameStates
DebugTools.Assert(component.NetSyncEnabled);
var state = _entityManager.GetComponentState(bus, component, null, GameTick.Zero);
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
DebugTools.Assert(state is not IComponentDeltaState);
compData.Add(netId, state);
}
}
@@ -700,7 +719,7 @@ namespace Robust.Client.GameStates
{
using var _ = _timing.StartStateApplicationArea();
// TODO repays optimize this.
// TODO replays optimize this.
// This currently just saves game states as they are applied.
// However this is inefficient and may have redundant data.
// E.g., we may record states: [10 to 15] [11 to 16] *error* [0 to 18] [18 to 19] [18 to 20] ...
@@ -876,9 +895,22 @@ namespace Robust.Client.GameStates
{
foreach (var (entity, data) in _toApply)
{
#if EXCEPTION_TOLERANCE
try
{
#endif
HandleEntityState(entity, data.NetEntity, data.Meta, _entities.EventBus, data.curState,
data.nextState, data.LastApplied, curState.ToSequence, data.EnteringPvs);
data.nextState, data.LastApplied, curState.ToSequence, data.EnteringPvs);
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
{
_sawmill.Error($"Caught exception while applying entity state. Entity: {_entities.ToPrettyString(entity)}. Exception: {e}");
_entityManager.DeleteEntity(entity);
RequestFullState();
continue;
}
#endif
if (!data.EnteringPvs)
continue;
@@ -917,7 +949,7 @@ namespace Robust.Client.GameStates
{
try
{
ProcessDeletions(delSpan, xforms, xformSys);
ProcessDeletions(delSpan, xforms, metas, xformSys);
}
catch (Exception e)
{
@@ -962,6 +994,7 @@ namespace Robust.Client.GameStates
}
var xforms = _entities.GetEntityQuery<TransformComponent>();
var metas = _entities.GetEntityQuery<MetaDataComponent>();
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
_toDelete.Clear();
@@ -990,12 +1023,12 @@ namespace Robust.Client.GameStates
// This entity is going to get deleted, but maybe some if its children won't be, so lets detach them to
// null. First we will detach the parent in order to reduce the number of broadphase/lookup updates.
xformSys.DetachParentToNull(ent, xform);
xformSys.DetachEntity(ent, xform);
// Then detach all children.
foreach (var child in xform._children)
{
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
xformSys.DetachEntity(child, xforms.Get(child), metas.Get(child), xform);
if (deleteClientChildren
&& !deleteClientEntities // don't add duplicates
@@ -1014,9 +1047,9 @@ namespace Robust.Client.GameStates
}
}
private void ProcessDeletions(
ReadOnlySpan<NetEntity> delSpan,
private void ProcessDeletions(ReadOnlySpan<NetEntity> delSpan,
EntityQuery<TransformComponent> xforms,
EntityQuery<MetaDataComponent> metas,
SharedTransformSystem xformSys)
{
// Processing deletions is non-trivial, because by default deletions will also delete all child entities.
@@ -1043,13 +1076,13 @@ namespace Robust.Client.GameStates
continue; // Already deleted? or never sent to us?
// First, a single recursive map change
xformSys.DetachParentToNull(id.Value, xform);
xformSys.DetachEntity(id.Value, xform);
// Then detach all children.
var childEnumerator = xform.ChildEnumerator;
while (childEnumerator.MoveNext(out var child))
{
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
xformSys.DetachEntity(child, xforms.Get(child), metas.Get(child), xform);
}
// Finally, delete the entity.
@@ -1120,7 +1153,7 @@ namespace Robust.Client.GameStates
continue;
}
if ((meta.Flags & MetaDataFlags.Detached) != 0)
if ((meta.Flags & (MetaDataFlags.Detached | MetaDataFlags.Undetachable)) != 0)
continue;
if (lastStateApplied.HasValue)
@@ -1138,13 +1171,13 @@ namespace Robust.Client.GameStates
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
containerSys.TryGetContainingContainer(xform.ParentUid, ent.Value, out container, null, true))
containerSys.TryGetContainingContainer(xform.ParentUid, ent.Value, out container))
{
containerSys.Remove((ent.Value, xform, meta), container, false, true);
}
meta._flags |= MetaDataFlags.Detached;
xformSys.DetachParentToNull(ent.Value, xform);
xformSys.DetachEntity(ent.Value, xform);
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
if (container != null)
@@ -1157,63 +1190,58 @@ namespace Robust.Client.GameStates
private void InitializeAndStart(Dictionary<NetEntity, EntityState> toCreate)
{
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
_toStart.Clear();
#if EXCEPTION_TOLERANCE
var brokenEnts = new List<EntityUid>();
#endif
using (_prof.Group("Initialize Entity"))
{
EntityUid entity = default;
foreach (var netEntity in toCreate.Keys)
{
var entity = _entityManager.GetEntity(netEntity);
#if EXCEPTION_TOLERANCE
try
{
#endif
_entities.InitializeEntity(entity, metaQuery.GetComponent(entity));
#if EXCEPTION_TOLERANCE
(entity, var meta) = _entityManager.GetEntityData(netEntity);
_entities.InitializeEntity(entity, meta);
_toStart.Add((entity, netEntity));
}
catch (Exception e)
{
_sawmill.Error($"Server entity threw in Init: ent={_entities.ToPrettyString(entity)}");
_sawmill.Error($"Server entity threw in Init: nent={netEntity}, ent={_entities.ToPrettyString(entity)}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
brokenEnts.Add(entity);
toCreate.Remove(netEntity);
}
_toCreate.Remove(netEntity);
_brokenEnts.Add(entity);
#if !EXCEPTION_TOLERANCE
throw;
#endif
}
}
}
using (_prof.Group("Start Entity"))
{
foreach (var netEntity in toCreate.Keys)
foreach (var (entity, netEntity) in _toStart)
{
var entity = _entityManager.GetEntity(netEntity);
#if EXCEPTION_TOLERANCE
try
{
#endif
_entities.StartEntity(entity);
#if EXCEPTION_TOLERANCE
_entities.StartEntity(entity);
}
catch (Exception e)
{
_sawmill.Error($"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}");
_sawmill.Error($"Server entity threw in Start: nent={netEntity}, ent={_entityManager.ToPrettyString(entity)}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
brokenEnts.Add(entity);
toCreate.Remove(netEntity);
}
_toCreate.Remove(netEntity);
_brokenEnts.Add(entity);
#if !EXCEPTION_TOLERANCE
throw;
#endif
}
}
}
#if EXCEPTION_TOLERANCE
foreach (var entity in brokenEnts)
foreach (var entity in _brokenEnts)
{
_entityManager.DeleteEntity(entity);
}
#endif
_brokenEnts.Clear();
}
private void HandleEntityState(EntityUid uid, NetEntity netEntity, MetaDataComponent meta, IEventBus bus, EntityState? curState,
@@ -1329,23 +1357,11 @@ namespace Robust.Client.GameStates
foreach (var (comp, cur, next) in _compStateWork.Values)
{
try
{
var handleState = new ComponentHandleState(cur, next);
bus.RaiseComponentEvent(comp, ref handleState);
}
#pragma warning disable CS0168 // Variable is declared but never used
catch (Exception e)
#pragma warning restore CS0168 // Variable is declared but never used
{
#if EXCEPTION_TOLERANCE
_sawmill.Error($"Failed to apply comp state: entity={_entities.ToPrettyString(uid)}, comp={comp.GetType()}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(HandleEntityState)}");
#else
_sawmill.Error($"Failed to apply comp state: entity={_entities.ToPrettyString(uid)}, comp={comp.GetType()}");
throw;
#endif
}
if (cur == null && next == null)
continue;
var handleState = new ComponentHandleState(cur, next);
bus.RaiseComponentEvent(uid, comp, ref handleState);
}
}
@@ -1414,10 +1430,10 @@ namespace Robust.Client.GameStates
_entities.TryGetComponent(xform.ParentUid, out MetaDataComponent? containerMeta) &&
(containerMeta.Flags & MetaDataFlags.Detached) == 0)
{
containerSys.TryGetContainingContainer(xform.ParentUid, uid, out container, null, true);
containerSys.TryGetContainingContainer(xform.ParentUid, uid, out container);
}
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachParentToNull(uid, xform);
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachEntity(uid, xform);
if (container != null)
containerSys.AddExpectedEntity(_entities.GetNetEntity(uid), container);
@@ -1496,8 +1512,11 @@ namespace Robust.Client.GameStates
_entityManager.AddComponent(uid, comp, true, meta);
}
if (state == null)
continue;
var handleState = new ComponentHandleState(state, null);
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
_entityManager.EventBus.RaiseComponentEvent(uid, comp, ref handleState);
}
// ensure we don't have any extra components

View File

@@ -32,7 +32,7 @@ namespace Robust.Client.GameStates
/// <summary>
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
/// </summary>
internal readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _lastStateFullRep
internal readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> _lastStateFullRep
= new();
/// <inheritdoc />
@@ -212,7 +212,7 @@ Had full state: {LastFullState != null}"
{
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
{
compData = new Dictionary<ushort, IComponentState>();
compData = new();
_lastStateFullRep.Add(entityState.NetEntity, compData);
}
@@ -221,21 +221,20 @@ Had full state: {LastFullState != null}"
var compState = change.State;
if (compState is IComponentDeltaState delta
&& !delta.FullState
&& compData.TryGetValue(change.NetID, out var old)) // May fail if relying on implicit data
{
DebugTools.Assert(old is IComponentDeltaState oldDelta && oldDelta.FullState, "last state is not a full state");
DebugTools.Assert(old is not IComponentDeltaState, "last state is not a full state");
if (cloneDelta)
{
compState = delta.CreateNewFullState(old);
compState = delta.CreateNewFullState(old!);
}
else
{
delta.ApplyToFullState(old);
delta.ApplyToFullState(old!);
compState = old;
}
DebugTools.Assert(compState is IComponentDeltaState newState && newState.FullState, "newly constructed state is not a full state");
DebugTools.Assert(compState is not IComponentDeltaState, "newly constructed state is not a full state");
}
compData[change.NetID] = compState;
@@ -391,7 +390,7 @@ Had full state: {LastFullState != null}"
LastFullStateRequested = null;
}
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> implicitData)
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> implicitData)
{
foreach (var (netEntity, implicitEntState) in implicitData)
{
@@ -399,6 +398,7 @@ Had full state: {LastFullState != null}"
foreach (var (netId, implicitCompState) in implicitEntState)
{
DebugTools.Assert(implicitCompState is not IComponentDeltaState);
ref var serverState = ref CollectionsMarshal.GetValueRefOrAddDefault(fullRep, netId, out var exists);
if (!exists)
@@ -407,36 +407,32 @@ Had full state: {LastFullState != null}"
continue;
}
if (serverState is not IComponentDeltaState serverDelta || serverDelta.FullState)
if (serverState is not IComponentDeltaState serverDelta)
continue;
DebugTools.AssertNotNull(implicitCompState);
// Server sent an initial delta state. This is fine as long as the client can infer an initial full
// state from the entity prototype.
if (implicitCompState is not IComponentDeltaState implicitDelta || !implicitDelta.FullState)
{
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {netEntity}");
continue;
}
serverDelta.ApplyToFullState(implicitCompState);
serverDelta.ApplyToFullState(implicitCompState!);
serverState = implicitCompState;
DebugTools.Assert(implicitCompState is IComponentDeltaState d && d.FullState);
DebugTools.Assert(serverState is not IComponentDeltaState);
}
}
}
public Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity netEntity)
public Dictionary<ushort, IComponentState?> GetLastServerStates(NetEntity netEntity)
{
return _lastStateFullRep[netEntity];
}
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep()
{
return _lastStateFullRep;
}
public bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary)
[NotNullWhen(true)] out Dictionary<ushort, IComponentState?>? dictionary)
{
return _lastStateFullRep.TryGetValue(entity, out dictionary);
}

View File

@@ -113,7 +113,7 @@ namespace Robust.Client.GameStates
/// <summary>
/// Returns the full collection of cached game states that are used to reset predicted entities.
/// </summary>
Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep();
Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep();
/// <summary>
/// This will perform some setup in order to reset the game to an earlier state. To fully reset the state

View File

@@ -83,13 +83,13 @@ namespace Robust.Client.GameStates
/// The data to merge.
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
/// </param>
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> data);
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> data);
/// <summary>
/// Get the last state data from the server for an entity.
/// </summary>
/// <returns>Dictionary (net ID -> ComponentState)</returns>
Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity entity);
Dictionary<ushort, IComponentState?> GetLastServerStates(NetEntity entity);
/// <summary>
/// Calculate the number of applicable states in the game state buffer from a given tick.
@@ -99,6 +99,6 @@ namespace Robust.Client.GameStates
int GetApplicableStateCount(GameTick? fromTick);
bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary);
[NotNullWhen(true)] out Dictionary<ushort, IComponentState?>? dictionary);
}
}

View File

@@ -313,7 +313,7 @@ namespace Robust.Client.GameStates
if (args.Length == 0)
{
entity = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
entity = _playerManager.LocalEntity ?? EntityUid.Invalid;
}
else if (!NetEntity.TryParse(args[0], out var netEntity) || !_entManager.TryGetEntity(netEntity, out entity))
{

View File

@@ -49,7 +49,7 @@ namespace Robust.Client.GameStates
while (query.MoveNext(out var uid, out var transform))
{
// if not on the same map, continue
if (transform.MapID != _eyeManager.CurrentMap || _container.IsEntityInContainer(uid))
if (transform.MapID != args.MapId || _container.IsEntityInContainer(uid))
continue;
if (transform.GridUid == uid)

View File

@@ -64,29 +64,22 @@ namespace Robust.Client.Graphics
/// <inheritdoc />
public Box2 GetWorldViewport()
{
var vpSize = _displayManager.ScreenSize;
var topLeft = ScreenToMap(Vector2.Zero);
var topRight = ScreenToMap(new Vector2(vpSize.X, 0));
var bottomRight = ScreenToMap(vpSize);
var bottomLeft = ScreenToMap(new Vector2(0, vpSize.Y));
var left = MathHelper.Min(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
var bottom = MathHelper.Min(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
var right = MathHelper.Max(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
var top = MathHelper.Max(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
return new Box2(left, bottom, right, top);
return GetWorldViewbounds().CalcBoundingBox();
}
/// <inheritdoc />
public Box2Rotated GetWorldViewbounds()
{
var vpSize = _displayManager.ScreenSize;
// This is an inefficient and roundabout way of geting the viewport.
// But its a method that shouldn't get used much.
var vp = MainViewport as Control;
var vpSize = vp?.PixelSize ?? _displayManager.ScreenSize;
var topRight = ScreenToMap(new Vector2(vpSize.X, 0)).Position;
var bottomLeft = ScreenToMap(new Vector2(0, vpSize.Y)).Position;
// This assumes the main viewports eye and the main eye are the same.
var rotation = new Angle(CurrentEye.Rotation);
var center = (bottomLeft + topRight) / 2;
@@ -108,18 +101,16 @@ namespace Robust.Client.Graphics
}
/// <inheritdoc />
public void GetScreenProjectionMatrix(out Matrix3 projMatrix)
public void GetScreenProjectionMatrix(out Matrix3x2 projMatrix)
{
Matrix3 result = default;
Matrix3x2 result = default;
result.R0C0 = PixelsPerMeter;
result.R1C1 = -PixelsPerMeter;
result.M11 = PixelsPerMeter;
result.M22 = -PixelsPerMeter;
var screenSize = _displayManager.ScreenSize;
result.R0C2 = screenSize.X / 2f;
result.R1C2 = screenSize.Y / 2f;
result.R2C2 = 1;
result.M31 = screenSize.X / 2f;
result.M32 = screenSize.Y / 2f;
/* column major
Sx 0 Tx

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using System;
using System.Numerics;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Graphics;
using Robust.Shared.Map;
@@ -13,26 +14,29 @@ namespace Robust.Client.Graphics
public interface IEyeManager
{
/// <summary>
/// The current eye that is being used to render the game.
/// The primary eye, which is usually the eye associated with the main viewport.
/// </summary>
/// <remarks>
/// Generally, you should avoid using this whenever possible. E.g., when rendering overlays should use the
/// eye & viewbounds that gets passed to the draw method.
/// Setting this property to null will use the default eye.
/// </remarks>
IEye CurrentEye { get; set; }
IViewportControl MainViewport { get; set; }
/// <summary>
/// The ID of the map on which the current eye is "placed".
/// </summary>
[Obsolete]
MapId CurrentMap { get; }
/// <summary>
/// A world-space box that is at LEAST the area covered by the viewport.
/// A world-space box that is at LEAST the area covered by the main viewport.
/// May be larger due to say rotation.
/// </summary>
Box2 GetWorldViewport();
/// <summary>
/// A world-space box of the area visible in the main viewport.
/// </summary>
Box2Rotated GetWorldViewbounds();
/// <summary>
@@ -40,10 +44,10 @@ namespace Robust.Client.Graphics
/// to UI screen space.
/// </summary>
/// <param name="projMatrix"></param>
void GetScreenProjectionMatrix(out Matrix3 projMatrix);
void GetScreenProjectionMatrix(out Matrix3x2 projMatrix);
/// <summary>
/// Projects a point from world space to UI screen space using the current camera.
/// Projects a point from world space to UI screen space using the main viewport.
/// </summary>
/// <param name="point">Point in world to transform.</param>
/// <returns>Corresponding point in UI screen space.</returns>

View File

@@ -6,7 +6,8 @@ namespace Robust.Client.Graphics.Clyde
{
("aPos", 0),
("tCoord", 1),
("modulate", 2)
("tCoord2", 2),
("modulate", 3)
};
private const int UniIModUV = 0;

View File

@@ -124,7 +124,7 @@ namespace Robust.Client.Graphics.Clyde
{
foreach (var (grid, chunks) in _mapChunkData)
{
var gridComp = _mapManager.GetGridComp(grid);
var gridComp = _entityManager.GetComponent<MapGridComponent>(grid);
foreach (var (index, chunk) in chunks)
{
if (!chunk.Dirty || gridComp.Chunks.ContainsKey(index))

View File

@@ -11,6 +11,7 @@ using Robust.Shared;
using Robust.Shared.Enums;
using Robust.Shared.Graphics;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Profiling;
using Robust.Shared.Utility;
@@ -250,10 +251,8 @@ namespace Robust.Client.Graphics.Clyde
private void DrawEntities(Viewport viewport, Box2Rotated worldBounds, Box2 worldAABB, IEye eye)
{
var mapId = eye.Position.MapId;
if (mapId == MapId.Nullspace || !_mapManager.HasMapEntity(mapId))
{
if (mapId == MapId.Nullspace)
return;
}
RenderOverlays(viewport, OverlaySpace.WorldSpaceBelowEntities, worldAABB, worldBounds);
var worldOverlays = GetOverlaysForSpace(OverlaySpace.WorldSpaceEntities);
@@ -368,7 +367,7 @@ namespace Robust.Client.Graphics.Clyde
_renderHandle.UseShader(entry.Sprite.PostShader);
CalcScreenMatrices(viewport.Size, out var proj, out var view);
_renderHandle.SetProjView(proj, view);
_renderHandle.SetModelTransform(Matrix3.Identity);
_renderHandle.SetModelTransform(Matrix3x2.Identity);
var rounded = roundedPos - entityPostRenderTarget.Size / 2;
@@ -481,7 +480,7 @@ namespace Robust.Client.Graphics.Clyde
var worldBounds = CalcWorldBounds(viewport);
var worldAABB = worldBounds.CalcBoundingBox();
if (_eyeManager.CurrentMap != MapId.Nullspace)
if (eye.Position.MapId != MapId.Nullspace)
{
using (DebugGroup("Lights"))
using (_prof.Group("Lights"))
@@ -514,7 +513,9 @@ namespace Robust.Client.Graphics.Clyde
if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawLight && eye.DrawFov)
{
ApplyFovToBuffer(viewport, eye);
var mapUid = _mapManager.GetMapEntityId(eye.Position.MapId);
if (_entityManager.GetComponent<MapComponent>(mapUid).LightingEnabled)
ApplyFovToBuffer(viewport, eye);
}
}
@@ -529,7 +530,7 @@ namespace Robust.Client.Graphics.Clyde
// Because the math is wrong.
// So there are distortions from incorrect projection.
_renderHandle.UseShader(_fovDebugShaderInstance);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
var pos = UIBox2.FromDimensions(viewport.Size / 2 - new Vector2(200, 200), new Vector2(400, 400));
_renderHandle.DrawingHandleScreen.DrawTextureRect(FovTexture, pos);
}
@@ -537,15 +538,18 @@ namespace Robust.Client.Graphics.Clyde
if (DebugLayers == ClydeDebugLayers.Light)
{
_renderHandle.UseShader(null);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
_renderHandle.DrawingHandleScreen.DrawTextureRect(
viewport.WallBleedIntermediateRenderTarget2.Texture,
UIBox2.FromDimensions(Vector2.Zero, viewport.Size), new Color(1, 1, 1, 0.5f));
}
using (_prof.Group("Overlays WS"))
if (eye.Position.MapId != MapId.Nullspace)
{
RenderOverlays(viewport, OverlaySpace.WorldSpace, worldAABB, worldBounds);
using (_prof.Group("Overlays WS"))
{
RenderOverlays(viewport, OverlaySpace.WorldSpace, worldAABB, worldBounds);
}
}
_currentViewport = oldVp;

View File

@@ -23,9 +23,12 @@ namespace Robust.Client.Graphics.Clyde
// Texture Coords.
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 2 * sizeof(float));
GL.EnableVertexAttribArray(1);
// Colour Modulation.
GL.VertexAttribPointer(2, 4, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 4 * sizeof(float));
// Texture Coords (2).
GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 4 * sizeof(float));
GL.EnableVertexAttribArray(2);
// Colour Modulation.
GL.VertexAttribPointer(3, 4, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 6 * sizeof(float));
GL.EnableVertexAttribArray(3);
}
// NOTE: This is:
@@ -37,6 +40,7 @@ namespace Robust.Client.Graphics.Clyde
{
public readonly Vector2 Position;
public readonly Vector2 TextureCoordinates;
public readonly Vector2 TextureCoordinates2;
// Note that this color is in linear space.
public readonly Color Modulate;
@@ -48,6 +52,15 @@ namespace Robust.Client.Graphics.Clyde
Modulate = modulate;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vertex2D(Vector2 position, Vector2 textureCoordinates, Vector2 textureCoordinates2, Color modulate)
{
Position = position;
TextureCoordinates = textureCoordinates;
TextureCoordinates2 = textureCoordinates2;
Modulate = modulate;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vertex2D(float x, float y, float u, float v, float r, float g, float b, float a)
: this(new Vector2(x, y), new Vector2(u, v), new Color(r, g, b, a))
@@ -85,28 +98,34 @@ namespace Robust.Client.Graphics.Clyde
[FieldOffset(20 * sizeof(float))] public Vector3 ViewMatrixC2;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ProjViewMatrices(in Matrix3 projMatrix, in Matrix3 viewMatrix)
public ProjViewMatrices(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix)
{
ProjMatrixC0 = new Vector3(projMatrix.R0C0, projMatrix.R1C0, projMatrix.R2C0);
ProjMatrixC1 = new Vector3(projMatrix.R0C1, projMatrix.R1C1, projMatrix.R2C1);
ProjMatrixC2 = new Vector3(projMatrix.R0C2, projMatrix.R1C2, projMatrix.R2C2);
// We put the rows of the input matrix into the columns of our GPU matrices
// this transpose is required, as in C#, we premultiply vectors with matrices
// (vM) while GL postmultiplies vectors with matrices (Mv); however, since
// the Matrix3x2 data is stored row-major, and GL uses column-major, the
// memory layout is the same (or would be, if Matrix3x2 didn't have an
// implicit column)
ProjMatrixC0 = new Vector3(projMatrix.M11, projMatrix.M12, 0);
ProjMatrixC1 = new Vector3(projMatrix.M21, projMatrix.M22, 0);
ProjMatrixC2 = new Vector3(projMatrix.M31, projMatrix.M32, 1);
ViewMatrixC0 = new Vector3(viewMatrix.R0C0, viewMatrix.R1C0, viewMatrix.R2C0);
ViewMatrixC1 = new Vector3(viewMatrix.R0C1, viewMatrix.R1C1, viewMatrix.R2C1);
ViewMatrixC2 = new Vector3(viewMatrix.R0C2, viewMatrix.R1C2, viewMatrix.R2C2);
ViewMatrixC0 = new Vector3(viewMatrix.M11, viewMatrix.M12, 0);
ViewMatrixC1 = new Vector3(viewMatrix.M21, viewMatrix.M22, 0);
ViewMatrixC2 = new Vector3(viewMatrix.M31, viewMatrix.M32, 1);
}
public void Apply(Clyde clyde, GLShaderProgram program)
{
program.SetUniformMaybe("projectionMatrix", new Matrix3(
ProjMatrixC0.X, ProjMatrixC1.X, ProjMatrixC2.X,
ProjMatrixC0.Y, ProjMatrixC1.Y, ProjMatrixC2.Y,
ProjMatrixC0.Z, ProjMatrixC1.Z, ProjMatrixC2.Z
program.SetUniformMaybe("projectionMatrix", new Matrix3x2(
ProjMatrixC0.X, ProjMatrixC0.Y, // Implicit 0
ProjMatrixC1.X, ProjMatrixC1.Y, // Implicit 0
ProjMatrixC2.X, ProjMatrixC2.Y // Implicit 1
));
program.SetUniformMaybe("viewMatrix", new Matrix3(
ViewMatrixC0.X, ViewMatrixC1.X, ViewMatrixC2.X,
ViewMatrixC0.Y, ViewMatrixC1.Y, ViewMatrixC2.Y,
ViewMatrixC0.Z, ViewMatrixC1.Z, ViewMatrixC2.Z
program.SetUniformMaybe("viewMatrix", new Matrix3x2(
ViewMatrixC0.X, ViewMatrixC0.Y, // Implicit 0
ViewMatrixC1.X, ViewMatrixC1.Y, // Implicit 0
ViewMatrixC2.X, ViewMatrixC2.Y // Implicit 1
));
}
}

View File

@@ -492,18 +492,18 @@ namespace Robust.Client.Graphics.Clyde
var offset = new Vector2(component.Radius, component.Radius);
Matrix3 matrix;
Matrix3x2 matrix;
if (mask == null)
{
matrix = Matrix3.Identity;
matrix = Matrix3x2.Identity;
}
else
{
// Only apply rotation if a mask is said, because else it doesn't matter.
matrix = Matrix3.CreateRotation(rotation);
matrix = Matrix3Helpers.CreateRotation(rotation);
}
(matrix.R0C2, matrix.R1C2) = lightPos;
(matrix.M31, matrix.M32) = lightPos;
_drawQuad(-offset, offset, matrix, lightShader);
}
@@ -692,7 +692,7 @@ namespace Robust.Client.Graphics.Clyde
// Blur horizontally to _wallBleedIntermediateRenderTarget1.
shader.SetUniformMaybe("direction", Vector2.UnitX);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.LightBlurTarget.Texture);
@@ -700,7 +700,7 @@ namespace Robust.Client.Graphics.Clyde
// Blur vertically to _wallBleedIntermediateRenderTarget2.
shader.SetUniformMaybe("direction", Vector2.UnitY);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.LightRenderTarget.Texture);
}
@@ -754,14 +754,14 @@ namespace Robust.Client.Graphics.Clyde
// Blur horizontally to _wallBleedIntermediateRenderTarget1.
shader.SetUniformMaybe("direction", Vector2.UnitX);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget1.Texture);
BindRenderTargetFull(viewport.WallBleedIntermediateRenderTarget2);
// Blur vertically to _wallBleedIntermediateRenderTarget2.
shader.SetUniformMaybe("direction", Vector2.UnitY);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget2.Texture);
}
@@ -909,13 +909,13 @@ namespace Robust.Client.Graphics.Clyde
// Second modification is that output must be fov-centred (difference-space)
uZero -= fovCentre;
var clipToDiff = new Matrix3(in uX, in uY, in uZero);
var clipToDiff = new Matrix3x2(uX.X, uX.Y, uY.X, uY.Y, uZero.X, uZero.Y);
fovShader.SetUniformMaybe("clipToDiff", clipToDiff);
_drawQuad(Vector2.Zero, Vector2.One, Matrix3.Identity, fovShader);
_drawQuad(Vector2.Zero, Vector2.One, Matrix3x2.Identity, fovShader);
}
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3 eyeTransform)
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3x2 eyeTransform)
{
using var _ = _prof.Group("UpdateOcclusionGeometry");
using var _p = DebugGroup(nameof(UpdateOcclusionGeometry));
@@ -968,9 +968,9 @@ namespace Robust.Client.Graphics.Clyde
var worldTransform = xformSystem.GetWorldMatrix(transform, xforms);
var box = occluder.BoundingBox;
var tl = worldTransform.Transform(box.TopLeft);
var tr = worldTransform.Transform(box.TopRight);
var br = worldTransform.Transform(box.BottomRight);
var tl = Vector2.Transform(box.TopLeft, worldTransform);
var tr = Vector2.Transform(box.TopRight, worldTransform);
var br = Vector2.Transform(box.BottomRight, worldTransform);
var bl = tl + br - tr;
// Faces.
@@ -1010,9 +1010,9 @@ namespace Robust.Client.Graphics.Clyde
//
// Calculate delta positions from camera.
var dTl = eyeTransform.Transform(tl);
var dTr = eyeTransform.Transform(tr);
var dBl = eyeTransform.Transform(bl);
var dTl = Vector2.Transform(tl, eyeTransform);
var dTr = Vector2.Transform(tr, eyeTransform);
var dBl = Vector2.Transform(bl, eyeTransform);
var dBr = dBl + dTr - dTl;
// Get which neighbors are occluding.
@@ -1201,7 +1201,7 @@ namespace Robust.Client.Graphics.Clyde
private void LightResolutionScaleChanged(float newValue)
{
_lightResolutionScale = newValue;
_lightResolutionScale = newValue > 0.05f ? newValue : 0.05f;
RegenAllLightRts();
}

View File

@@ -15,7 +15,7 @@ namespace Robust.Client.Graphics.Clyde
{
private RenderHandle _renderHandle = default!;
private sealed class RenderHandle : IRenderHandle
internal sealed class RenderHandle : IRenderHandle
{
private readonly Clyde _clyde;
private readonly IEntityManager _entities;
@@ -33,17 +33,17 @@ namespace Robust.Client.Graphics.Clyde
DrawingHandleWorld = new DrawingHandleWorldImpl(white, this);
}
public void SetModelTransform(in Matrix3 matrix)
public void SetModelTransform(in Matrix3x2 matrix)
{
_clyde.DrawSetModelTransform(matrix);
}
public Matrix3 GetModelTransform()
public Matrix3x2 GetModelTransform()
{
return _clyde.DrawGetModelTransform();
}
public void SetProjView(in Matrix3 proj, in Matrix3 view)
public void SetProjView(in Matrix3x2 proj, in Matrix3x2 view)
{
_clyde.DrawSetProjViewTransform(proj, view);
}
@@ -88,16 +88,21 @@ namespace Robust.Client.Graphics.Clyde
{
var clydeTexture = ExtractTexture(texture, in subRegion, out var csr);
var (w, h) = clydeTexture.Size;
var sr = new Box2(csr.Left / w, (h - csr.Bottom) / h, csr.Right / w, (h - csr.Top) / h);
var sr = WorldTextureBoundsToUV(clydeTexture, csr);
_clyde.DrawTexture(clydeTexture.TextureId, bl, br, tl, tr, in modulate, in sr);
}
internal static Box2 WorldTextureBoundsToUV(ClydeTexture texture, UIBox2 csr)
{
var (w, h) = texture.Size;
return new Box2(csr.Left / w, (h - csr.Bottom) / h, csr.Right / w, (h - csr.Top) / h);
}
/// <summary>
/// Converts a subRegion (px) into texture coords (0-1) of a given texture (cells of the textureAtlas).
/// </summary>
private static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegion, out UIBox2 sr)
internal static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegion, out UIBox2 sr)
{
if (texture is AtlasTexture atlas)
{
@@ -172,9 +177,9 @@ namespace Robust.Client.Graphics.Clyde
var oldModel = _clyde._currentMatrixModel;
var newModel = oldModel;
position += new Vector2(oldModel.R0C2, oldModel.R1C2);
newModel.R0C2 = 0;
newModel.R1C2 = 0;
position += new Vector2(oldModel.M31, oldModel.M32);
newModel.M31 = 0;
newModel.M32 = 0;
SetModelTransform(newModel);
// Switch rendering to pseudo-world space.
@@ -189,7 +194,7 @@ namespace Robust.Client.Graphics.Clyde
// Maaaaybe this is meant to have a minus sign.
var rot = -(float) eyeRot.Theta;
var view = Matrix3.CreateTransform(ofsX, ofsY, rot, scale.X, scale.Y);
var view = Matrix3Helpers.CreateTransform(ofsX, ofsY, rot, scale.X, scale.Y);
SetProjView(proj, view);
}
@@ -292,12 +297,12 @@ namespace Robust.Client.Graphics.Clyde
_renderHandle = renderHandle;
}
public override void SetTransform(in Matrix3 matrix)
public override void SetTransform(in Matrix3x2 matrix)
{
_renderHandle.SetModelTransform(matrix);
}
public override Matrix3 GetTransform()
public override Matrix3x2 GetTransform()
{
return _renderHandle.GetModelTransform();
}
@@ -397,12 +402,12 @@ namespace Robust.Client.Graphics.Clyde
_renderHandle = renderHandle;
}
public override void SetTransform(in Matrix3 matrix)
public override void SetTransform(in Matrix3x2 matrix)
{
_renderHandle.SetModelTransform(matrix);
}
public override Matrix3 GetTransform()
public override Matrix3x2 GetTransform()
{
return _renderHandle.GetModelTransform();
}

View File

@@ -62,7 +62,7 @@ namespace Robust.Client.Graphics.Clyde
// This matrix is applied to most normal geometry coming in.
// Some is applied while the batch is being created (e.g. simple texture draw calls).
// For DrawPrimitives OTOH the model matrix is passed along with the render command so is applied in the shader.
private Matrix3 _currentMatrixModel = Matrix3.Identity;
private Matrix3x2 _currentMatrixModel = Matrix3x2.Identity;
// Buffers and data for the batching system. Written into during (queue) and processed during (submit).
private readonly Vertex2D[] BatchVertexData = new Vertex2D[MaxBatchQuads * 4];
@@ -84,8 +84,8 @@ namespace Robust.Client.Graphics.Clyde
// Current projection & view matrices that are being used ot render.
// This gets updated to keep track during (queue) and (misc), but not during (submit).
private Matrix3 _currentMatrixProj;
private Matrix3 _currentMatrixView;
private Matrix3x2 _currentMatrixProj;
private Matrix3x2 _currentMatrixView;
// (queue) and (misc), current state of the scissor test. Null if disabled.
private UIBox2i? _currentScissorState;
@@ -110,25 +110,25 @@ namespace Robust.Client.Graphics.Clyde
UniformConstantsUBO.Reallocate(constants);
}
private void CalcScreenMatrices(in Vector2i screenSize, out Matrix3 proj, out Matrix3 view)
private void CalcScreenMatrices(in Vector2i screenSize, out Matrix3x2 proj, out Matrix3x2 view)
{
proj = Matrix3.Identity;
proj.R0C0 = 2f / screenSize.X;
proj.R1C1 = -2f / screenSize.Y;
proj.R0C2 = -1;
proj.R1C2 = 1;
proj = Matrix3x2.Identity;
proj.M11 = 2f / screenSize.X;
proj.M22 = -2f / screenSize.Y;
proj.M31 = -1;
proj.M32 = 1;
if (_currentRenderTarget.FlipY)
{
proj.R1C1 *= -1;
proj.R1C2 *= -1;
proj.M22 *= -1;
proj.M32 *= -1;
}
view = Matrix3.Identity;
view = Matrix3x2.Identity;
}
private void CalcWorldMatrices(in Vector2i screenSize, in Vector2 renderScale, IEye eye,
out Matrix3 proj, out Matrix3 view)
out Matrix3x2 proj, out Matrix3x2 view)
{
eye.GetViewMatrix(out view, renderScale);
@@ -136,20 +136,20 @@ namespace Robust.Client.Graphics.Clyde
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CalcWorldProjMatrix(in Vector2i screenSize, out Matrix3 proj)
private void CalcWorldProjMatrix(in Vector2i screenSize, out Matrix3x2 proj)
{
proj = Matrix3.Identity;
proj.R0C0 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
proj.R1C1 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
proj = Matrix3x2.Identity;
proj.M11 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
proj.M22 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
if (_currentRenderTarget.FlipY)
{
proj.R1C1 *= -1;
proj.R1C2 *= -1;
proj.M22 *= -1;
proj.M32 *= -1;
}
}
private void SetProjViewBuffer(in Matrix3 proj, in Matrix3 view)
private void SetProjViewBuffer(in Matrix3x2 proj, in Matrix3x2 view)
{
// TODO: Fix perf here.
// This immediately causes a glBufferData() call every time this is changed.
@@ -160,7 +160,7 @@ namespace Robust.Client.Graphics.Clyde
ProjViewUBO.Reallocate(combined);
}
private void SetProjViewFull(in Matrix3 proj, in Matrix3 view)
private void SetProjViewFull(in Matrix3x2 proj, in Matrix3x2 view)
{
_currentMatrixProj = proj;
_currentMatrixView = view;
@@ -285,21 +285,21 @@ namespace Robust.Client.Graphics.Clyde
};
}
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3 modelMatrix, GLShaderProgram program)
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3x2 modelMatrix, GLShaderProgram program)
{
DrawQuadWithVao(QuadVAO, a, b, modelMatrix, program);
}
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3 modelMatrix,
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3x2 modelMatrix,
GLShaderProgram program)
{
BindVertexArray(vao.Handle);
CheckGlError();
var rectTransform = Matrix3.Identity;
(rectTransform.R0C0, rectTransform.R1C1) = b - a;
(rectTransform.R0C2, rectTransform.R1C2) = a;
rectTransform.Multiply(modelMatrix);
var rectTransform = Matrix3x2.Identity;
(rectTransform.M11, rectTransform.M22) = b - a;
(rectTransform.M31, rectTransform.M32) = a;
rectTransform = rectTransform * modelMatrix;
program.SetUniformMaybe(UniIModelMatrix, rectTransform);
_debugStats.LastGLDrawCalls += 1;
@@ -315,7 +315,7 @@ namespace Robust.Client.Graphics.Clyde
FlushBatchQueue();
// Reset renderer state.
_currentMatrixModel = Matrix3.Identity;
_currentMatrixModel = Matrix3x2.Identity;
_queuedShaderInstance = _defaultShader;
SetScissorFull(null);
}
@@ -496,7 +496,7 @@ namespace Robust.Client.Graphics.Clyde
case bool b:
program.SetUniform(name, b ? 1 : 0);
break;
case Matrix3 matrix3:
case Matrix3x2 matrix3:
program.SetUniform(name, matrix3);
break;
case Matrix4 matrix4:
@@ -532,17 +532,17 @@ namespace Robust.Client.Graphics.Clyde
return ref command;
}
private void DrawSetModelTransform(in Matrix3 matrix)
private void DrawSetModelTransform(in Matrix3x2 matrix)
{
_currentMatrixModel = matrix;
}
private Matrix3 DrawGetModelTransform()
private Matrix3x2 DrawGetModelTransform()
{
return _currentMatrixModel;
}
private void DrawSetProjViewTransform(in Matrix3 proj, in Matrix3 view)
private void DrawSetProjViewTransform(in Matrix3x2 proj, in Matrix3x2 view)
{
BreakBatch();
@@ -571,17 +571,17 @@ namespace Robust.Client.Graphics.Clyde
EnsureBatchSpaceAvailable(4, GetQuadBatchIndexCount());
EnsureBatchState(texture, true, GetQuadBatchPrimitiveType(), _queuedShader);
bl = _currentMatrixModel.Transform(bl);
br = _currentMatrixModel.Transform(br);
tr = _currentMatrixModel.Transform(tr);
bl = Vector2.Transform(bl, _currentMatrixModel);
br = Vector2.Transform(br, _currentMatrixModel);
tr = Vector2.Transform(tr, _currentMatrixModel);
tl = tr + bl - br;
// TODO: split batch if necessary.
var vIdx = BatchVertexIndex;
BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft, modulate);
BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight, modulate);
BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight, modulate);
BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft, modulate);
BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft, new Vector2(0, 0), modulate);
BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight, new Vector2(1, 0), modulate);
BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight, new Vector2(1, 1), modulate);
BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft, new Vector2(0, 1), modulate);
BatchVertexIndex += 4;
QuadBatchIndexWrite(BatchIndexData, ref BatchIndexIndex, (ushort) vIdx);
@@ -676,8 +676,8 @@ namespace Robust.Client.Graphics.Clyde
EnsureBatchSpaceAvailable(2, 0);
EnsureBatchState(_stockTextureWhite.TextureId, false, BatchPrimitiveType.LineList, _queuedShader);
a = _currentMatrixModel.Transform(a);
b = _currentMatrixModel.Transform(b);
a = Vector2.Transform(a, _currentMatrixModel);
b = Vector2.Transform(b, _currentMatrixModel);
// TODO: split batch if necessary.
var vIdx = BatchVertexIndex;
@@ -807,7 +807,7 @@ namespace Robust.Client.Graphics.Clyde
command.DrawBatch.ShaderInstance = metaData.ShaderInstance;
command.DrawBatch.Count = currentIndex - metaData.StartIndex;
command.DrawBatch.ModelMatrix = Matrix3.Identity;
command.DrawBatch.ModelMatrix = Matrix3x2.Identity;
_debugStats.LastBatches += 1;
}
@@ -882,7 +882,7 @@ namespace Robust.Client.Graphics.Clyde
_queuedRenderCommands.Clear();
_currentViewport = null;
_lightingReady = false;
_currentMatrixModel = Matrix3.Identity;
_currentMatrixModel = Matrix3x2.Identity;
SetScissorFull(null);
BindRenderTargetFull(_mainWindow!.RenderTarget);
_batchMetaData = null;
@@ -961,13 +961,13 @@ namespace Robust.Client.Graphics.Clyde
public BatchPrimitiveType PrimitiveType;
// TODO: this makes the render commands so much more large please remove.
public Matrix3 ModelMatrix;
public Matrix3x2 ModelMatrix;
}
private struct RenderCommandProjViewMatrix
{
public Matrix3 ProjMatrix;
public Matrix3 ViewMatrix;
public Matrix3x2 ProjMatrix;
public Matrix3x2 ViewMatrix;
}
private struct RenderCommandScissor
@@ -1056,11 +1056,11 @@ namespace Robust.Client.Graphics.Clyde
private readonly struct FullStoredRendererState
{
public readonly Matrix3 ProjMatrix;
public readonly Matrix3 ViewMatrix;
public readonly Matrix3x2 ProjMatrix;
public readonly Matrix3x2 ViewMatrix;
public readonly LoadedRenderTarget RenderTarget;
public FullStoredRendererState(in Matrix3 projMatrix, in Matrix3 viewMatrix,
public FullStoredRendererState(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix,
LoadedRenderTarget renderTarget)
{
ProjMatrix = projMatrix;

View File

@@ -506,7 +506,7 @@ namespace Robust.Client.Graphics.Clyde
data.Parameters[name] = value;
}
private protected override void SetParameterImpl(string name, in Matrix3 value)
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
{
var data = Parent._shaderInstances[Handle];
data.ParametersDirty = true;

View File

@@ -601,7 +601,7 @@ namespace Robust.Client.Graphics.Clyde
}
}
private sealed class ClydeTexture : OwnedTexture
internal sealed class ClydeTexture : OwnedTexture
{
private readonly Clyde _clyde;
public readonly bool IsSrgb;
@@ -649,24 +649,30 @@ namespace Robust.Client.Graphics.Clyde
return $"ClydeTexture: ({TextureId})";
}
public override Color GetPixel(int x, int y)
public override unsafe Color GetPixel(int x, int y)
{
if (!_clyde._loadedTextures.TryGetValue(TextureId, out var loaded))
{
throw new DataException("Texture not found");
}
Span<byte> rgba = stackalloc byte[4*this.Size.X*this.Size.Y];
unsafe
{
fixed (byte* p = rgba)
{
var curTexture2D = GL.GetInteger(GetPName.TextureBinding2D);
var bufSize = 4 * loaded.Size.X * loaded.Size.Y;
var buffer = ArrayPool<byte>.Shared.Rent(bufSize);
GL.GetTextureImage(loaded.OpenGLObject.Handle, 0, PF.Rgba, PT.UnsignedByte, 4*this.Size.X*this.Size.Y, (IntPtr) p);
}
GL.BindTexture(TextureTarget.Texture2D, loaded.OpenGLObject.Handle);
fixed (byte* p = buffer)
{
GL.GetTexImage(TextureTarget.Texture2D, 0, PF.Rgba, PT.UnsignedByte, (IntPtr) p);
}
int pixelPos = (this.Size.X*(this.Size.Y-y) + x)*4;
return new Color(rgba[pixelPos+0], rgba[pixelPos+1], rgba[pixelPos+2], rgba[pixelPos+3]);
GL.BindTexture(TextureTarget.Texture2D, curTexture2D);
var pixelPos = (loaded.Size.X * (loaded.Size.Y - y - 1) + x) * 4;
var color = new Color(buffer[pixelPos+0], buffer[pixelPos+1], buffer[pixelPos+2], buffer[pixelPos+3]);
ArrayPool<byte>.Shared.Return(buffer);
return color;
}
}

View File

@@ -52,7 +52,7 @@ namespace Robust.Client.Graphics.Clyde
// view matrix
vp.Eye.GetViewMatrixInv(out var viewMatrixInv, vp.RenderScale);
point = viewMatrixInv * point;
point = Vector2.Transform(point, viewMatrixInv);
return point;
}
@@ -136,7 +136,7 @@ namespace Robust.Client.Graphics.Clyde
// view matrix
Eye.GetViewMatrixInv(out var viewMatrixInv, RenderScale);
newPoint = viewMatrixInv * newPoint;
newPoint = Vector2.Transform(newPoint, viewMatrixInv);
return new MapCoordinates(newPoint, Eye.Position.MapId);
}
@@ -150,7 +150,7 @@ namespace Robust.Client.Graphics.Clyde
var newPoint = point;
eye.GetViewMatrix(out var viewMatrix, RenderScale);
newPoint = viewMatrix * newPoint;
newPoint = Vector2.Transform(newPoint, viewMatrix);
// (inlined version of UiProjMatrix)
newPoint *= new Vector2(1, -1) * EyeManager.PixelsPerMeter;
@@ -159,14 +159,14 @@ namespace Robust.Client.Graphics.Clyde
return newPoint;
}
public Matrix3 GetWorldToLocalMatrix()
public Matrix3x2 GetWorldToLocalMatrix()
{
if (Eye == null)
return Matrix3.Identity;
return Matrix3x2.Identity;
Eye.GetViewMatrix(out var viewMatrix, RenderScale * new Vector2(EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter));
viewMatrix.R0C2 += Size.X / 2f;
viewMatrix.R1C2 += Size.Y / 2f;
viewMatrix.M31 += Size.X / 2f;
viewMatrix.M32 += Size.Y / 2f;
return viewMatrix;
}

View File

@@ -361,7 +361,7 @@ namespace Robust.Client.Graphics.Clyde
{
}
private protected override void SetParameterImpl(string name, in Matrix3 value)
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
{
}
@@ -482,7 +482,7 @@ namespace Robust.Client.Graphics.Clyde
return default;
}
public Matrix3 GetWorldToLocalMatrix() => default;
public Matrix3x2 GetWorldToLocalMatrix() => default;
public Vector2 WorldToLocal(Vector2 point)
{

View File

@@ -247,28 +247,33 @@ namespace Robust.Client.Graphics.Clyde
GL.Uniform1(uniformId, singles.Length, singles);
}
public void SetUniform(string uniformName, in Matrix3 matrix)
public void SetUniform(string uniformName, in Matrix3x2 matrix)
{
var uniformId = GetUniform(uniformName);
SetUniformDirect(uniformId, matrix);
}
public void SetUniform(int uniformName, in Matrix3 matrix)
public void SetUniform(int uniformName, in Matrix3x2 matrix)
{
var uniformId = GetUniform(uniformName);
SetUniformDirect(uniformId, matrix);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe void SetUniformDirect(int slot, in Matrix3 value, bool transpose=true)
private unsafe void SetUniformDirect(int slot, in Matrix3x2 value)
{
Matrix3 tmpTranspose = value;
if (transpose)
{
// transposition not supported on GLES2, & no access to _hasGLES
tmpTranspose.Transpose();
}
GL.UniformMatrix3(slot, 1, false, (float*) &tmpTranspose);
// We put the rows of the input matrix into the columns of our GPU matrices
// this transpose is required, as in C#, we premultiply vectors with matrices
// (vM) while GL postmultiplies vectors with matrices (Mv); however, since
// the Matrix3x2 data is stored row-major, and GL uses column-major, the
// memory layout is the same (or would be, if Matrix3x2 didn't have an
// implicit column)
var buf = stackalloc float[9]{
value.M11, value.M12, 0,
value.M21, value.M22, 0,
value.M31, value.M32, 1
};
GL.UniformMatrix3(slot, 1, false, (float*)buf);
_clyde.CheckGlError();
}
@@ -474,7 +479,7 @@ namespace Robust.Client.Graphics.Clyde
}
}
public void SetUniformMaybe(string uniformName, in Matrix3 value)
public void SetUniformMaybe(string uniformName, in Matrix3x2 value)
{
if (TryGetUniform(uniformName, out var slot))
{
@@ -490,7 +495,7 @@ namespace Robust.Client.Graphics.Clyde
}
}
public void SetUniformMaybe(int uniformName, in Matrix3 value)
public void SetUniformMaybe(int uniformName, in Matrix3x2 value)
{
if (TryGetUniform(uniformName, out var slot))
{

View File

@@ -1,4 +1,5 @@
varying highp vec2 UV;
varying highp vec2 UV2;
varying highp vec2 Pos;
varying highp vec4 VtxModulate;

View File

@@ -2,10 +2,12 @@
/*layout (location = 0)*/ attribute vec2 aPos;
// Texture coordinates.
/*layout (location = 1)*/ attribute vec2 tCoord;
/*layout (location = 2)*/ attribute vec2 tCoord2;
// Colour modulation.
/*layout (location = 2)*/ attribute vec4 modulate;
/*layout (location = 3)*/ attribute vec4 modulate;
varying vec2 UV;
varying vec2 UV2;
varying vec2 Pos;
varying vec4 VtxModulate;
@@ -36,5 +38,6 @@ void main()
gl_Position = vec4(VERTEX, 0.0, 1.0);
Pos = (VERTEX + 1.0) / 2.0;
UV = mix(modifyUV.xy, modifyUV.zw, tCoord);
UV2 = tCoord2;
VtxModulate = zFromSrgb(modulate);
}

View File

@@ -1,4 +1,5 @@
varying highp vec2 UV;
varying highp vec2 UV2;
uniform sampler2D lightMap;

View File

@@ -2,10 +2,12 @@
/*layout (location = 0)*/ attribute vec2 aPos;
// Texture coordinates.
/*layout (location = 1)*/ attribute vec2 tCoord;
/*layout (location = 2)*/ attribute vec2 tCoord2;
// Colour modulation.
/*layout (location = 2)*/ attribute vec4 modulate;
/*layout (location = 3)*/ attribute vec4 modulate;
varying vec2 UV;
varying vec2 UV2;
// Maybe we should merge these CPU side.
// idk yet.
@@ -40,6 +42,7 @@ void main()
vec2 VERTEX = aPos;
UV = tCoord;
UV2 = tCoord2;
// [SHADER_CODE]

View File

@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Robust.Shared.Graphics;
using System.Runtime.Intrinsics;
using Robust.Shared.Maths;
namespace Robust.Client.Graphics
@@ -43,19 +44,19 @@ namespace Robust.Client.Graphics
{
CheckDisposed();
var matrix = Matrix3.CreateTransform(in position, in rotation, in scale);
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation, in scale);
SetTransform(in matrix);
}
public void SetTransform(in Vector2 position, in Angle rotation)
{
var matrix = Matrix3.CreateTransform(in position, in rotation);
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation);
SetTransform(in matrix);
}
public abstract void SetTransform(in Matrix3 matrix);
public abstract void SetTransform(in Matrix3x2 matrix);
public abstract Matrix3 GetTransform();
public abstract Matrix3x2 GetTransform();
public abstract void UseShader(ShaderInstance? shader);
@@ -63,6 +64,19 @@ namespace Robust.Client.Graphics
// ---- DrawPrimitives: Vector2 API ----
/// <summary>
/// Draws arbitrary geometry primitives with a flat color.
/// </summary>
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
/// <param name="vertices">The list of vertices to render.</param>
/// <param name="color">The color to draw with.</param>
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, List<Vector2> vertices,
Color color)
{
var span = CollectionsMarshal.AsSpan(vertices);
DrawPrimitives(primitiveTopology, span, color);
}
/// <summary>
/// Draws arbitrary geometry primitives with a flat color.
/// </summary>
@@ -223,6 +237,8 @@ namespace Robust.Client.Graphics
{
public Vector2 Position;
public Vector2 UV;
public Vector2 UV2;
/// <summary>
/// Modulation colour for this vertex.
/// Note that this color is in linear space.

View File

@@ -49,7 +49,7 @@ namespace Robust.Client.Graphics
/// <summary>
/// Matrix equivalent of <see cref="LocalToWorld(Vector2)"/>.
/// </summary>
Matrix3 GetWorldToLocalMatrix();
Matrix3x2 GetWorldToLocalMatrix();
/// <summary>
/// Converts a point in world-space to the viewport's screen coordinates.

View File

@@ -6,7 +6,10 @@ using Robust.Shared.Maths;
namespace Robust.Client.Graphics
{
internal interface IRenderHandle
/// <remarks>
/// Unstable API. Likely to break hard during renderer rewrite if you rely on it.
/// </remarks>
public interface IRenderHandle
{
DrawingHandleScreen DrawingHandleScreen { get; }
DrawingHandleWorld DrawingHandleWorld { get; }

View File

@@ -22,7 +22,7 @@ namespace Robust.Client.Graphics
/// If set to true, <see cref="ScreenTexture"/> will be set to the current frame (at the moment before the overlay is rendered). This can be costly to performance, but
/// some shaders will require it as a passed in uniform to operate.
/// </summary>
public virtual bool RequestScreenTexture => false;
public virtual bool RequestScreenTexture { get; set; } = false;
/// <summary>
/// If <see cref="RequestScreenTexture"> is true, then this will be set to the texture corresponding to the current frame. If false, it will always be null.

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