Compare commits

...

268 Commits

Author SHA1 Message Date
Pieter-Jan Briers
c2a1521c95 Version: 214.2.2 2024-08-11 19:32:36 +02:00
Pieter-Jan Briers
a26b48414b Compile compat fixes
(cherry picked from commit 025d90d281)
(cherry picked from commit 799702b814)
(cherry picked from commit 4600ee8e5788891f1b610e2d5141fb4e1228d323)
2024-08-11 19:32:36 +02:00
Pieter-Jan Briers
614a03036b Version: 214.2.1 2024-08-11 17:56:10 +02:00
Pieter-Jan Briers
8c8a3c0e17 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)
(cherry picked from commit db8ba83866c523e08e4fba0b80cd954f4f190613)
2024-08-11 17:56:10 +02: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
metalgearsloth
9e0fc7017c Version: 209.0.1 2024-02-02 01:02:42 +11:00
metalgearsloth
76317b7ab3 Fix bad import (#4882)
fork why are you so SLOW.
2024-02-02 01:01:21 +11:00
metalgearsloth
d5f4d4bf2f Version: 209.0.0 2024-02-02 00:40:18 +11:00
metalgearsloth
156d1a6b14 Add SharedMapSystem helper for tile methods (#4845)
* Add SharedMapSystem helper for tile methods

* weh

* release

* Resolves

* Move some of these

* note

* Remove obsolete

* note
2024-02-02 00:39:20 +11:00
metalgearsloth
3fa456fd44 Fix relay refreshes where the entity gets contained to itself (#4865)
If the parent is somewhere below the transform hierarchy should still be okay, this just fixes where something getting pulled gets picked up as pulling doesn't seem to be handling this anymore.
2024-02-01 20:33:33 +11:00
LordCarve
bf9bb46154 Added ReferenceEquals() tests for RobustIntegrationTest (#4839)
* Added a test that checks that RobustIntegrationTest Client and Server do not end up with same sub-ComponentState reference objects.

* Un-ignore and adjust the test.

* review

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-02-01 20:22:52 +11:00
metalgearsloth
fb3da0b53c Add entitylookup methods for parent / map (#4870)
* Add entitylookup methods for parent / map

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

* weh

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-01-31 17:21:41 +01:00
Pieter-Jan Briers
5c635c09b4 Replay int overflows, fixed again (#4878)
* Reapply "Fix replay int overflow issues." (#4802)

This reverts commit 32049e34f2.

* IConfigurationManager.LoadDefaultsFromTomlStream now does required type conversions.

This fixes scenarios like loading of `long` CVars.
2024-01-31 17:17:28 +01:00
Pieter-Jan Briers
fad539212d Cache TotalTicks in MIDI renderer
This is the "total length" of the MIDI file, but it's not cached by FluidSynth and expensive to calculate.

This property was used in the render code (for local files only) and the instrument menu. Caching it drastically improves performance.
2024-01-31 00:29:03 +01:00
Pieter-Jan Briers
7275302639 Stop multithreading FluidSynth with synth.cpu-cores.
It makes no sense for our use case, and it caused FluidSynth to allocate a different thread pool *per* mixer. And every one of those threads have *high* priority. That's like *really* bad.

Furthermore, it was based on ParallelProcessCount which is currently bugged, and because of that we were always allocating  256 (!!!) real OS threads for a MIDI synthesizer. CHRIST. (fix for ParallelProcessCount is separate)

I assume this is responsible for a ton of people's MIDI lag, it just murdering their PC's CPU scheduler.

The ability to multithread FluidSynth still exists as a CVar but it'll default to 1 and I don't think it makes sense to ever change it.

Also there was code to dynamically change the parameter, as far as I could test this just always crashed the process so out it goes.
2024-01-31 00:27:30 +01:00
metalgearsloth
5057ff97a3 Add MaxDimension property to Box2 (#4871)
* Add MaxDimension property to Box2

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

* a

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-01-30 13:28:05 +01:00
metalgearsloth
754d5a1fbb Add GetLocalPosition to controls (#4872)
* 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.

* weh

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-01-30 13:27:28 +01:00
Kara
7cee5b67a7 More easings (#4876)
* More easings

* yeah
2024-01-30 13:24:16 +01:00
Kara
21729e7e48 Smarter EntitySystem sawmill name conversion (#4875) 2024-01-29 22:52:39 +01:00
metalgearsloth
394d1e6cc2 Add global rectangles for controls (#4873)
* 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.

* weh

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-01-29 11:08:02 +01:00
metalgearsloth
f73d7f7285 Add dotted line drawing to screen handle (#4874)
Probably needs anti-aliasing but idk an easy way to do it.
2024-01-29 11:07:15 +01:00
DrSmugleaf
e505cfffd8 Fix TileEdgeOverlay flickering with tiles that are barely out of view (#4868) 2024-01-29 14:13:19 +11:00
collinlunn
d0fe3591ef Fixes parsing of ShaderBlendMode in ShaderPrototype (#4867)
* Fixes parsing of ShaderBlendMode in ShaderPrototype

* Changes ShaderPrototype parser to use case insensitive overload
2024-01-28 12:20:06 -08:00
James Simonson
1868f32457 Exposed "Bottom Margin" to itemlist (#4862)
* Exposed "Bottom Margin" to itemlist

* Changed from ItemBottomMargin to ItemSeparation

* Update ItemList.cs
2024-01-27 17:26:37 +01:00
metalgearsloth
ca82767b07 Version: 208.0.0 2024-01-25 17:47:44 +11:00
Kevin Zheng
76024330a7 Do not crash failing to load user keybindings (#4844) 2024-01-25 17:44:34 +11:00
metalgearsloth
21e8107eb1 Don't serialize metadata flags (#4861)
* Don't serialize metadata flags

Probably fine, saves us saving it to map files.

* Release notes
2024-01-25 17:44:16 +11:00
metalgearsloth
bcaa97a79b Version: 207.1.0 2024-01-21 18:55:44 +11:00
metalgearsloth
19727f6a25 Localise merge_grids command (#4850) 2024-01-21 18:20:21 +11:00
metalgearsloth
2102b96323 Update uploadfile dependencies (#4849)
* Update uploadfile dependencies

Also removed the 1 warning in there.

* Thanks rider
2024-01-21 18:20:13 +11:00
metalgearsloth
59b3ffda4f Add grid merging (#3627)
* Add grid merging

* More preliminary cleanup

* Fixes

* Fixes

* weh

* tweaks

* Tests

* weh

* Fix direction test

* Release notes
2024-01-21 17:41:04 +11:00
metalgearsloth
aae929966c Add NotNullWhen true to EntMan HasComp (#4848)
The proxy methods already have it but not the interface itself.
2024-01-20 15:03:47 +11:00
metalgearsloth
0cf842cacc Version: 207.0.0 2024-01-19 13:30:24 +11:00
metalgearsloth
96885c5b53 Quick lookup fix (#4847)
* Quick lookup fix

Forgor to push, just makes the Box2Rotated query use polyshape so it's accurate.

* a
2024-01-19 13:28:29 +11:00
metalgearsloth
d45ce7742e Entitylookup approx / shape changes (#4842)
* Entitylookup approx / shape changes

- Make the shape queries respect the approx flag.
- Make everything use shape queries.
- Hopefully reduce some of the internal cruft.
- Add some new methods I need for trade station.

* Bunch of deduplication

* Remove some more duplication abuse

* Remove intersection duplication

* Bunch more cleanup

* MapManager rejig

* Fix some stuff

* More fixes

* Release notes

* Fix TryFindGrid

* Sensor check

* Fix query

* Fix map queries

* More cleanup

* Fix whatever this is.

* also dis

* Fix entity query

* Smol optimisations

* Also this
2024-01-19 13:02:22 +11:00
Kot
50e27fd204 Fix full state updates (#4833)
* Clear entities seen by a session when the full update is requested

* Disable PVS budged for the full update

It helps preventing weird intermediate states when a client observes
themselves in a void with all the alert notifications going wild.
However it introduces a spike on CPU and networking.

* Add changelog line

* Move the changelog line to the proper place

* Update RELEASE-NOTES.md

* Revert "Disable PVS budged for the full update"

This reverts commit 6976ca04b8.

* Update RELEASE-NOTES.md

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-01-18 17:40:55 +13:00
Pieter-Jan Briers
af98933173 Isolate net messages in integration tests. (#4838)
* Isolate net messages in integration tests.

Integration tests don't use Lidgren to connect client and send, instead they just use some in-process channels to communicate. Because of this, the original implementation of net messages *directly* passed the net message instances between client and server instances. This caused issues whenever content would mutate data in a NetMessage after it "passed through".

Now we run the messages through WriteToBuffer() and ReadFromBuffer() so they pass through binary serialization. This means there's no more implicit sharing of the objects.

Note that this requires some trickery: Lidgren's message types have internal constructors. Really ideally we'd change the engine to make this more testable... but that's a content breaking change. Instead I just added InternalsVisibleTo to Lidgren so we can mess with it. We maintain the library ourselves anyways I can do what I want.

Fixes #4836

* Register Robust.UnitTesting as assembly for reflection.

This is necessary so that serialized types in the assembly can be picked up by NetSerializer.

Have to disable automatic reflection on all entity systems/components that tests register manually right now, because otherwise tests break.

* Stop shallow cloning specific net messages in integration tests.

This isn't necessary anymore now that we have a thorough fix.

* Wow I really forgot to copy-paste that line to the other side huh.

* Add test that serializer hash matches.

* Another test one I missed earlier.

* Changelog
2024-01-16 21:04:39 +01:00
metalgearsloth
e357dada65 Version: 206.0.0 2024-01-14 19:13:14 +11:00
metalgearsloth
19c48862e2 Entitylookup tweaks (#4808)
* Entitylookup tweaks

- Removed a dupe internal method.
- Removed some dupe internal code.
- Renamed some methods in line with local vs non-local.

* Update release

* Also this
2024-01-14 19:10:33 +11:00
metalgearsloth
448ce94b35 Make MIDI update rate comically low (#4830)
Was around this before but apparently avoiding lock contention not hard enough.
2024-01-14 18:19:56 +11:00
metalgearsloth
3681b7f0d5 Cleanup and tweak teleport commands (#4835)
* Cleanup and tweak teleport commands

- Cleanup the code duplication.
- Teleports to grid-local center instead.

* Release notes.
2024-01-14 17:44:02 +11:00
DrSmugleaf
7b3c883653 Add support for forcing prototypes in a file or directory to be parsed as abstract (#4829)
* Add support for forcing prototypes in a file or directory to be parsed as abstract

* Simplify mapping add abstract true call

* Fix docs

* Address reviews
2024-01-13 03:51:36 -08:00
Pieter-Jan Briers
c81004ddb4 Skip serialization generator with SkipRobustAnalyzer
Skips it in GLFW and Lidgren.
2024-01-12 23:18:53 +01:00
Pieter-Jan Briers
f844011348 Version: 205.0.0 2024-01-12 23:08:42 +01:00
Pieter-Jan Briers
0094040d68 Dependency update / fixes / skrungle bungle (#4825)
* Move to Central Package Management.

Allows us to store NuGet package versions all in one place. Yay!

* Update NuGet packages and fix code for changes.

Notable:

Changes to ILVerify.
Npgsql doesn't need hacks for inet anymore, now we need hacks to make the old code work with this new reality.
NUnit's analyzers are already complaining and I didn't even update it to 4.x yet.
TerraFX changed to GetLastSystemError so error handling had to be changed.
Buncha APIs have more NRT annotations.

* Remove dotnet-eng NuGet package source.

I genuinely don't know what this was for, and Central Package Management starts throwing warnings about it, so YEET.

* Fix double loading of assemblies due to ALC shenanigans.

Due to how the "sideloading" code for the ModLoader was set up, it would first try to load Microsoft.Extensions.Primitives from next to the content dll. But we already have that library in Robust!

Chaos ensues.

We now try to forcibly prioritize loading from the default ALC first to avoid this.

* Remove Robust.Physics project.

Never used.

* Remove erroneous NVorbis reference.

Should be VorbisPizza and otherwise wasn't used.

* Sandbox fixes

* Remove unused unit test package references.

Castle.Core and NUnit.ConsoleRunner.

* Update NUnit to 4.0.1

This requires replacing all the old assertion methods because they removed them 🥲

* Mute CA1416 (platform check) errors

TerraFX started annotating APIs with this and I can't be arsed to entertain this analyzer so out it goes.

* Fine ya cranky, no more CPM for Robust.Client.Injectors

* Changelog

* Oh so that's what dotnet-eng was used for. Yeah ok that makes sense.

* Central package management for remaining 2 robust projects

* Ok that was a bad idea let's just use NUnit 3 on the analyzer test project

* Oh right forgot to remove this one

* Update to a newer version of RemoteExecutor

* Disable RemoteExecutor test

https://github.com/dotnet/arcade/issues/8483 Yeah this package is not well maintained and clearly we can't rely on it.

* Fix immutable list serialization
2024-01-12 22:59:52 +01:00
metalgearsloth
dfb5369664 Version: 204.1.0 2024-01-12 12:57:42 +11:00
Pieter-Jan Briers
2462c906b3 Remove Herobrine
Idk according to the changelog I already did this once but clearly I didn't so ???
2024-01-11 02:01:23 +01:00
metalgearsloth
8cbc05840f Remove some unnecessary GetEntityQuery<T> (#4823) 2024-01-10 18:31:32 +01:00
Pieter-Jan Briers
510846321d BUI Improvements / fixes (#4826)
* Extension helper function for registering BUI events filtered to UI key.

Usages of events like BoundUIClosedEvent frequently did not check the UI key or do it improperly. This has, and will continue to cause, bugs.

The new helper (accessible as Subs.BuiEvents() from an entity system) makes it easy to subscribe to BUI events while also filtering to the correct UI key.

Also added missing SubscribeLocalEvent overloads with new handler types to EntitySystem.Subscriptions.

* Sprinkle [ViewVariables] around BUI

* Avoid buggy behavior if a Bound UI is closed inside the `BoundUIOpenedEvent` that's opening it.
2024-01-10 18:30:57 +01:00
metalgearsloth
ac60567583 Version: 204.0.0 2024-01-09 21:52:36 +11:00
metalgearsloth
7592997f4e Add random pick / take methods for sets (#4821)
* Add random pick / take methods for sets

I want it don't at me.

* cleanup (mraow)

* Revert "cleanup (mraow)"

This reverts commit e279957f21.

* flatpak

* notes
2024-01-09 00:37:19 +11:00
Pieter-Jan Briers
c68b3dccb7 Fix server NRE in console command completions.
Happens if you just type "tp:to<space>" into the console.

Toolshed can fail to provide completion results, in which case a null propagates and possibly crashes the server.
2024-01-08 11:18:11 +01:00
Pieter-Jan Briers
da2a2ce4ff Remove "Do not use from content" public members. 2024-01-07 04:43:59 +01:00
Pieter-Jan Briers
538418ea93 Fix exception when VVing non-networked components
The VV code forcibly dirties all components, which trips an assert for non-networked components.
2024-01-07 04:22:38 +01:00
Kara
2bf284bce8 Infer VVAccess.ReadWrite for all datafields (#4442) 2024-01-06 18:27:58 -08:00
Pieter-Jan Briers
b51cb06d53 Changelog for #4818 2024-01-06 19:23:58 +01:00
Kot
bab6c29fbe Add TextEdit.OnTextChanged event (#4818) 2024-01-06 18:59:19 +01:00
Pieter-Jan Briers
9502c86a65 Fix GLFW clipboard sometimes returning null.
Fixes #4817
2024-01-06 18:52:28 +01:00
Pieter-Jan Briers
359811f71e Fix incorrect IPostInjectInit in DebugConsole
IPostInjectInit does not run through IoCManager.InjectDependencies(). This meant the logger was not initialized, and loading failures in the console history would crash the client startup due to an NRE while trying to log a warning.

See https://github.com/space-wizards/space-station-14/issues/23624
2024-01-06 15:47:55 +01:00
DrSmugleaf
9de5840017 Make IEntityManager.EntityNetManager not nullable, make EntityManager abstract (#4445)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-01-07 00:05:02 +11:00
metalgearsloth
45af00096f Version: 203.0.0 2024-01-06 18:43:23 +11:00
Leon Friedrich
2f0283edb7 Freeze event bus dictionaries (#4727)
* Make component factory use frozen collections

* Fix integration tests

* Also freeze _entTraitDict

* A

* I love integration test setup logic

* Re-add public method

* Freeze event bus

* Remove per-component dictionary lookup on EntAddComponent

* release notes + fix test jank

* Fix merge

* fix tests

* Shutdown
2024-01-06 13:50:19 +11:00
Vasilis
d142393221 Clamp volume gain calculations (#4815) 2024-01-06 13:48:04 +11:00
metalgearsloth
ac147dc2a1 Remove physics sleep cancelling (#4813)
* Remove physics sleep cancelling

Was a bad idea, in the future I'm going to add even more asserts around this but for now I can fix content.

* Fix this
2024-01-06 13:38:41 +11:00
Pieter-Jan Briers
16af5cff9c Delete FodyWeavers.xsd
We used to use Fody. We don't anymore. Yeet.
2024-01-05 12:42:25 +01:00
ElectroJr
7725dbff78 Version: 202.1.1 2024-01-04 23:24:07 -05:00
Leon Friedrich
f2f8824678 Fix pvs error (#4812)
* Fix pvs error

* rename variable
2024-01-05 15:17:44 +11:00
Leon Friedrich
f2e9ed0b73 NetSyncEnabled fixes (#4811)
* NetSyncEnabled fixes

* A
2024-01-05 15:11:09 +11:00
Leon Friedrich
9e6d0aa44a Fix state handling bugs (#4810)
* Fix container state bug

* Fix _pendingReapplyNetStates bug
2024-01-05 15:11:00 +11:00
Leon Friedrich
1758fced75 Revert some grid/map related changes (#4809) 2024-01-05 13:48:43 +11:00
metalgearsloth
31e2a3efe4 Version: 202.1.0 2024-01-04 15:18:07 +11:00
metalgearsloth
371db7db2f Add GetLocalEntitiesIntersecting for griduid + tilesize (#4807) 2024-01-04 15:05:18 +11:00
metalgearsloth
bb23bc6acc Version: 202.0.0 2024-01-04 13:44:56 +11:00
metalgearsloth
270326e188 Add IComponentState / struct support (#4610)
Still boxes but hey at least the struct dream is real.
2024-01-04 13:42:47 +11:00
Leon Friedrich
7d27835543 Use frozen collections in component factory and EntityManager._entTraitDict (#4726)
* Make component factory use frozen collections

* Fix integration tests

* Also freeze _entTraitDict

* A

* I love integration test setup logic

* Re-add public method

* changes

* Better notes

* And the new method

* Fix notes
2024-01-04 12:21:24 +11:00
Leon Friedrich
cfb88b2e8e Arch related engine changes (#4806) 2024-01-04 12:18:03 +11:00
ElectroJr
8a83787d58 Version: 201.0.0 2024-01-03 19:16:36 -05:00
deathride58
6dd9f9e0f5 Blindness hotfix (engine side) (#4804) 2024-01-04 11:13:14 +11:00
Leon Friedrich
7f3445b1c6 Try fix pvs null exception (#4805) 2024-01-04 11:07:42 +11:00
metalgearsloth
5c15f26f09 Version: 200.0.0 2024-01-03 17:29:49 +11:00
faint
b8fbe5e465 Compiler error for AutoNetworkedField fields in a component without a AutoGenerateComponentState attribute (#4670)
* yeah

* remove autonetfield

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-01-03 17:24:08 +11:00
metalgearsloth
32049e34f2 Revert "Fix replay int overflow issues." (#4802)
This reverts commit 9877323195.
2024-01-03 16:16:15 +11:00
Leon Friedrich
26b09283f3 Prevent /tpgrid from moving grids to nullspace (#4798)
* Prevent `/tpgrid` from moving grids to nullspace

* Undo breaking change

* a
2024-01-03 16:07:33 +11:00
Leon Friedrich
6c6360e50a Avoid calling DirtyEntity() when repeatedly dirtying the same component. (#4797) 2024-01-03 16:06:10 +11:00
Pieter-Jan Briers
9877323195 Fix replay int overflow issues.
This came up while we were configuring stuff for the 24h round.
2024-01-03 01:18:50 +01:00
Letter N
b6f52f4c27 Implement OSWindowStyles.* on linux (#4790)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-01-03 01:04:03 +01:00
Leon Friedrich
c1789cbbaf Make MappingDataNode ordered (#4800) 2024-01-03 10:22:44 +11:00
metalgearsloth
f44f5b5a98 Version: 199.0.0 2024-01-02 17:31:55 +11:00
Leon Friedrich
f4faa1ad3d Run grid traversal on entity spawn (#4796)
* Run grid traversal on entity spawn

* Add test

* Fix tests
2024-01-02 17:25:13 +11:00
Leon Friedrich
97d03c6954 VisMask and EntityLifeStage changes (#4794) 2024-01-02 11:43:08 +11:00
Leon Friedrich
8accbc700a PVS fixes & cleanup (#4793)
* PVS fixes & cleanup

* More cleanup

* Move `CacheGlobalOverrides()` onto the main thread
2024-01-01 01:06:47 -08:00
metalgearsloth
9f013534b3 Add Box2i union for Vector2i (#4777) 2024-01-01 00:00:47 -08:00
Leon Friedrich
0b8febf6a6 FlushEntity() and EntityManager event changes (#4791) 2024-01-01 11:04:11 +11:00
Leon Friedrich
188985ecc2 Try prevent some PVS NREs (#4792) 2024-01-01 11:03:50 +11:00
Leon Friedrich
42434d1f49 Process pvs-leave in parallel (#4787)
* Process pvs-leave in parallel

* oops

* Try fix eternal tests
2024-01-01 05:53:24 +11:00
deathride58
87492cb0c3 zLibrary expansion - Grayscale, noise, and circular gradient utilities (#4784) 2023-12-30 18:35:53 -08:00
metalgearsloth
517ae7f1bd Better audio logs for gain (#4789)
* Better audio logs for gain

Need to track down the -10 gain setter.

* Release checks
2023-12-31 13:34:43 +11:00
ElectroJr
73357f022b Version: 198.1.0 2023-12-30 14:32:05 -05:00
Leon Friedrich
012faa0a16 Don't log errors when attempting to delete invalid entities (#4786) 2023-12-31 06:24:33 +11:00
Letter N
7a8abb3db7 debug ui ui update (#4782)
* UI Debugger: Make it look better

* cleanup

- Use GetOrNew()
- Use typeof(Control)
- Don't check string prefixes
2023-12-31 06:24:06 +11:00
Leon Friedrich
d6ce7e950b Add per-session force send pvs override (#4781) 2023-12-31 05:26:56 +11:00
metalgearsloth
e39b249070 Add gaussian extension for System.Random (#4776) 2023-12-31 05:26:39 +11:00
Letter N
5b889936be add styleclass on menubar so it can be styled (#4780)
* add styleclass on this

* public const string and concistent naming
2023-12-31 05:22:40 +11:00
deathride58
712809195d Allows getting the light rendertarget from overlays (#4783) 2023-12-31 05:17:42 +11:00
Leon Friedrich
64b5d6e323 Pvs cleanup & expose methods for benchmarking (#4779)
* Pvs cleanup & expose methods for benchmarking

* Fix tests

* Minimalist PvsData
2023-12-30 13:23:29 +11:00
metalgearsloth
baa607532d Version: 198.0.1 2023-12-29 13:46:29 +11:00
metalgearsloth
ff064dd859 Fix DefaultMagicAczProvider on FULL_RELEASE (#4778) 2023-12-29 13:45:24 +11:00
metalgearsloth
8305bffcac Version: 198.0.0 2023-12-29 11:51:31 +11:00
metalgearsloth
17a8972052 Fix GetEntitiesIntersecting for maps (#4764)
* Fix GetEntitiesIntersecting for maps

Still valid to call it in this way. Also added the collections versions because muh allocs.

* Add local ones too
2023-12-28 16:16:57 -08:00
metalgearsloth
56e03eae3e Add UI click sound support (#4705)
* Add UI click sound support

* Audio option

* Add Cvar

* Note and fix disabled sounds

* Fix double sound

* Comment removal

* ou

---------

Co-authored-by: Kara <lunarautomaton6@gmail.com>
2023-12-28 16:15:42 -08:00
Pieter-Jan Briers
1f948e17c4 Disable automatic UI scaling for non-main windows by default
This causes tons of problems with OpenDream's small popup windows, and even for SS14 it doesn't make much sense.

The behavior can be re-enabled per window by setting WindowRoot.DisableAutoScaling to false.
2023-12-28 03:38:20 +01:00
Pieter-Jan Briers
efaed42b24 DefaultMagicAczProvider refuses to work on FULL_RELEASE.
It's intended for development only so having it run on a FULL_RELEASE is probably a bug.
2023-12-28 02:31:34 +01:00
Pieter-Jan Briers
be2e31ff9d Print additional runtime diagnostics on server startup.
Brought to you by "I went insane debugging something stupid my own fault for 30 minutes"
2023-12-28 02:31:34 +01:00
ElectroJr
a891cacae5 Version: 197.1.0 2023-12-27 20:26:57 -05:00
Leon Friedrich
e9e0117402 Fix pvs bug (#4775)
* Fix pvs bug

* Moar asserts
2023-12-28 12:24:41 +11:00
Pieter-Jan Briers
24114d87e6 Add Full Hybrid ACZ
This is Hybrid ACZ (Content.Client.zip in server package) but with the ability to add extra files on top (OpenDream rsc)

Also added IStatusHost.InvalidateAcz().
2023-12-28 01:37:57 +01:00
Leon Friedrich
7cd78f3f4e Fix sprite animation bug (#4774)
* Fix sprite animation bug

* CL
2023-12-28 11:13:25 +11:00
ElectroJr
ca64aae7f0 Version: 197.0.0 2023-12-27 17:29:01 -05:00
ElectroJr
be0fb4250c Replace scale with override 2023-12-27 17:22:07 -05:00
Leon Friedrich
eba58cb893 Remove RobustTree & PVSCollection (#4759)
* Cut down RobustTree

* Better LoD

* Add PvsPriority flag

* Undo cvar name change

* reorganize grafana metrics

* Fix tests

* Fix replays

* Don't try process empty chunks

* Fix move benchmark

* Fix benchmark

* Remove obsolete audio methods

* Moar benchmarks

* Rename EntityData

* A

* B
2023-12-28 09:10:13 +11:00
ElectroJr
ced6d5a8b0 Version: 196.0.0 2023-12-27 16:58:33 -05:00
metalgearsloth
495671576e Add cycling for auto-animated states (#4763)
* Add cycling for auto-animated states

* Non-nullable and AdvanceFrameAnimation() tweak
2023-12-28 08:51:00 +11:00
Leon Friedrich
0611674915 Fix toolshed commands not working on reconnect (#4766)
* Fix toolshed commands not working on reconnect

* fix tests?

* space

* I love #if block errors
2023-12-28 08:48:33 +11:00
Leon Friedrich
e6ab61fe42 Prevent recursion in GridTraversalSystem (#4773) 2023-12-28 08:47:09 +11:00
Leon Friedrich
5232e61d38 Add multi-component Dirty() methods (#4770) 2023-12-28 08:20:06 +11:00
Leon Friedrich
a3e90aa04e Add NetEntity support to SpriteView (#4771) 2023-12-28 08:19:42 +11:00
Leon Friedrich
7a719d9f61 Stop toolshed from generating completions for non-toolshed commands (#4767)
* Stop generating toolshed completions for non-toolshed commands

* Better readability????

* Fix missing completions
2023-12-28 08:19:25 +11:00
Pieter-Jan Briers
61385b7c21 Fix WebView processes hanging around on unclean shutdown.
The processes now voluntarily exit themselves if the parent process exits.
2023-12-27 15:33:31 +01:00
Leon Friedrich
2503f9c4e0 Reduce Loc.GetString allocs (#4757)
* Reduce LocalizationManager allocs

* Make _fallbackCultures non-nullable

* Reduce allocs even more
2023-12-27 07:44:18 +11:00
ElectroJr
eb092e90ef Version: 195.0.1 2023-12-25 23:03:00 -05:00
Leon Friedrich
eb556c8728 Fix prototype manager enumeration exception (#4769) 2023-12-26 15:01:00 +11:00
ike709
2e398ded08 Fix audiostreams double-setting their source (#4768)
* Fix audiostreams double-setting their source

* loaded bool

* ok

---------

Co-authored-by: ike709 <ike709@github.com>
2023-12-26 14:51:27 +11:00
metalgearsloth
75cf15caa2 Fix placementmgr rotation (#4765)
If the entity is a self-deleting one this fixes it getting placed and immediately throwing.
2023-12-26 14:25:33 +11:00
ike709
8d7d6a26ba Fix playing audio streams (#4761)
* Fix playing audio streams

* mostly address review

---------

Co-authored-by: ike709 <ike709@github.com>
2023-12-25 18:36:24 -08:00
Pieter-Jan Briers
d593ffbb47 The small innocent change that broke the packaging workflow and surely there's nothing else going to break from this. 2023-12-25 18:18:53 +01:00
Pieter-Jan Briers
15b377dbd6 Version: 195.0.0 2023-12-25 18:00:13 +01:00
Leon Friedrich
d212479689 Don't defer adding joints (#4762) 2023-12-25 17:41:04 +11:00
Pieter-Jan Briers
2d3379d7f4 Bring CEF up to snuff (#4760) 2023-12-25 00:28:51 +01:00
Leon Friedrich
7b171b2212 Fix some physics bugs (#4746)
* Fix SetAwake()

* Remove bad debug assert

* EntityQuery & misc optimizations

* Remove bad(?) code, and add new test

* I love engine tests

* Add debug assert
2023-12-24 17:28:43 +11:00
metalgearsloth
7e8a5e199f Don't defer grid traversals (#4750)
* Don't defer grid traversals

Transform is in a slightly better state so we can remove this hacky thing.

It got to the point it was doing a bunch of unnecessary checks (no need to check container if you're just checking map / grid parent assuming no map / grid has containers).

The main concern now is: Does not deferring cause any issues, which it doesn't seem to (previously this mostly happened with transform state handling). On server-side the main worry is broadphasesystem seeing as this is being run manually to check for entities entering grids.

* Comment cleanup

* Obsolete MoveEvent .FromStateHandling

* Faster map position calculation

* Check Xform.GridUid instead of checking for `MapGridComponent`

---------

Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
2023-12-24 16:33:00 +11:00
faint
ae74a5d7d4 PrototypeAttribute name parameter is optional now (#4672)
* PrototypeAttribute name parameter is optional now

* (^///^)

* suggestion

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

* add anther attribute ctor

* [Virtual] -> sealed
2023-12-23 17:36:44 +11:00
Leon Friedrich
9446ab76f9 Make integration tests fail when logging console errors (#4747)
* Make tests fail when logging console errors

* Add log convenience property
2023-12-23 13:53:23 +11:00
Kara
bb5cb10d57 Generic variants of AssertEqual methods (#4756)
* Generic variants of `AssertEqual` methods

* Remove invalid AssertionMethod
2023-12-23 13:52:08 +11:00
Pieter-Jan Briers
d6ccaee6d4 Version: 194.1.0 2023-12-22 22:25:26 +01:00
metalgearsloth
ce8982e371 Make audiostream methods public (#4752) 2023-12-22 21:14:08 +01:00
metalgearsloth
371cd7ddf1 Reduce robust mapped string allocs (#4754) 2023-12-22 17:08:07 +01:00
metalgearsloth
6573531d5d GetEntitiesInRange flag + TryDelta (#4749)
Needed for tesla too lazy to split
2023-12-22 14:17:46 +11:00
Leon Friedrich
b1e1f27aa3 Add stack trace to audio errors (#4748) 2023-12-22 13:23:28 +11:00
Jordan Dominion
80ce583454 Minor note about TGS using the watchdog API (#4742)
* Minor note about TGS using the watchdog API

* Move comment to new line

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

---------

Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
2023-12-22 09:20:37 +11:00
ElectroJr
2b4a428f9f Version: 194.0.2 2023-12-20 01:57:01 -05:00
Leon Friedrich
41ee330828 Add more PVS null checks (#4743) 2023-12-20 17:54:24 +11:00
ElectroJr
f82452c855 Version: 194.0.1 2023-12-19 19:33:05 -05:00
Leon Friedrich
785e2f84f6 Turn EntityData struct into a class (#4729)
* Turn EntityData struct into a class

* Stop overbudget dictionary removals

* Merge additional logs

* More logging
2023-12-20 11:32:14 +11:00
Leon Friedrich
2bdc1d77ca Add grid deletion/reparenting logs (#4741) 2023-12-20 11:24:40 +11:00
metalgearsloth
51205beb56 Fix audio occlusion (#4738)
Saw this a week ago but forgot to PR.
2023-12-20 11:17:30 +11:00
metalgearsloth
ef325b4780 Fix SetPositionInParent throwing for end position (#4740)
If a child is at the end spot this will ensure it doesn't throw.
2023-12-20 11:15:20 +11:00
ElectroJr
ede337a869 Version: 194.0.0 2023-12-18 20:29:41 -05:00
Kara
d416344aef MoveEvent broadcast -> C# event (#4732)
* MoveEvent broadcast -> C# event

* Broadcast MoveEvent -> C# event

* oops

* forgob

* release notes

* Update description of moveevent
2023-12-19 12:26:13 +11:00
Kara
fb98eb1a0c TreeRecursiveMoveEvent -> C# event (#4733)
* TreeRecursiveMoveEvent -> C# event

* entity query

* Make query protected
2023-12-19 12:19:19 +11:00
Leon Friedrich
ed9a0b4812 Fix freezing while loading ogg files (#4731)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2023-12-19 00:45:21 +01:00
ElectroJr
e7c6151310 Version: 193.2.0 2023-12-17 20:39:52 -05:00
Leon Friedrich
005c2e784a Add more PVS logs (#4730) 2023-12-18 12:36:48 +11:00
ElectroJr
1009dd3ea0 Version: 193.1.1 2023-12-16 23:29:50 -05:00
ElectroJr
87b82160b0 Fix #if FULL_RELEASE error 2023-12-16 23:28:58 -05:00
ElectroJr
fc318c9ecd Version: 193.1.0 2023-12-16 22:56:16 -05:00
Leon Friedrich
ac957ca7fc Try fix PVS bugs (#4728)
* Try fix PVS bugs

* change order
2023-12-17 14:55:46 +11:00
Leon Friedrich
1bdd82b0bf Raise ECS event on prototype-reload (#4724)
* Raise system event on prototype-reload

* Oops
2023-12-17 08:30:53 +11:00
Leon Friedrich
0150c5e6ff Add IPrototypeManager.GetInstances() (#4723) 2023-12-17 08:29:31 +11:00
Pieter-Jan Briers
b2cc90d00f Convert static readonly dictionaries to FrozenDictionary (#4722)
* Convert static readonly dictionaries to FrozenDictionary

* Also `IReadOnlyDictionary`
2023-12-17 04:47:01 +11:00
Leon Friedrich
1f8b89e92f Add frozen collections to sandbox and add yaml type serializers (#4721) 2023-12-16 18:36:58 +01:00
ElectroJr
33d394295e Version: 193.0.0 2023-12-16 11:42:37 -05:00
Leon Friedrich
4934a9c5a5 Combine PVS dictionaries (#4693)
* Try combine dictionaries

* Remove last-ack

* Remove lastSent

* Use structs

* Don't clear previous-sent on deletion

* Pass data into ProcessEntry

* Store visibility data in EntityData

* Turn ToSend into hashset

* Add EntityStringRepresentation constructors

* Add MetaDataComponent to EntityData

* Remove metadata cache

* Create GetStates partial class

* add ToSend partial class

* fix debug asserts

* Cleanup

* Rename partial class

* Try to optimize `ProcessLeavePvs`

* Add ProcessLeavePvsBenchmark

* Turn HashSet into List

* Collect expensive asserts into one block

* Revert "Add ProcessLeavePvsBenchmark"

This reverts commit 93d984e929.

* remove comment
2023-12-17 03:37:48 +11:00
Leon Friedrich
91ebc3eb02 Use FrozenDictionary for prototype instances (#4719)
* Use FrozenDictionary for prototype instances

* Add assert

* Change log to verbose

* RStopwatch.StartNew

* Rename TryGetKindInstances
2023-12-17 03:22:18 +11:00
Leon Friedrich
bc84590a33 Make ChildEnumerator out value non-nullable (#4707)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2023-12-17 03:10:20 +11:00
Kelrak
e3944dc6fb Should resolve some error spam in non EFX systems (#4704)
* Should resolve some error spam in non EFX systems

* Exit early if not supported
2023-12-16 15:57:59 +11:00
Leon Friedrich
6a77f4c27b Use frozen dictionaries for prototype kinds (#4718)
* Use frozen dictionaries for prototype kinds

* More logs

* AAAA

* Change log to verbose

* RStopwatch.StartNew()
2023-12-16 15:51:55 +11:00
Leon Friedrich
6246ae412e Make RobustMappedStringSerializer use frozen dictionaries (#4717)
* Make RobustMappedStringSerializer use frozen dictionaries

* Update RobustMappedStringSerializer.MappedStringDict.cs

* Change log to verbose

* RStopwatch.StartNew()
2023-12-16 15:48:57 +11:00
ElectroJr
ac86accc20 Version: 192.0.0 2023-12-15 23:43:53 -05:00
Leon Friedrich
19ff7f25ca Slightly speed up IDependencyCollection (#4716) 2023-12-16 00:23:12 +01:00
Pieter-Jan Briers
3f83733a03 Decode ogg files as shorts instead of floats (#4715) 2023-12-15 20:48:07 +01:00
Leon Friedrich
438fed2f0e Make TryGetEntity protected (#4713)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2023-12-15 19:40:43 +01:00
Pieter-Jan Briers
816a535a92 Fix obsoletion warnings for BinaryFormatter stuff
This became an outright warning in .NET 8. Into the trash, never used it.
2023-12-15 19:37:13 +01:00
Pieter-Jan Briers
01df42aa8f Fix ICommonSession.ConnectedClient obsoletion warnings 2023-12-15 19:34:30 +01:00
Pieter-Jan Briers
a200d73ef9 Use FrozenDictionary in DependencyCollection.
Did not test the perf of this at all but it's the perfect use case so...
2023-12-15 19:28:56 +01:00
metalgearsloth
8d30735ffb Run ProcessQueuedAcks asynchronously (#4696) 2023-12-15 19:10:09 +01:00
489 changed files with 16685 additions and 7329 deletions

View File

@@ -10,5 +10,8 @@ charset = utf-8
[*.{csproj,xml,yml,dll.config,targets,props}]
indent_size = 2
[nuget.config]
indent_size = 2
[*.gdsl]
indent_style = tab

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

74
Directory.Packages.props Normal file
View File

@@ -0,0 +1,74 @@
<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="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.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" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.8.0" />
<PackageVersion Include="Microsoft.CodeCoverage" Version="17.8.0" />
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.0" />
<PackageVersion Include="Microsoft.DotNet.RemoteExecutor" Version="8.0.0-beta.24059.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="8.0.0" />
<PackageVersion Include="Microsoft.ILVerification" Version="8.0.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageVersion Include="Moq" Version="4.20.70" />
<PackageVersion Include="NUnit" Version="4.0.1" />
<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="OpenTK.OpenAL" Version="4.7.7" />
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
<PackageVersion Include="Pidgin" Version="3.2.2" />
<PackageVersion Include="Robust.Natives" Version="0.1.1" />
<PackageVersion Include="Robust.Natives.Cef" Version="120.1.9" />
<PackageVersion Include="Robust.Shared.AuthLib" Version="0.1.2" />
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.7" />
<PackageVersion Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.7" />
<PackageVersion Include="Serilog" Version="3.1.1" />
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
<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

@@ -0,0 +1,9 @@
using System.Runtime.CompilerServices;
// So I wanted to mess with NetIncomingMessage and NetOutgoingMessage from tests.
// Now.. the instructors are internal...
// Unless...
// I mean we have this project here from the weird way we're compiling Lidgren.
// I could just put this in here... it wouldn't touch the main Lidgren repo at all...
[assembly: InternalsVisibleTo("Robust.UnitTesting")]

View File

@@ -16,6 +16,8 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="CursedHorrorsBeyondOurWildestImagination.cs" />
<Compile Include="Lidgren.Network\Lidgren.Network\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>

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

@@ -4,6 +4,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<Import Project="Robust.Custom.targets" Condition="Exists('Robust.Custom.targets')"/>
@@ -27,5 +28,5 @@
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
<!-- serialization generator -->
<Import Project="Robust.Serialization.Generator.targets" />
<Import Project="Robust.Serialization.Generator.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
</Project>

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

@@ -61,18 +61,5 @@ namespace OpenToolkit.GraphicsLibraryFramework
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GLFWException"/> class with the specified context
/// and the serialization information.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> associated with this exception.</param>
/// <param name="context">
/// A <see cref="StreamingContext"/> that represents the context of this exception.
/// </param>
protected GLFWException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -54,6 +54,728 @@ END TEMPLATE-->
*None yet*
## 214.2.2
## 214.2.1
## 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
* Fix missed import from 209.0.0.
## 209.0.0
### Breaking changes
* `replay.max_compressed_size` and `replay.max_uncompressed_size` CVars are now `long`.
* Remove obsolete CoordinatesExtension for ToEntityCoordinates from GridUid / Vector2i.
### New features
* Add GetEntitiesOnMap / GetChildEntities to EntityLookupSystem to return components on the specified map and components with the specified parent respectively.
* 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.
* `IConfigurationManager.LoadDefaultsFromTomlStream` properly does type conversions. This fixes scenarios like loading of `long` CVars.
* Add helper methods for TileRef / Vector2i to SharedMapSystem for ToCenterCoordinates (tile center EntityCoordinates) and ToCoordinates (tile origin to EntityCoordinates).
* Copy some of the coordinates extensions to SharedTransformSystem.
### Bugfixes
* Fixed integer overflows in replay max size calculation.
* Explicitly capped `replay.replay_tick_batchSize` internally to avoid high values causing allocation failures.
### Other
* Important MIDI performance improvements.
## 208.0.0
### Breaking changes
* Metadata flags are no longer serialized as they get rebuilt on entity startup.
### Bugfixes
* Log failing to load user keybinds and handle the exception.
## 207.1.0
### New features
* Add the ability to merge grids via GridFixtureSystem.
## 207.0.0
### Breaking changes
* Update EntityLookup internally so non-approximate queries use the GJK solver and are much more accurate. This also means the approximate flag matters much more if you don't need narrowphase checks.
* Add shape versions of queries for both EntityLookup and MapManager.
### Bugfixes
* Fix PVS full state updates not clearing session entities and causing exceptions.
### Other
* Integration tests now run `NetMessage`s through serialization rather than passing the objects between client and server. This causes tests that missed `[NetSerializer]` attributes on any objects that need them to fail.
### Internal
* Remove a lot of duplicate code internally from EntityLookup and MapManager.
## 206.0.0
### Breaking changes
* tpto will teleport you to physics-center instead of transform center instead.
* Rename local EntityLookup methods to reflect they take local AABBs and not world AABBs.
### New features
* Add some additional EntityLookup methods for local queries.
* Add support to PrototypeManager for parsing specific files / directories as abstract.
### Bugfixes
* Fix tpto short-circuiting if one of the listed entities isn't found.
* Fix tpto not allowing grids as targets.
### Other
* Reduce MIDI source update rate from 10hz to 4hz.
### Internal
* Remove some duplicate internal code in EntityLookupSystem.
* Skip serialization sourcegen in GLFW and Lidgren.
## 205.0.0
### Breaking changes
* The unused `Robust.Physics` project has been deleted.
* The project now uses [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management).
* (Almost) all the NuGet packages have been updated. This causes many problems. I am so sorry.
* Cleaned up some unused packages as well.
## 204.1.0
### New features
* New `EntitySystem` subscription helper for working with Bound User Interface events. You can find them by doing `Subs.BuiEvents<>()` in a system.
* The `EntityManager.Subscriptions` type (for building helper extension methods) now uses
### Bugfixes
* Avoid loading assemblies from content `/Assemblies` if Robust ships its own copy. This avoid duplicate or weird mismatching version issues.
### Other
* Removed glibc version check warning.
## 204.0.0
### Breaking changes
* Make EntityManager abstract and make IEntityManager.EntityNetManager not nullable.
* Make VVAccess.ReadWrite default for all Datafields instead of VVAccess.ReadOnly
### New features
* `TextEdit.OnTextChanged`
* Add Pick and PickAndTake versions for System.Random for ICollections.
### Bugfixes
* Fix `IClipboardManager.GetText()` returning null in some cases.
* Fix possible NRE in server-side console command completion code.
* Fix possible NRE on DebugConsole logs.
* Fix exception when VVing non-networked components.
### Other
* Remove "Do not use from content" from IComponent.
## 203.0.0
### Breaking changes
* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered.
* Event bus subscriptions are now locked after `IEntityManager` has started, instead of after the first component gets added. Any event subscriptions now need to happen before startup (but after init).
* Event bus subscriptions must now be locked before raising any events.
* Delete FodyWeavers.xsd as it hasn't been used for a long time.
* Remove physics sleep cancelling as it was, in hindsight, a bad idea.
### New features
* `RobustUnitTest` now has a `ExtraComponents` field for automatically registering additional components.
* `IComponentFactory.RegisterIgnore()` now accepts more than one string.
* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components.
### Bugfixes
* Clamp volume calculations for audio rather than throwing.
## 202.1.1
### Bugfixes
* Reverted some map/grid initialisation changes that might've been causing broadphase/physics errors.
* Fixed PVS sometimes sending entities without first sending their children.
* Fixed a container state handling bug caused by containers not removing expected entities when shutting down.
* Fixed a `EnsureEntity<T>` state handling bug caused by improper handling of entity deletions.
* Fixed a bad NetSyncEnabled debug assert.
## 202.1.0
### New features
* Add GetLocalEntitiesIntersecting overload that takes in a griduid and a Vector2i tile.
## 202.0.0
### Breaking changes
* Various entity manager methods now have a new `where T : IComponent` constraint.
* The `IComponentFactory.ComponentAdded` event has been renamed to `ComponentsAdded` and now provides an array of component registrations.
* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered.
### New features
* Added `IComponentFactory.GetAllRegistrations()`
* Add IComponentState interface support for component states so structs can be used in lieu of classes.
## 201.0.0
### Breaking changes
* The `zCircleGradient` shader function arguments have changed. It now requires a pixel-size to ensure that the gradient is properly entered.
### Bugfixes
* Fixed some PVS null reference errors.
## 200.0.0
### Breaking changes
* MappingDataNode is now ordered.
* Make invalid AutoNetworkedFields compiler errors.
### New features
* `OSWindowStyles.NoTitleBar` (supported only on Linux X11 for now).
### Bugfixes
* Avoid calling DirtyEntity when a component's last modified tick is not current.
* Fix `tpgrid` allowing moving grids to nullspace.
### Other
* `OSWindowStyles.NoTitleOptions` is now supported on Linux X11.
## 199.0.0
### Breaking changes
* Various `IEntityManager` C# events now use `Entity<MetadataComponent>` instead of `EntityUid`
* Entity visibility masks now use a ushort instead of an integer.
* Run grid traversal on entity spawn.
### New features
* Added two new `IEntityManager` C# events that get raiseed before and after deleting ("flushing") all entities.
* Added a new `DeleteEntity()` override that takes in the entity's metadata and transform components.
* Add better audio logs.
* Expand z-library shader.
* Add a Box2i union for Vector2i and add a Contains variant that assumes the Vector2i is a tile and not a point.
### Bugfixes
* Try to prevent some NREs in PVS.
* More PVS fixes and cleanup.
## 198.1.0
### New features
* `IClydeViewport` now provides access to the light render target.
* Added a style-class to the `MenuBar` popup control.
* Added `NextGaussian()` extension method for `System.Random`.
* Added per-session variant of `PvsOverrideSystem.AddForceSend()`.
### Bugfixes
* Stopped the client from logging errors when attempting to delete invalid entities.
### Other
* The `DevWindow` UI inspector has been improved a bit and it now groups properties by their defining type.
## 198.0.1
### Bugfixes
* Fix preprocessor flag for FULL_RELEASE preventing building.
## 198.0.0
### Breaking changes
* Disable DefaultMagicAczProvider for FULL_RELEASE as it's only meant for debugging.
### New features
* Automatic UI scale is disabled by default for non-main windows. If desired, it can be re-enabled per window by changing `WindowRoot.DisableAutoScaling`.
* Add UI click and hover sound support via IUserInterfaceManager.SetClickSound / .SetHoverSound
### Bugfixes
* Fix GetEntitiesIntersecting for map entities without grids.
### Other
* Print more diagnostics on server startup.
## 197.1.0
### New features
* ACZ improvements: `IStatusHost.InvalidateAcz()` and `IStatusHost.SetFullHybridAczProvider()`.
### Bugfixes
* Fixes a PVS bug that happens when grids moved across maps.
* Fixes sprite animations not working properly
## 197.0.0
### Breaking changes
* PvsOverrideSystem has been reworked:
* Session and global overrides now default to always being recursive (i.e., sending all children).
* Session & global overrides will always respect a client's PVS budgets.
* Entities with an override will now still be sent in the same way as other entities if they are within a player's view. If you want to prevent them from being sent, you need to use visibility masks.
* Entities can have more than one kind of override (i.e., multiple sessions).
### New features
* Added a `PvsSize ` field to `EyeComponent`, which can be used to modify the PVS range of an eye.
* Added a new `NetLowLodRange` cvar for reducing the number of distant entities that get sent to a player. If a PVS chunk is beyond this range (but still within PVS range), then only high-priority entities on that chunk will get sent.
* Added a new metadata flag for tagging an entity as a "high prority" entity that should get sent even on distant chunks. This only works for entities that are directly attached to a grid or map. This is currently used by lights & occluders.
### Other
* PVS has been reworked again, and should hopefully be noticeable faster.
* PVS now prioritizes sending chunks that are closer to a player's eyes.
## 196.0.0
### Breaking changes
* Dirtying a non-networked component will now fail a debug assert.
* The `IInvocationContext` interface for toolshed commands now requires a UserId field. The session field should be cleared if a player disconnects.
### New features
* `LocalizationManager` now supports multiple fallback cultures
* SpriteView now supports using a `NetEntity` to select an entity to draw.
* Added methods for simultaneously dirtying several components on the same entity.
* Animated sprite layers now have a "Cycle" option that will reverse an animation when it finishes.
### Bugfixes
* Fixed a recursion/stack-overflow in `GridTraversalSystem`
* Ensure `Robust.Client.WebView` processes get shut down if game process exits uncleanly.
* Fixed Toolshed commands not properly functioning after disconnecting and reconnecting.
### Other
* Console command completions no longer suggest toolshed commands for non-toolshed commands.
## 195.0.1
### Bugfixes
* Fixes playing audio using audio streams
* Fixes placement manager exceptions when placing self deleting / spawner entities
* Fixed `IPrototypeManager.EnumeratePrototypes<T>` throwing an exception when there are no instances.
## 195.0.0
### New features
* Generic versions of `DebugTools.AssertEquals()` functions.
* `[Prototype]` now does not need to have a name specified, the name is inferred from the class name.
### Bugfixes
* Fixes a physics bug that could cause deleted entities to remain on the physics map.
* Fixes a bug in entity lookup code that could cause clients to get stuck in an infinite loop.
### Other
* `Robust.Client.WebView` has been brought alive again.
* The addition of physics joints is no longer deferred to the next tick.
* Grid traversal is no longer deferred to the next tick.
* Integration tests now fail when console commands log errors.
## 194.1.0
### New features
* `IAudioManager` has APIs to directly load `AudioStream`s from data streams.
* `AudioSystem` has new `Play*` methods.
* `EntityCoordinates.TryDelta()`
* `EntityLookupSystem.GetEntitiesInRange()` untyped hashset overload has `flags` parameter.
## 194.0.2
### Internal
* Added some null-checks to PVS to try reduce the error spam.
## 194.0.1
### Bugfixes
* Fixed `Control.SetPositionInParent` failing to move an entity to the last position.
* Fixed audio occlusion not working.
### Internal
* Added some logs for grid/map deletion and movement to debug some map loading issues.
* Refactored some parts of PVS. It should be slightly faster, though the game may be unstable for a bit.
## 194.0.0
### Breaking changes
* MoveEvent is no longer raised broadcast, subscribe to the SharedTransformSystem.OnGlobalMoveEvent C# event instead
### Bugfixes
* Fixed the game sometimes freezing while trying to load specific audio files.
## 193.2.0
### Other
* Added more PVS error logs
## 193.1.1
### Bugfixes
* Fixed an exception when building in FULL_RELEASE
## 193.1.0
### New features
* Added FrozenDictionary and FrozenHashSet to sandbox whitelist
* Added yaml type serializers for FrozenDictionary and FrozenHashSet
* Added `IPrototypeManager.GetInstances<T>()`
* `IPrototypeManager` now also raises `PrototypesReloadedEventArgs` as a system event.
### Bugfixes
* Might fix some PVS bugs added in the last version.
### Internal
* Various static dictionaries have been converted into FrozenDictionary.
## 193.0.0
### Breaking changes
* The `TransformChildrenEnumerator`'s out values are now non-nullable
### New features
* Added `IPrototypeManager.TryGetInstances()`, which returns a dictionary of prototype instances for a given prototype kind/type.
### Bugfixes
* Fixed `BaseAudioSource.SetAuxiliary()` throwing errors on non-EFX systems
### Internal
* The internals of PVS system have been reworked to reduce the number of dictionary lookups.
* `RobustMappedStringSerializer` now uses frozen dictionaries
* `IPrototypeManager` now uses frozen dictionaries
## 192.0.0
### Breaking changes
* `EntitySystem.TryGetEntity` is now `protected`.
### Internal
* PVS message ack processing now happens asynchronously
* Dependency collections now use a `FrozenDictionary`
## 191.0.1
### Bugfixes

View File

@@ -9,6 +9,7 @@ cmd-parse-failure-float = {$arg} is not a valid float.
cmd-parse-failure-bool = {$arg} is not a valid bool.
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
cmd-parse-failure-grid = {$arg} is not a valid grid.
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
cmd-error-file-not-found = Could not find file: {$file}.
@@ -489,7 +490,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.
@@ -565,3 +566,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

@@ -0,0 +1,8 @@
cmd-merge_grids-desc = Combines 2 grids into 1 grid
cmd-merge_grids-help = merge_grids <gridUid1> <gridUid2> <offsetX> <offsetY> [angle]
cmd-merge_grids-hintA = Grid A
cmd-merge_grids-hintB = Grid B
cmd-merge_grids-xOffset = X offset
cmd-merge_grids-yOffset = Y offset
cmd-merge_grids-angle = [Angle]

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

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

@@ -1,24 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\MSBuild\Robust.Properties.targets" />
<Import Project="..\MSBuild\Robust.Engine.props" />
<PropertyGroup>
<SkipRobustAnalyzer>true</SkipRobustAnalyzer>
</PropertyGroup>
<Import Project="..\MSBuild\Robust.Properties.targets"/>
<Import Project="..\MSBuild\Robust.Engine.props"/>
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1"/>
<PackageReference Include="NUnit" Version="3.13.2"/>
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
<PackageReference Include="NUnit.Analyzers" Version="3.3.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageVersion Update="NUnit" Version="3.14.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzer.Testing"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces"/>
<PackageReference Include="NUnit"/>
<PackageReference Include="NUnit3TestAdapter"/>
<PackageReference Include="NUnit.Analyzers"/>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Robust.Analyzers\Robust.Analyzers.csproj"/>
<ProjectReference Include="..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" Aliases="SerializationGenerator" />
</ItemGroup>
</Project>

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,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
@@ -18,7 +19,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 +29,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 +39,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,7 +49,7 @@ 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,

View File

@@ -9,7 +9,7 @@ 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;

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

@@ -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,21 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<!-- Needed for NotNullableFlagAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" />
</ItemGroup>
<ItemGroup>
<!-- Needed for FriendAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" />
@@ -27,4 +16,10 @@
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" />
</ItemGroup>
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
<PropertyGroup>
<Nullable>disable</Nullable>
</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

@@ -1,15 +1,18 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Globalization;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Order;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;
using Robust.Benchmarks.Exporters;
@@ -44,10 +47,16 @@ public sealed class DefaultSQLConfig : IConfig
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => DefaultConfig.Instance.GetLogicalGroupRules();
public IEnumerable<EventProcessor> GetEventProcessors() => DefaultConfig.Instance.GetEventProcessors();
public IEnumerable<IColumnHidingRule> GetColumnHidingRules() => DefaultConfig.Instance.GetColumnHidingRules();
public IOrderer Orderer => DefaultConfig.Instance.Orderer!;
public ICategoryDiscoverer? CategoryDiscoverer => DefaultConfig.Instance.CategoryDiscoverer;
public SummaryStyle SummaryStyle => DefaultConfig.Instance.SummaryStyle;
public ConfigUnionRule UnionRule => DefaultConfig.Instance.UnionRule;
public string ArtifactsPath => DefaultConfig.Instance.ArtifactsPath;
public CultureInfo CultureInfo => DefaultConfig.Instance.CultureInfo!;
public ConfigOptions Options => DefaultConfig.Instance.Options;
public TimeSpan BuildTimeout => DefaultConfig.Instance.BuildTimeout;
public IReadOnlyList<Conclusion> ConfigAnalysisConclusion => DefaultConfig.Instance.ConfigAnalysisConclusion;
}

View File

@@ -13,12 +13,12 @@
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Properties.targets" />

View File

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Robust.Server.Containers;
using Robust.Server.GameStates;
@@ -11,7 +12,9 @@ using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.UnitTesting.Server;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.UnitTesting;
namespace Robust.Benchmarks.Transform;
@@ -19,114 +22,177 @@ namespace Robust.Benchmarks.Transform;
/// This benchmark tests various transform/move related functions with an entity that has many children.
/// </summary>
[Virtual, MemoryDiagnoser]
public class RecursiveMoveBenchmark
public class RecursiveMoveBenchmark : RobustIntegrationTest
{
private ISimulation _simulation = default!;
private IEntityManager _entMan = default!;
private SharedTransformSystem _transform = default!;
private ContainerSystem _container = default!;
private PvsSystem _pvs = default!;
private EntityCoordinates _mapCoords;
private EntityCoordinates _gridCoords;
private EntityCoordinates _gridCoords2;
private EntityUid _ent;
private EntityUid _child;
private TransformComponent _childXform = default!;
private EntityQuery<TransformComponent> _query;
private ICommonSession[] _players = default!;
private PvsSession _session = default!;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.InitializeInstance();
ProgramShared.PathOffset = "../../../../";
var server = StartServer();
var client = StartClient();
if (!_simulation.Resolve<IConfigurationManager>().GetCVar(CVars.NetPVS))
throw new InvalidOperationException("PVS must be enabled");
Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()).Wait();
var mapMan = server.ResolveDependency<IMapManager>();
_entMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
_entMan = _simulation.Resolve<IEntityManager>();
_transform = _entMan.System<SharedTransformSystem>();
_container = _entMan.System<ContainerSystem>();
_pvs = _entMan.System<PvsSystem>();
_query = _entMan.GetEntityQuery<TransformComponent>();
// Create map & grid
var mapMan = _simulation.Resolve<IMapManager>();
var mapSys = _entMan.System<SharedMapSystem>();
var mapId = mapMan.CreateMap();
var map = mapMan.GetMapEntityId(mapId);
var gridComp = mapMan.CreateGridEntity(mapId);
var grid = gridComp.Owner;
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
_mapCoords = new EntityCoordinates(map, 100, 100);
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
_ent = _entMan.Spawn();
var netMan = client.ResolveDependency<IClientNetManager>();
client.SetConnectTarget(server);
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
// Quick check that SetCoordinates actually changes the parent as expected
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
_transform.SetCoordinates(_ent, _gridCoords);
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
throw new Exception("Grid traversal error.");
_transform.SetCoordinates(_ent, _mapCoords);
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
throw new Exception("Grid traversal error.");
// Add 5 direct children in slots to represent clothing.
for (var i = 0; i < 5; i++)
for (int i = 0; i < 10; i++)
{
var id = $"inventory{i}";
_container.EnsureContainer<ContainerSlot>(_ent, id);
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
throw new Exception($"Failed to setup entity");
server.WaitRunTicks(1).Wait();
client.WaitRunTicks(1).Wait();
}
// body parts
_container.EnsureContainer<Container>(_ent, "body");
for (var i = 0; i < 5; i++)
// Ensure client & server ticks are synced.
// Client runs 1 tick ahead
{
// Simple organ
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
throw new Exception($"Failed to setup entity");
var sTick = (int)server.Timing.CurTick.Value;
var cTick = (int)client.Timing.CurTick.Value;
var delta = cTick - sTick;
// body part that has another body part / limb
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
throw new Exception($"Failed to setup entity");
if (delta > 1)
server.WaitRunTicks(delta - 1).Wait();
else if (delta < 1)
client.WaitRunTicks(1 - delta).Wait();
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
throw new Exception($"Failed to setup entity");
sTick = (int)server.Timing.CurTick.Value;
cTick = (int)client.Timing.CurTick.Value;
delta = cTick - sTick;
if (delta != 1)
throw new Exception("Failed setup");
}
// Backpack
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
throw new Exception($"Failed to setup entity");
// Misc backpack contents.
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
for (var i = 0; i < 10; i++)
// Set up map and spawn player
server.WaitPost(() =>
{
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
var mapId = mapMan.CreateMap();
var map = mapMan.GetMapEntityId(mapId);
var gridComp = mapMan.CreateGridEntity(mapId);
var grid = gridComp.Owner;
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
_gridCoords2 = new EntityCoordinates(grid, .5f, .6f);
_mapCoords = new EntityCoordinates(map, 100, 100);
var playerUid = _entMan.SpawnEntity(null, _mapCoords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, playerUid);
sPlayerMan.JoinGame(session);
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
_ent = _entMan.Spawn();
// Quick check that SetCoordinates actually changes the parent as expected
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
_transform.SetCoordinates(_ent, _gridCoords);
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
throw new Exception("Grid traversal error.");
_transform.SetCoordinates(_ent, _mapCoords);
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
throw new Exception("Grid traversal error.");
// Add 5 direct children in slots to represent clothing.
for (var i = 0; i < 5; i++)
{
var id = $"inventory{i}";
_container.EnsureContainer<ContainerSlot>(_ent, id);
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
throw new Exception($"Failed to setup entity");
}
// body parts
_container.EnsureContainer<Container>(_ent, "body");
for (var i = 0; i < 5; i++)
{
// Simple organ
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
throw new Exception($"Failed to setup entity");
// body part that has another body part / limb
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
throw new Exception($"Failed to setup entity");
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
throw new Exception($"Failed to setup entity");
}
// Backpack
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
throw new Exception($"Failed to setup entity");
// Misc backpack contents.
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
for (var i = 0; i < 10; i++)
{
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
throw new Exception($"Failed to setup entity");
}
// Emergency box inside of the backpack
var box = backpackStorage.ContainedEntities.First();
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
for (var i = 0; i < 10; i++)
{
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
throw new Exception($"Failed to setup entity");
}
// Deepest child.
_child = boxContainer.ContainedEntities.First();
_childXform = _query.GetComponent(_child);
_players = new[] {session};
_session = _pvs.PlayerData[session];
}).Wait();
for (int i = 0; i < 10; i++)
{
server.WaitRunTicks(1).Wait();
client.WaitRunTicks(1).Wait();
}
// Emergency box inside of the backpack
var box = backpackStorage.ContainedEntities.First();
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
for (var i = 0; i < 10; i++)
{
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
throw new Exception($"Failed to setup entity");
}
PvsTick();
PvsTick();
}
// Deepest child.
_child = boxContainer.ContainedEntities.First();
_childXform = _query.GetComponent(_child);
_pvs.ProcessCollections();
private void PvsTick()
{
_session.ClearState();
_pvs.CacheSessionData(_players);
_pvs.GetVisibleChunks();
_pvs.ProcessVisibleChunksSequential();
}
/// <summary>
@@ -140,6 +206,13 @@ public class RecursiveMoveBenchmark
_transform.SetCoordinates(_ent, _mapCoords);
}
[Benchmark]
public void MoveEntityASmidge()
{
_transform.SetCoordinates(_ent, _gridCoords);
_transform.SetCoordinates(_ent, _gridCoords2);
}
/// <summary>
/// Like <see cref="MoveEntity"/>, but also processes queued PVS chunk updates.
/// </summary>
@@ -147,9 +220,18 @@ public class RecursiveMoveBenchmark
public void MoveAndUpdateChunks()
{
_transform.SetCoordinates(_ent, _gridCoords);
_pvs.ProcessCollections();
PvsTick();
_transform.SetCoordinates(_ent, _mapCoords);
_pvs.ProcessCollections();
PvsTick();
}
[Benchmark]
public void MoveASmidgeAndUpdateChunk()
{
_transform.SetCoordinates(_ent, _gridCoords);
PvsTick();
_transform.SetCoordinates(_ent, _gridCoords2);
PvsTick();
}
[Benchmark]

View File

@@ -6,8 +6,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="17.0.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="Microsoft.Build.Framework" Version="17.8.3" />
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
<PackageReference Include="Pidgin" Version="2.5.0" />
</ItemGroup>

View File

@@ -1,18 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" 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

@@ -0,0 +1,85 @@
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef;
/// <summary>
/// Shared functionality for all <see cref="CefClient"/> implementations.
/// </summary>
/// <remarks>
/// Locks down a bunch of CEF functionality we absolutely do not need content to do right now.
/// </remarks>
internal abstract class BaseRobustCefClient : CefClient
{
private readonly RobustCefPrintHandler _printHandler = new();
private readonly RobustCefPermissionHandler _permissionHandler = new();
private readonly RobustCefDialogHandler _dialogHandler = new();
private readonly RobustCefDragHandler _dragHandler = new();
protected override CefPrintHandler GetPrintHandler() => _printHandler;
protected override CefPermissionHandler GetPermissionHandler() => _permissionHandler;
protected override CefDialogHandler GetDialogHandler() => _dialogHandler;
protected override CefDragHandler GetDragHandler() => _dragHandler;
private sealed class RobustCefPrintHandler : CefPrintHandler
{
protected override void OnPrintSettings(CefBrowser browser, CefPrintSettings settings, bool getDefaults)
{
}
protected override bool OnPrintDialog(CefBrowser browser, bool hasSelection, CefPrintDialogCallback callback)
{
return false;
}
protected override bool OnPrintJob(CefBrowser browser, string documentName, string pdfFilePath, CefPrintJobCallback callback)
{
return false;
}
protected override void OnPrintReset(CefBrowser browser)
{
}
}
private sealed class RobustCefPermissionHandler : CefPermissionHandler
{
protected override bool OnRequestMediaAccessPermission(
CefBrowser browser,
CefFrame frame,
string requestingOrigin,
CefMediaAccessPermissionTypes requestedPermissions,
CefMediaAccessCallback callback)
{
callback.Cancel();
return true;
}
}
private sealed class RobustCefDialogHandler : CefDialogHandler
{
protected override bool OnFileDialog(
CefBrowser browser,
CefFileDialogMode mode,
string title,
string defaultFilePath,
string[] acceptFilters,
CefFileDialogCallback callback)
{
callback.Cancel();
return true;
}
}
private sealed class RobustCefDragHandler : CefDragHandler
{
protected override bool OnDragEnter(CefBrowser browser, CefDragData dragData, CefDragOperationsMask mask)
{
return true;
}
protected override void OnDraggableRegionsChanged(CefBrowser browser, CefFrame frame, CefDraggableRegion[] regions)
{
}
}
}

View File

@@ -1,4 +1,7 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
@@ -19,6 +22,8 @@ namespace Robust.Client.WebView.Cef
var mainArgs = new CefMainArgs(argv);
StartWatchThread();
// This will block executing until the subprocess is shut down.
var code = CefRuntime.ExecuteProcess(mainArgs, new RobustCefApp(null), IntPtr.Zero);
@@ -29,5 +34,44 @@ namespace Robust.Client.WebView.Cef
return code;
}
private static void StartWatchThread()
{
//
// CEF has this nasty habit of not shutting down all its processes if the parent crashes.
// Great!
//
// We use a separate thread in each CEF child process to watch the main PID.
// If it exits, we kill ourselves after a couple seconds.
//
if (Environment.GetEnvironmentVariable("ROBUST_CEF_BROWSER_PROCESS_ID") is not { } parentIdString)
return;
if (Environment.GetEnvironmentVariable("ROBUST_CEF_BROWSER_PROCESS_MODULE") is not { } parentModuleString)
return;
if (!int.TryParse(parentIdString, CultureInfo.InvariantCulture, out var parentId))
return;
var process = Process.GetProcessById(parentId);
if ((process.MainModule?.FileName ?? "") != parentModuleString)
{
process.Dispose();
return;
}
new Thread(() => WatchThread(process)) { Name = "CEF Watch Thread", IsBackground = true }
.Start();
}
private static void WatchThread(Process p)
{
p.WaitForExit();
Thread.Sleep(3000);
Environment.Exit(1);
}
}
}

View File

@@ -76,12 +76,9 @@ namespace Robust.Client.WebView.Cef
return true;
}
protected override unsafe bool Read(IntPtr dataOut, int bytesToRead, out int bytesRead, CefResourceReadCallback callback)
protected override bool Read(Span<byte> response, out int bytesRead, CefResourceReadCallback callback)
{
var byteSpan = new Span<byte>((void*) dataOut, bytesToRead);
bytesRead = _stream.Read(byteSpan);
bytesRead = _stream.Read(response);
return bytesRead != 0;
}

View File

@@ -31,10 +31,12 @@ namespace Robust.Client.WebView.Cef
// Disable zygote on Linux.
commandLine.AppendSwitch("--no-zygote");
// Work around https://bitbucket.org/chromiumembedded/cef/issues/3213/ozone-egl-initialization-does-not-have
// Work around https://github.com/chromiumembedded/cef/issues/3213
// Desktop GL force makes Chromium not try to load its own ANGLE/Swiftshader so load paths aren't problematic.
if (OperatingSystem.IsLinux())
commandLine.AppendSwitch("--use-gl", "desktop");
// UPDATE: That bug got fixed and now this workaround breaks CEF.
// Keeping all this comment history in case I ever wanan remember what the `--use-gl` flag is.
//if (OperatingSystem.IsLinux())
// commandLine.AppendSwitch("--use-gl", "desktop");
// commandLine.AppendSwitch("--single-process");

View File

@@ -3,7 +3,7 @@ using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
{
// Simple CEF client.
internal sealed class RobustCefClient : CefClient
internal sealed class RobustCefClient : BaseRobustCefClient
{
private readonly CefRenderHandler _renderHandler;
private readonly CefRequestHandler _requestHandler;

View File

@@ -150,7 +150,7 @@ namespace Robust.Client.WebView.Cef
}
}
private sealed class WindowCefClient : CefClient
private sealed class WindowCefClient : BaseRobustCefClient
{
private readonly CefLifeSpanHandler _lifeSpanHandler;
private readonly CefRequestHandler _requestHandler;

View File

@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
@@ -85,6 +86,10 @@ namespace Robust.Client.WebView.Cef
_app = new RobustCefApp(_sawmill);
var process = Process.GetCurrentProcess();
Environment.SetEnvironmentVariable("ROBUST_CEF_BROWSER_PROCESS_ID", process.Id.ToString());
Environment.SetEnvironmentVariable("ROBUST_CEF_BROWSER_PROCESS_MODULE", process.MainModule?.FileName ?? "");
// So these arguments look like nonsense, but it turns out CEF is just *like that*.
// The first argument is literally nonsense, but it needs to be there as otherwise the second argument doesn't apply
// The second argument turns off CEF's bullshit error handling, which breaks dotnet's error handling.

View File

@@ -8,8 +8,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
<PackageReference Include="Robust.Natives.Cef" Version="102.0.9" />
<PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="Robust.Natives.Cef" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,28 @@
using System.Numerics;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Console;
namespace Robust.Client.WebView;
internal sealed class TestBrowseWindow : DefaultWindow
{
protected override Vector2 ContentsMinimumSize => new Vector2(640, 480);
public TestBrowseWindow()
{
var wv = new WebViewControl();
wv.Url = "https://spacestation14.io";
Contents.AddChild(wv);
}
}
internal sealed class TestBrowseWindowCommand : LocalizedCommands
{
public override string Command => "test_browse_window";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
new TestBrowseWindow().Open();
}
}

View File

@@ -5,6 +5,7 @@ using Robust.Client.WebView.Headless;
using Robust.Client.WebViewHook;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
[assembly: WebViewManagerImpl(typeof(WebViewManager))]
@@ -22,6 +23,9 @@ namespace Robust.Client.WebView
var cfg = dependencies.Resolve<IConfigurationManagerInternal>();
cfg.LoadCVarsFromAssembly(typeof(WebViewManager).Assembly);
var refl = dependencies.Resolve<IReflectionManager>();
refl.LoadAssemblies(typeof(WebViewManager).Assembly);
dependencies.RegisterInstance<IWebViewManager>(this);
dependencies.RegisterInstance<IWebViewManagerInternal>(this);

View File

@@ -97,11 +97,11 @@ internal partial class AudioManager
// check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM.
if (vorbis.Channels == 1)
{
format = ALFormat.MonoFloat32Ext;
format = ALFormat.Mono16;
}
else if (vorbis.Channels == 2)
{
format = ALFormat.StereoFloat32Ext;
format = ALFormat.Stereo16;
}
else
{
@@ -110,9 +110,9 @@ internal partial class AudioManager
unsafe
{
fixed (float* ptr = vorbis.Data.Span)
fixed (short* ptr = vorbis.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float),
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(short),
(int) vorbis.SampleRate);
}
}
@@ -281,7 +281,7 @@ internal partial class AudioManager
_bufferedAudioSources.Remove(handle);
}
IAudioSource? IAudioInternal.CreateAudioSource(AudioStream stream)
public IAudioSource? CreateAudioSource(AudioStream stream)
{
var source = AL.GenSource();

View File

@@ -148,7 +148,7 @@ internal sealed partial class AudioManager : IAudioInternal
var error = AL.GetError();
if (error != ALError.NoError)
{
OpenALSawmill.Error("[{0}:{1}] AL error: {2}, {3}", callerMember, callerLineNumber, error, message);
OpenALSawmill.Error("[{0}:{1}] AL error: {2}, {3}. Stacktrace is {4}", callerMember, callerLineNumber, error, message, Environment.StackTrace);
}
}

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;

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 MetaDataSystem _metadata = 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)
@@ -133,13 +136,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();
@@ -158,32 +154,57 @@ public sealed partial class AudioSystem : SharedAudioSystem
return;
}
// Source has already been set
if (component.Loaded)
{
return;
}
if (!TryGetAudio(component.FileName, out var audioResource))
{
Log.Error($"Error creating audio source for {audioResource}, can't find file {component.FileName}");
return;
}
var source = _audio.CreateAudioSource(audioResource);
SetupSource((uid, component), audioResource);
component.Loaded = true;
}
if (source == null)
private void SetupSource(Entity<AudioComponent> entity, AudioResource audioResource, TimeSpan? length = null)
{
var component = entity.Comp;
if (TryAudioLimit(component.FileName))
{
Log.Error($"Error creating audio source for {audioResource}");
DebugTools.Assert(false);
source = component.Source;
var newSource = _audio.CreateAudioSource(audioResource);
if (newSource == null)
{
Log.Error($"Error creating audio source for {audioResource}");
DebugTools.Assert(false);
}
else
{
component.Source = newSource;
}
}
component.Source = source;
if ((component.Flags & AudioFlags.GridAudio) != 0x0)
{
_metadata.SetFlag(entity.Owner, MetaDataFlags.Undetachable, true);
}
// Need to set all initial data for first frame.
ApplyAudioParams(component.Params, component);
source.Global = component.Global;
component.Source.Global = component.Global;
// Don't play until first frame so occlusion etc. are correct.
component.Gain = 0f;
length ??= GetAudioLength(component.FileName);
// If audio came into range then start playback at the correct position.
var offset = (Timing.CurTime - component.AudioStart).TotalSeconds % GetAudioLength(component.FileName).TotalSeconds;
var offset = (Timing.CurTime - component.AudioStart).TotalSeconds % length.Value.TotalSeconds;
if (offset > 0)
{
@@ -195,6 +216,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
{
// Breaks with prediction?
component.Source.Dispose();
RemoveAudioLimit(component.FileName);
}
private void OnAudioAttenuation(int obj)
@@ -312,7 +335,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
return;
}
var paramsGain = MathF.Pow(10, component.Params.Volume / 10);
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.
@@ -395,18 +418,13 @@ public sealed partial class AudioSystem : SharedAudioSystem
return false;
}
private bool TryCreateAudioSource(AudioStream stream, [NotNullWhen(true)] out IAudioSource? source)
private bool TryGetAudio(AudioStream stream, [NotNullWhen(true)] out AudioResource? audio)
{
if (!Timing.IsFirstTimePredicted)
{
source = null;
Log.Error($"Tried to create audio source outside of prediction!");
DebugTools.Assert(false);
return false;
}
if (_resourceCache.TryGetResource(stream, out audio))
return true;
source = _audio.CreateAudioSource(stream);
return source != null;
Log.Error($"Server failed to play audio stream {stream.Title}.");
return false;
}
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string filename, EntityCoordinates coordinates,
@@ -461,7 +479,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
/// </summary>
/// <param name="stream">The audio stream to play.</param>
/// <param name="audioParams"></param>
private (EntityUid Entity, AudioComponent Component)? PlayGlobal(AudioStream stream, AudioParams? audioParams = null)
public (EntityUid Entity, AudioComponent Component)? PlayGlobal(AudioStream stream, AudioParams? audioParams = null)
{
var (entity, component) = CreateAndStartPlayingStream(audioParams, stream);
component.Global = true;
@@ -496,7 +514,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
/// <param name="stream">The audio stream to play.</param>
/// <param name="entity">The entity "emitting" the audio.</param>
/// <param name="audioParams"></param>
private (EntityUid Entity, AudioComponent Component)? PlayEntity(AudioStream stream, EntityUid entity, AudioParams? audioParams = null)
public (EntityUid Entity, AudioComponent Component)? PlayEntity(AudioStream stream, EntityUid entity, AudioParams? audioParams = null)
{
if (TerminatingOrDeleted(entity))
{
@@ -537,7 +555,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
/// <param name="stream">The audio stream 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(AudioStream stream, EntityCoordinates coordinates, AudioParams? audioParams = null)
public (EntityUid Entity, AudioComponent Component)? PlayStatic(AudioStream stream, EntityCoordinates coordinates, AudioParams? audioParams = null)
{
if (TerminatingOrDeleted(coordinates.EntityId))
{
@@ -574,6 +592,16 @@ public sealed partial class AudioSystem : SharedAudioSystem
return PlayGlobal(filename, audioParams);
}
public override void LoadStream<T>(Entity<AudioComponent> entity, T stream)
{
if (stream is AudioStream audioStream)
{
TryGetAudio(audioStream, out var audio);
SetupSource(entity, audio!, audioStream.Length);
entity.Comp.Loaded = true;
}
}
/// <inheritdoc />
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null)
{
@@ -608,7 +636,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
{
var audioP = audioParams ?? AudioParams.Default;
var entity = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace);
var comp = SetupAudio(entity, stream.Name!, audioP);
var comp = SetupAudio(entity, null, audioP, stream.Length);
LoadStream((entity, comp), stream);
EntityManager.InitializeAndStartEntity(entity);
var source = comp.Source;

View File

@@ -21,8 +21,6 @@ internal interface IAudioInternal : IAudioManager
/// </summary>
void FlushALDisposeQueues();
IAudioSource? CreateAudioSource(AudioStream stream);
/// <summary>
/// Returns a buffered audio source.
/// </summary>
@@ -62,10 +60,4 @@ internal interface IAudioInternal : IAudioManager
/// Manually calculates the specified gain for an attenuation source with the specified distance.
/// </summary>
float GetAttenuationGain(float distance, float rolloffFactor, float referenceDistance, float maxDistance);
AudioStream LoadAudioOggVorbis(Stream stream, string? name = null);
AudioStream LoadAudioWav(Stream stream, string? name = null);
AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null);
}

View File

@@ -1,3 +1,8 @@
using System;
using System.IO;
using Robust.Client.Audio.Sources;
using Robust.Shared.Audio.Sources;
namespace Robust.Client.Audio;
/// <summary>
@@ -5,5 +10,13 @@ namespace Robust.Client.Audio;
/// </summary>
public interface IAudioManager
{
IAudioSource? CreateAudioSource(AudioStream stream);
AudioStream LoadAudioOggVorbis(Stream stream, string? name = null);
AudioStream LoadAudioWav(Stream stream, string? name = null);
AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null);
void SetMasterGain(float gain);
}

View File

@@ -72,7 +72,7 @@ internal sealed partial class MidiManager : IMidiManager
// To avoid lock contention until some kind of MIDI refactor.
private TimeSpan _nextUpdate;
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(0.1f);
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(0.25f);
private SemaphoreSlim _updateSemaphore = new(1);
@@ -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";
@@ -192,7 +192,12 @@ internal sealed partial class MidiManager : IMidiManager
_settings["synth.midi-bank-select"].StringValue = "gm";
//_settings["synth.verbose"].IntValue = 1; // Useful for debugging.
_parallel.AddAndInvokeParallelCountChanged(UpdateParallelCount);
var midiParallel = _cfgMan.GetCVar(CVars.MidiParallelism);
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(midiParallel) * 2048), 1, 65535);
_settings["synth.cpu-cores"].IntValue = Math.Clamp(midiParallel, 1, 256);
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
_midiSawmill.Debug($"Synth Polyphony: {_settings["synth.polyphony"].IntValue}");
}
catch (Exception e)
{
@@ -201,7 +206,10 @@ internal sealed partial class MidiManager : IMidiManager
return;
}
_midiThread = new Thread(ThreadUpdate);
_midiThread = new Thread(ThreadUpdate)
{
Name = "RobustToolbox MIDI Thread"
};
_midiThread.Start();
_updateJob = new MidiUpdateJob()
@@ -219,18 +227,6 @@ internal sealed partial class MidiManager : IMidiManager
FluidsynthInitialized = true;
}
private void UpdateParallelCount()
{
if (_settings == null)
return;
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(_parallel.ParallelProcessCount) * 2048), 1, 65535);
_settings["synth.cpu-cores"].IntValue = Math.Clamp(_parallel.ParallelProcessCount, 1, 256);
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
_midiSawmill.Debug($"Synth Polyphony: {_settings["synth.polyphony"].IntValue}");
}
private void LoggerDelegate(NFluidsynth.Logger.LogLevel level, string message, IntPtr data)
{
var rLevel = level switch

View File

@@ -38,6 +38,7 @@ internal sealed class MidiRenderer : IMidiRenderer
private readonly Synth _synth;
private readonly Sequencer _sequencer;
private NFluidsynth.Player? _player;
private int _playerTotalTicks;
private MidiDriver? _driver;
private byte _midiProgram = 1;
private byte _midiBank = 1;
@@ -144,7 +145,21 @@ internal sealed class MidiRenderer : IMidiRenderer
public bool DisableProgramChangeEvent { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public int PlayerTotalTick => _player?.GetTotalTicks ?? 0;
public int PlayerTotalTick
{
get
{
// GetTotalTicks is really expensive (has to iterate the entire file, not cached).
// Slight problem with caching it ourselves: the value only becomes available when the player loads the MIDI file.
// And that only happens after playback really starts, with the timer and synth and all that stuff.
// So we cache it "as soon as it's available", i.e. not 0.
// We don't care about playlists and such, so it shouldn't change anymore after.
if (_playerTotalTicks != 0)
return _playerTotalTicks;
return _playerTotalTicks = _player?.GetTotalTicks ?? 0;
}
}
[ViewVariables(VVAccess.ReadWrite)]
public int PlayerTick
@@ -339,6 +354,7 @@ internal sealed class MidiRenderer : IMidiRenderer
return false;
}
_playerTotalTicks = 0;
_player?.Dispose();
_player = new NFluidsynth.Player(_synth);
_player.SetPlaybackCallback(MidiPlayerEventHandler);
@@ -377,6 +393,7 @@ internal sealed class MidiRenderer : IMidiRenderer
_player?.Join();
_player?.Dispose();
_player = null;
_playerTotalTicks = 0;
}
StopAllNotes();

View File

@@ -10,7 +10,7 @@ using Robust.Shared.Maths;
namespace Robust.Client.Audio.Sources;
internal abstract class BaseAudioSource : IAudioSource
public abstract class BaseAudioSource : IAudioSource
{
/*
* This may look weird having all these methods here however
@@ -27,22 +27,25 @@ internal abstract class BaseAudioSource : IAudioSource
/// </summary>
protected int FilterHandle;
protected readonly AudioManager Master;
internal readonly AudioManager Master;
/// <summary>
/// Prior gain that was set.
/// </summary>
private float _gain;
private float _occlusion;
private bool IsEfxSupported => Master.IsEfxSupported;
protected BaseAudioSource(AudioManager master, int sourceHandle)
internal BaseAudioSource(AudioManager master, int sourceHandle)
{
Master = master;
SourceHandle = sourceHandle;
AL.GetSource(SourceHandle, ALSourcef.Gain, out _gain);
}
/// <inheritdoc />
public void Pause()
{
AL.SourcePause(SourceHandle);
@@ -66,6 +69,13 @@ internal abstract class BaseAudioSource : IAudioSource
Playing = false;
}
/// <inheritdoc />
public void Restart()
{
AL.SourceRewind(SourceHandle);
StartPlaying();
}
/// <inheritdoc />
public virtual bool Playing
{
@@ -270,13 +280,7 @@ internal abstract class BaseAudioSource : IAudioSource
/// <inheritdoc />
public float Occlusion
{
get
{
_checkDisposed();
AL.GetSource(SourceHandle, ALSourcef.MaxDistance, out var value);
Master._checkAlError();
return value;
}
get => _occlusion;
set
{
_checkDisposed();
@@ -291,6 +295,8 @@ internal abstract class BaseAudioSource : IAudioSource
gain *= gain * gain;
AL.Source(SourceHandle, ALSourcef.Gain, _gain * gain);
}
_occlusion = value;
Master._checkAlError();
}
}
@@ -340,9 +346,11 @@ internal abstract class BaseAudioSource : IAudioSource
}
}
public void SetAuxiliary(IAuxiliaryAudio? audio)
void IAudioSource.SetAuxiliary(IAuxiliaryAudio? audio)
{
_checkDisposed();
if (!IsEfxSupported)
return;
if (audio is AuxiliaryAudio impAudio)
{

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

View File

@@ -8,7 +8,7 @@ using Robust.Shared.Network.Messages;
namespace Robust.Client.Console;
internal sealed partial class ClientConsoleHost
internal partial class ClientConsoleHost
{
private readonly Dictionary<int, PendingCompletion> _completionsPending = new();
private int _completionSeq;

View File

@@ -47,7 +47,8 @@ namespace Robust.Client.Console
}
/// <inheritdoc cref="IClientConsoleHost" />
internal sealed partial class ClientConsoleHost : ConsoleHost, IClientConsoleHost, IConsoleHostInternal, IPostInjectInit
[Virtual]
internal partial class ClientConsoleHost : ConsoleHost, IClientConsoleHost, IConsoleHostInternal, IPostInjectInit
{
[Dependency] private readonly IClientConGroupController _conGroup = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
@@ -187,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);
@@ -199,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

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Text;
using System.Text.RegularExpressions;
@@ -494,9 +495,9 @@ namespace Robust.Client.Console.Commands
}
}
internal static List<(string, string)> PropertyValuesFor(Control control)
internal static List<MemberInfo> GetAllMembers(Control control)
{
var members = new List<(string, string)>();
var members = new List<MemberInfo>();
var type = control.GetType();
foreach (var fieldInfo in type.GetAllFields())
@@ -506,7 +507,7 @@ namespace Robust.Client.Console.Commands
continue;
}
members.Add((fieldInfo.Name, fieldInfo.GetValue(control)?.ToString() ?? "null"));
members.Add(fieldInfo);
}
foreach (var propertyInfo in type.GetAllProperties())
@@ -516,7 +517,19 @@ namespace Robust.Client.Console.Commands
continue;
}
members.Add((propertyInfo.Name, propertyInfo.GetValue(control)?.ToString() ?? "null"));
members.Add(propertyInfo);
}
return members;
}
internal static List<(string, string)> PropertyValuesFor(Control control)
{
var members = new List<(string, string)>();
foreach (var fieldInfo in GetAllMembers(control))
{
members.Add((fieldInfo.Name, fieldInfo.GetValue(control)?.ToString() ?? "null"));
}
foreach (var (attachedProperty, value) in control.AllAttachedProperties)
@@ -528,6 +541,35 @@ namespace Robust.Client.Console.Commands
members.Sort((a, b) => string.Compare(a.Item1, b.Item1, StringComparison.Ordinal));
return members;
}
internal static Dictionary<string, List<(string, string)>> PropertyValuesForInheritance(Control control)
{
var returnVal = new Dictionary<string, List<(string, string)>>();
var engine = typeof(Control).Assembly;
foreach (var member in GetAllMembers(control))
{
var type = member.DeclaringType!;
var cname = type.Assembly == engine ? type.Name : type.ToString();
if (type != typeof(Control))
cname = $"Control > {cname}";
returnVal.GetOrNew(cname).Add((member.Name, member.GetValue(control)?.ToString() ?? "null"));
}
foreach (var (attachedProperty, value) in control.AllAttachedProperties)
{
var cname = $"Attached > {attachedProperty.OwningType.Name}";
returnVal.GetOrNew(cname).Add((attachedProperty.Name, value?.ToString() ?? "null"));
}
foreach (var v in returnVal.Values)
{
v.Sort((a, b) => string.Compare(a.Item1, b.Item1, StringComparison.Ordinal));
}
return returnVal;
}
}
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

@@ -43,6 +43,7 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
GC.Collect();
Span<EntityUid> ents = stackalloc EntityUid[amount];
var stopwatch = new Stopwatch();
stopwatch.Start();
@@ -50,12 +51,17 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
for (var i = 0; i < amount; i++)
{
_entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
ents[i] = _entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
}
MeasureProfiler.SaveData();
shell.WriteLine($"Client: Profiled spawning {amount} entities in {stopwatch.Elapsed.TotalMilliseconds:N3} ms");
foreach (var ent in ents)
{
_entities.DeleteEntity(ent);
}
}
}
#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

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

View File

@@ -1,82 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="InlineIL" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="SequencePoints">
<xs:annotation>
<xs:documentation>Defines if sequence points should be generated for each emitted IL instruction. Default value: Debug</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Debug">
<xs:annotation>
<xs:documentation>Insert sequence points in Debug builds only (this is the default).</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Release">
<xs:annotation>
<xs:documentation>Insert sequence points in Release builds only.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="True">
<xs:annotation>
<xs:documentation>Always insert sequence points.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="False">
<xs:annotation>
<xs:documentation>Never insert sequence points.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="Warnings">
<xs:annotation>
<xs:documentation>Defines how warnings should be handled. Default value: Warnings</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Warnings">
<xs:annotation>
<xs:documentation>Emit build warnings (this is the default).</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Ignore">
<xs:annotation>
<xs:documentation>Do not emit warnings.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Errors">
<xs:annotation>
<xs:documentation>Treat warnings as errors.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -35,8 +35,6 @@ namespace Robust.Client
throw new InvalidOperationException("Cannot start twice!");
}
GlibcBug.Check();
_hasStarted = true;
if (CommandLineArgs.TryParse(args, out var parsed))
@@ -109,9 +107,15 @@ namespace Robust.Client
{
IsBackground = false,
Priority = priority,
Name = "Game thread",
Name = "Game thread"
};
if (OperatingSystem.IsWindows())
{
// Necessary for CEF to not complain when using CEF debug binaries.
_gameThread.SetApartmentState(ApartmentState.STA);
}
_gameThread.Start();
// Will block until game exit

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;
@@ -67,7 +68,7 @@ namespace Robust.Client.GameObjects
public override void QueueDeleteEntity(EntityUid? uid)
{
if (uid == null)
if (uid == null || uid == EntityUid.Invalid)
return;
if (IsClientSide(uid.Value))
@@ -98,29 +99,60 @@ namespace Robust.Client.GameObjects
base.Dirty(ent, meta);
}
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metaDataComponent = null)
/// <inheritdoc />
public override void Dirty<T1, T2>(Entity<T1, T2> ent, MetaDataComponent? meta = null)
{
if (_playerManager.LocalPlayer?.ControlledEntity == uid)
return base.ToPrettyString(uid) with { Session = _playerManager.LocalPlayer.Session };
if (_gameTiming.InPrediction)
base.Dirty(ent, meta);
}
return base.ToPrettyString(uid);
/// <inheritdoc />
public override void Dirty<T1, T2, T3>(Entity<T1, T2, T3> ent, MetaDataComponent? meta = null)
{
if (_gameTiming.InPrediction)
base.Dirty(ent, meta);
}
/// <inheritdoc />
public override void Dirty<T1, T2, T3, T4>(Entity<T1, T2, T3, T4> ent, MetaDataComponent? meta = null)
{
if (_gameTiming.InPrediction)
base.Dirty(ent, meta);
}
public override void RaisePredictiveEvent<T>(T msg)
{
var localPlayer = _playerManager.LocalPlayer;
DebugTools.AssertNotNull(localPlayer);
var session = _playerManager.LocalSession;
DebugTools.AssertNotNull(session);
var sequence = _stateMan.SystemMessageDispatched(msg);
EntityNetManager?.SendSystemNetworkMessage(msg, sequence);
DebugTools.Assert(!_stateMan.IsPredictionEnabled || _gameTiming.InPrediction && _gameTiming.IsFirstTimePredicted || _client.RunLevel != ClientRunLevel.Connected);
var eventArgs = new EntitySessionEventArgs(localPlayer!.Session);
var eventArgs = new EntitySessionEventArgs(session!);
EventBus.RaiseEvent(EventSource.Local, msg);
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;
@@ -208,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

@@ -18,11 +18,5 @@ namespace Robust.Client.GameObjects
public ComponentStateApplyException(string message, Exception inner) : base(message, inner)
{
}
protected ComponentStateApplyException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}
}

View File

@@ -319,6 +319,7 @@ namespace Robust.Client.GameObjects
Scale = Vector2.One,
Visible = true,
RenderingStrategy = LayerRenderingStrategy.UseSpriteStrategy,
Cycle = false,
});
state = null;
texture = null;
@@ -794,6 +795,7 @@ namespace Robust.Client.GameObjects
}
layer.RenderingStrategy = layerDatum.RenderingStrategy ?? layer.RenderingStrategy;
layer.Cycle = layerDatum.Cycle;
layer.Color = layerDatum.Color ?? layer.Color;
layer._rotation = layerDatum.Rotation ?? layer._rotation;
@@ -1515,6 +1517,19 @@ namespace Robust.Client.GameObjects
[ViewVariables] public float AnimationTime;
[ViewVariables] public int AnimationFrame;
/// <summary>
/// Is the animation currently playing in reverse.
/// </summary>
[ViewVariables] public bool Reversed { get; internal set; }
/// <summary>
/// If every animation delay finishes do we reverse it.
/// </summary>
/// <remarks>
/// Only applies if the state is auto-animated.
/// </remarks>
[ViewVariables] public bool Cycle;
private RSI.State? _actualState;
[ViewVariables] public RSI.State? ActualState => _actualState;
@@ -2033,15 +2048,49 @@ namespace Robust.Client.GameObjects
internal void AdvanceFrameAnimation(RSI.State state)
{
// Can't advance frames without more than 1 delay which is already checked above.
var delayCount = state.DelayCount;
while (AnimationTimeLeft < 0)
{
AnimationFrame += 1;
if (AnimationFrame >= delayCount)
if (Reversed)
{
AnimationFrame = 0;
AnimationTime = -AnimationTimeLeft;
AnimationFrame -= 1;
// Animation finished, do we cycle back to positive or reset.
if (AnimationFrame < 0)
{
if (Cycle)
{
AnimationFrame = 1;
Reversed = false;
}
else
{
AnimationFrame = delayCount - 1;
}
AnimationTime = -AnimationTimeLeft;
}
}
else
{
AnimationFrame += 1;
// Animation finished, do we reverse or reset.
if (AnimationFrame >= delayCount)
{
if (Cycle)
{
AnimationFrame = delayCount - 2;
Reversed = true;
}
else
{
AnimationFrame = 0;
}
AnimationTime = -AnimationTimeLeft;
}
}
AnimationTimeLeft += state.GetDelay(AnimationFrame);

View File

@@ -1,14 +1,11 @@
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Map.Enumerators;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using static Robust.Shared.GameObjects.OccluderComponent;
namespace Robust.Client.GameObjects;
@@ -30,17 +27,15 @@ internal sealed class ClientOccluderSystem : OccluderSystem
base.Initialize();
SubscribeLocalEvent<OccluderComponent, AnchorStateChangedEvent>(OnAnchorChanged);
SubscribeLocalEvent<OccluderComponent, ReAnchorEvent>(OnReAnchor);
SubscribeLocalEvent<OccluderComponent, ComponentShutdown>(OnShutdown);
}
public override void SetEnabled(EntityUid uid, bool enabled, OccluderComponent? comp = null)
public override void SetEnabled(EntityUid uid, bool enabled, OccluderComponent? comp = null, MetaDataComponent? meta = null)
{
if (!Resolve(uid, ref comp, false) || enabled == comp.Enabled)
return;
comp.Enabled = enabled;
Dirty(uid, comp);
base.SetEnabled(uid, enabled, comp, meta);
var xform = Transform(uid);
QueueTreeUpdate(uid, comp, xform);
@@ -94,11 +89,6 @@ internal sealed class ClientOccluderSystem : OccluderSystem
AnchorStateChanged(uid, comp, args.Transform);
}
private void OnReAnchor(EntityUid uid, OccluderComponent comp, ref ReAnchorEvent args)
{
AnchorStateChanged(uid, comp, args.Xform);
}
private void QueueOccludedDirectionUpdate(EntityUid sender, OccluderComponent occluder, TransformComponent? xform = null)
{
if (!Resolve(sender, ref xform))
@@ -178,8 +168,9 @@ internal sealed class ClientOccluderSystem : OccluderSystem
var tile = grid.TileIndicesFor(xform.Coordinates);
DebugTools.Assert(occluder.LastPosition == null
|| occluder.LastPosition.Value.Grid == xform.GridUid && occluder.LastPosition.Value.Tile == tile);
// TODO: Sub to parent changes instead or something.
// DebugTools.Assert(occluder.LastPosition == null
// || occluder.LastPosition.Value.Grid == xform.GridUid && occluder.LastPosition.Value.Tile == tile);
occluder.LastPosition = (xform.GridUid.Value, tile);
// dir starts at the relative effective south direction;

View File

@@ -54,14 +54,26 @@ namespace Robust.Client.GameObjects
DebugTools.Assert(ExpectedEntities.TryGetValue(netEntity, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(netEntity));
}
private void HandleEntityInitialized(EntityUid uid)
private void HandleEntityInitialized(Entity<MetaDataComponent> ent)
{
if (!RemoveExpectedEntity(GetNetEntity(uid), out var container))
var (uid, meta) = ent;
if (!RemoveExpectedEntity(meta.NetEntity, out var container))
return;
Insert((uid, TransformQuery.GetComponent(uid), MetaQuery.GetComponent(uid), null), container);
}
public override void ShutdownContainer(BaseContainer container)
{
foreach (var ent in container.ExpectedEntities)
{
if (ExpectedEntities.Remove(ent, out var c))
DebugTools.Assert(c == container);
}
base.ShutdownContainer(container);
}
private void HandleComponentState(EntityUid uid, ContainerManagerComponent component, ref ComponentHandleState args)
{
if (args.Current is not ContainerManagerComponentState cast)
@@ -324,32 +336,30 @@ namespace Robust.Client.GameObjects
if (_pointLightQuery.TryGetComponent(entity, out var light))
_lightSys.SetContainerOccluded(entity, lightOccluded, light);
var childEnumerator = xform.ChildEnumerator;
// Try to avoid TryComp if we already know stuff is occluded.
if ((!spriteOccluded || !lightOccluded) && TryComp<ContainerManagerComponent>(entity, out var manager))
{
while (childEnumerator.MoveNext(out var child))
foreach (var child in xform._children)
{
// Thank god it's by value and not by ref.
var childSpriteOccluded = spriteOccluded;
var childLightOccluded = lightOccluded;
// We already know either sprite or light is not occluding so need to check container.
if (manager.TryGetContainer(child.Value, out var container))
if (manager.TryGetContainer(child, out var container))
{
childSpriteOccluded = childSpriteOccluded || !container.ShowContents;
childLightOccluded = childLightOccluded || container.OccludesLight;
}
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), childSpriteOccluded, childLightOccluded);
UpdateEntity(child, TransformQuery.GetComponent(child), childSpriteOccluded, childLightOccluded);
}
}
else
{
while (childEnumerator.MoveNext(out var child))
foreach (var child in xform._children)
{
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), spriteOccluded, lightOccluded);
UpdateEntity(child, TransformQuery.GetComponent(child), spriteOccluded, lightOccluded);
}
}
}

View File

@@ -105,12 +105,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))
@@ -145,27 +143,22 @@ namespace Robust.Client.GameObjects
private void GenerateInputCommand(IConsoleShell shell, string argstr, string[] args)
{
var localPlayer = _playerManager.LocalPlayer;
if(localPlayer is null)
return;
var pent = localPlayer.ControlledEntity;
if(pent is null)
if (_playerManager.LocalEntity is not { } pent)
return;
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
var pxform = Transform(pent.Value);
var pxform = Transform(pent);
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 coords = EntityCoordinates.FromMap(EntityManager, pent, new MapCoordinates(wPos, pxform.MapID));
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);
HandleInputCommand(localPlayer.Session, keyFunction, message);
HandleInputCommand(_playerManager.LocalSession, keyFunction, message);
}
private void OnAttachedEntityChanged(LocalPlayerAttachedEvent message)
@@ -208,11 +201,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

@@ -68,6 +68,10 @@ namespace Robust.Client.GameObjects
return RemCompDeferred<PointLightComponent>(uid);
}
protected override void UpdatePriority(EntityUid uid, SharedPointLightComponent comp, MetaDataComponent meta)
{
}
private void HandleInit(EntityUid uid, PointLightComponent component, ComponentInit args)
{
SetMask(component.MaskPath, component);
@@ -95,28 +99,23 @@ namespace Robust.Client.GameObjects
_lightTree.QueueTreeUpdate(uid, clientComp);
}
public override void SetEnabled(EntityUid uid, bool enabled, SharedPointLightComponent? comp = null)
public override void SetEnabled(EntityUid uid, bool enabled, SharedPointLightComponent? comp = null, MetaDataComponent? meta = null)
{
if (!ResolveLight(uid, ref comp) || enabled == comp.Enabled || comp is not PointLightComponent clientComp)
return;
comp.Enabled = enabled;
RaiseLocalEvent(uid, new PointLightToggleEvent(comp.Enabled));
Dirty(uid, comp);
base.SetEnabled(uid, enabled, comp, meta);
if (!comp.ContainerOccluded)
_lightTree.QueueTreeUpdate(uid, clientComp);
}
public override void SetRadius(EntityUid uid, float radius, SharedPointLightComponent? comp = null)
public override void SetRadius(EntityUid uid, float radius, SharedPointLightComponent? comp = null, MetaDataComponent? meta = null)
{
if (!ResolveLight(uid, ref comp) || MathHelper.CloseToPercent(radius, comp.Radius) ||
comp is not PointLightComponent clientComp)
return;
comp.Radius = radius;
Dirty(uid, comp);
base.SetRadius(uid, radius, comp, meta);
if (clientComp.TreeUid != null)
_lightTree.QueueTreeUpdate(uid, clientComp);
}

View File

@@ -122,14 +122,13 @@ public sealed partial class SpriteSystem
return GetFallbackState();
}
private void OnPrototypesReloaded(PrototypesReloadedEventArgs protoReloaded)
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
{
// Check if any EntityPrototype has been changed.
if (!protoReloaded.ByType.TryGetValue(typeof(EntityPrototype), out var changedSet))
if (!args.TryGetModified<EntityPrototype>(out var modified))
return;
// Remove all changed prototypes from the cache, if they're there.
foreach (var (prototype, _) in changedSet.Modified)
foreach (var prototype in modified)
{
// Let's be lazy and not regenerate them until something needs them again.
_cachedPrototypeIcons.Remove(prototype);

View File

@@ -58,11 +58,11 @@ namespace Robust.Client.GameObjects
UpdatesAfter.Add(typeof(SpriteTreeSystem));
_proto.PrototypesReloaded += OnPrototypesReloaded;
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
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,13 +72,6 @@ namespace Robust.Client.GameObjects
QueueUpdateInert(uid, component);
}
public override void Shutdown()
{
base.Shutdown();
_proto.PrototypesReloaded -= OnPrototypesReloaded;
_cfg.UnsubValueChanged(CVars.RenderSpriteDirectionBias, OnBiasChanged);
}
private void OnBiasChanged(double value)
{
SpriteComponent.DirectionBias = value;

View File

@@ -29,10 +29,7 @@ namespace Robust.Client.GameObjects
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.Session = _playerManager.LocalSession!;
message.Entity = GetNetEntity(uid);
message.UiKey = uiKey;
@@ -75,8 +72,7 @@ namespace Robust.Client.GameObjects
boundInterface.Open();
uiComp.OpenInterfaces[uiKey] = boundInterface;
var playerSession = _playerManager.LocalPlayer?.Session;
if (playerSession != null)
if (_playerManager.LocalSession is { } playerSession)
{
uiComp.Interfaces[uiKey]._subscribedSessions.Add(playerSession);
RaiseLocalEvent(uid, new BoundUIOpenedEvent(uiKey, uid, playerSession), 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

@@ -43,7 +43,7 @@ public sealed class ClientDirtySystem : EntitySystem
return;
// Client-side entity deletion is not supported and will cause errors.
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity.Owner, ev.Entity.Comp)}. Trace: {Environment.StackTrace}");
}
private void OnCompRemoved(RemovedComponentEventArgs args)
@@ -71,9 +71,9 @@ public sealed class ClientDirtySystem : EntitySystem
RemovedComponents.Clear();
}
private void OnEntityDirty(EntityUid e)
private void OnEntityDirty(Entity<MetaDataComponent> e)
{
if (_timing.InPrediction && !IsClientSide(e))
if (_timing.InPrediction && !IsClientSide(e, e))
DirtyEntities.Add(e);
}
}

View File

@@ -49,16 +49,16 @@ namespace Robust.Client.GameStates
// Game state dictionaries that get used every tick.
private readonly Dictionary<EntityUid, (NetEntity NetEntity, MetaDataComponent Meta, bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
private readonly Dictionary<ushort, (IComponent Component, IComponentState? curState, IComponentState? nextState)> _compStateWork = new();
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
private readonly HashSet<NetEntity> _stateEnts = new();
private readonly List<EntityUid> _toDelete = new();
private readonly List<IComponent> _toRemove = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _outputData = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _outputData = new();
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
private readonly ObjectPool<Dictionary<ushort, ComponentState>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, ComponentState>>(new DictPolicy<ushort, ComponentState>(), 256);
private readonly ObjectPool<Dictionary<ushort, IComponentState>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, IComponentState>>(new DictPolicy<ushort, IComponentState>(), 256);
private uint _metaCompNetId;
@@ -232,9 +232,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 +257,7 @@ namespace Robust.Client.GameStates
public void UpdateFullRep(GameState state, bool cloneDelta = false)
=> _processor.UpdateFullRep(state, cloneDelta);
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
=> _processor.GetFullRep();
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
@@ -343,7 +343,7 @@ namespace Robust.Client.GameStates
}
// If we were waiting for a new state, we are now applying it.
if (_processor.WaitingForFull)
if (curState.FromSequence == GameTick.Zero)
{
_processor.OnFullStateReceived();
_timing.LastProcessedTick = curState.ToSequence;
@@ -351,7 +351,10 @@ namespace Robust.Client.GameStates
PartialStateReset(curState, true);
}
else
{
DebugTools.Assert(!_processor.WaitingForFull);
_timing.LastProcessedTick += 1;
}
_timing.CurTick = _timing.LastRealTick = _timing.LastProcessedTick;
@@ -532,7 +535,6 @@ namespace Robust.Client.GameStates
using var __ = _timing.StartStateApplicationArea();
// This is terrible, and I hate it. This also needs to run even when prediction is disabled.
_entitySystemManager.GetEntitySystem<SharedGridTraversalSystem>().QueuedEvents.Clear();
_entitySystemManager.GetEntitySystem<TransformSystem>().Reset();
if (!PredictionNeedsResetting)
@@ -777,15 +779,13 @@ namespace Robust.Client.GameStates
newMeta.LastStateApplied = curState.ToSequence;
// Check if there's any component states awaiting this entity.
if (_entityManager.PendingNetEntityStates.TryGetValue(es.NetEntity, out var value))
if (_entityManager.PendingNetEntityStates.Remove(es.NetEntity, out var value))
{
foreach (var (type, owner) in value)
{
var pending = _pendingReapplyNetStates.GetOrNew(owner);
pending.Add(type);
}
_entityManager.PendingNetEntityStates.Remove(es.NetEntity);
}
}
@@ -849,6 +849,17 @@ namespace Robust.Client.GameStates
if (!metas.TryGetComponent(uid, out var meta))
continue;
// It may also have been queued for deletion, in which case its last server state entry has already been removed.
// I love me some spaghetti order-of-operation dependent code
if (!_processor._lastStateFullRep.ContainsKey(meta.NetEntity))
{
DebugTools.Assert(curState.EntityDeletions.Value.Contains(meta.NetEntity));
continue;
}
DebugTools.Assert(!curState.EntityDeletions.Value.Contains(meta.NetEntity));
// State already being re-applied so don't bulldoze it.
ref var state = ref CollectionsMarshal.GetValueRefOrAddDefault(_toApply, uid, out var exists);
@@ -982,16 +993,15 @@ namespace Robust.Client.GameStates
xformSys.DetachParentToNull(ent, xform);
// Then detach all children.
var childEnumerator = xform.ChildEnumerator;
while (childEnumerator.MoveNext(out var child))
foreach (var child in xform._children)
{
xformSys.DetachParentToNull(child.Value, xforms.GetComponent(child.Value), xform);
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
if (deleteClientChildren
&& !deleteClientEntities // don't add duplicates
&& _entities.IsClientSide(child.Value))
&& _entities.IsClientSide(child))
{
_toDelete.Add(child.Value);
_toDelete.Add(child);
}
}
@@ -1039,7 +1049,7 @@ namespace Robust.Client.GameStates
var childEnumerator = xform.ChildEnumerator;
while (childEnumerator.MoveNext(out var child))
{
xformSys.DetachParentToNull(child.Value, xforms.GetComponent(child.Value), xform);
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
}
// Finally, delete the entity.
@@ -1110,7 +1120,7 @@ namespace Robust.Client.GameStates
continue;
}
if ((meta.Flags & MetaDataFlags.Detached) != 0)
if ((meta.Flags & (MetaDataFlags.Detached | MetaDataFlags.Undetachable)) != 0)
continue;
if (lastStateApplied.HasValue)

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, ComponentState>> _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, ComponentState>();
compData = new Dictionary<ushort, IComponentState>();
_lastStateFullRep.Add(entityState.NetEntity, compData);
}
@@ -391,7 +391,7 @@ Had full state: {LastFullState != null}"
LastFullStateRequested = null;
}
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> implicitData)
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> implicitData)
{
foreach (var (netEntity, implicitEntState) in implicitData)
{
@@ -425,18 +425,18 @@ Had full state: {LastFullState != null}"
}
}
public Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity netEntity)
public Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity netEntity)
{
return _lastStateFullRep[netEntity];
}
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
{
return _lastStateFullRep;
}
public bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? 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, ComponentState>> 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, ComponentState>> 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, ComponentState> 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, ComponentState>? 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

@@ -28,6 +28,8 @@ namespace Robust.Client.Graphics.Clyde
private int _verticesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * 4;
private int _indicesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
private List<Entity<MapGridComponent>> _grids = new();
private void _drawGrids(Viewport viewport, Box2 worldAABB, Box2Rotated worldBounds, IEye eye)
{
var mapId = eye.Position.MapId;
@@ -37,14 +39,15 @@ namespace Robust.Client.Graphics.Clyde
mapId = MapId.Nullspace;
}
var grids = new List<Entity<MapGridComponent>>();
_mapManager.FindGridsIntersecting(mapId, worldBounds, ref grids);
_grids.Clear();
_mapManager.FindGridsIntersecting(mapId, worldBounds, ref _grids);
var requiresFlush = true;
GLShaderProgram gridProgram = default!;
var gridOverlays = GetOverlaysForSpace(OverlaySpace.WorldSpaceGrids);
var mapSystem = _entityManager.System<SharedMapSystem>();
foreach (var mapGrid in grids)
foreach (var mapGrid in _grids)
{
if (!_mapChunkData.TryGetValue(mapGrid, out var data))
{
@@ -65,7 +68,7 @@ namespace Robust.Client.Graphics.Clyde
var transform = _entityManager.GetComponent<TransformComponent>(mapGrid);
gridProgram.SetUniform(UniIModelMatrix, transform.WorldMatrix);
var enumerator = mapGrid.Comp.GetMapChunks(worldBounds);
var enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
while (enumerator.MoveNext(out var chunk))
{

View File

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

@@ -198,6 +198,7 @@ namespace Robust.Client.Graphics.Clyde
}
IRenderTexture IClydeViewport.RenderTarget => RenderTarget;
IRenderTexture IClydeViewport.LightRenderTarget => LightRenderTarget;
public IEye? Eye { get; set; }
}
}

View File

@@ -224,8 +224,8 @@ namespace Robust.Client.Graphics.Clyde
fixed (char* pCaption = "RobustToolbox: Failed to create window")
{
Windows.MessageBoxW(HWND.NULL,
(ushort*) pText,
(ushort*) pCaption,
pText,
pCaption,
MB.MB_OK | MB.MB_ICONERROR);
}
}

View File

@@ -463,6 +463,9 @@ namespace Robust.Client.Graphics.Clyde
public IRenderTexture RenderTarget { get; } =
new DummyRenderTexture(Vector2i.One, new DummyTexture(Vector2i.One));
public IRenderTexture LightRenderTarget { get; } =
new DummyRenderTexture(Vector2i.One, new DummyTexture(Vector2i.One));
public IEye? Eye { get; set; }
public Vector2i Size { get; }
public Color? ClearColor { get; set; } = Color.Black;

View File

@@ -413,7 +413,7 @@ namespace Robust.Client.Graphics.Clyde
DXGI_ADAPTER_DESC1 desc;
ThrowIfFailed("GetDesc1", _adapter->GetDesc1(&desc));
var descName = new ReadOnlySpan<char>(desc.Description, 128).TrimEnd('\0');
var descName = ((ReadOnlySpan<char>)desc.Description).TrimEnd('\0');
Logger.DebugS("clyde.ogl.angle", "Successfully created D3D11 device!");
Logger.DebugS("clyde.ogl.angle", $"D3D11 Device Adapter: {descName.ToString()}");
@@ -493,7 +493,7 @@ namespace Robust.Client.Graphics.Clyde
DXGI_ADAPTER_DESC1 desc;
ThrowIfFailed("GetDesc1", adapter->GetDesc1(&desc));
var descName = new ReadOnlySpan<char>(desc.Description, 128);
var descName = ((ReadOnlySpan<char>)desc.Description);
if (descName.StartsWith(name))
return adapter;

View File

@@ -20,11 +20,5 @@ namespace Robust.Client.Graphics.Clyde
public ShaderCompilationException(string message, Exception inner) : base(message, inner)
{
}
protected ShaderCompilationException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}
}

View File

@@ -137,5 +137,70 @@ highp vec4 zTexture(highp vec2 uv)
return zTextureSpec(TEXTURE, uv);
}
// -- color --
// Grayscale function for the ITU's Rec BT-709. Primarily intended for HDTVs, but standard sRGB monitors are coincidentally extremely close.
highp float zGrayscale_BT709(highp vec3 col) {
return dot(col, vec3(0.2126, 0.7152, 0.0722));
}
// Grayscale function for the ITU's Rec BT-601, primarily intended for SDTV, but amazing for a handful of niche use-cases.
highp float zGrayscale_BT601(highp vec3 col) {
return dot(col, vec3(0.299, 0.587, 0.114));
}
// If you don't have any reason to be specifically using the above grayscale functions, then you should default to this.
highp float zGrayscale(highp vec3 col) {
return zGrayscale_BT709(col);
}
// -- noise --
//zRandom, zNoise, and zFBM are derived from https://godotshaders.com/snippet/2d-noise/ and https://godotshaders.com/snippet/fractal-brownian-motion-fbm/
highp vec2 zRandom(highp vec2 uv){
uv = vec2( dot(uv, vec2(127.1,311.7) ),
dot(uv, vec2(269.5,183.3) ) );
return -1.0 + 2.0 * fract(sin(uv) * 43758.5453123);
}
highp float zNoise(highp vec2 uv) {
highp vec2 uv_index = floor(uv);
highp vec2 uv_fract = fract(uv);
highp vec2 blur = smoothstep(0.0, 1.0, uv_fract);
return mix( mix( dot( zRandom(uv_index + vec2(0.0,0.0) ), uv_fract - vec2(0.0,0.0) ),
dot( zRandom(uv_index + vec2(1.0,0.0) ), uv_fract - vec2(1.0,0.0) ), blur.x),
mix( dot( zRandom(uv_index + vec2(0.0,1.0) ), uv_fract - vec2(0.0,1.0) ),
dot( zRandom(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) * 0.5 + 0.5;
}
highp float zFBM(highp vec2 uv) {
const int octaves = 6;
highp float amplitude = 0.5;
highp float frequency = 3.0;
highp float value = 0.0;
for(int i = 0; i < octaves; i++) {
value += amplitude * zNoise(frequency * uv);
amplitude *= 0.5;
frequency *= 2.0;
}
return value;
}
// -- generative --
// Function that creates a circular gradient. Screenspace shader bread n butter.
highp float zCircleGradient(highp vec2 ps, highp vec2 coord, highp float maxi, highp float radius, highp float dist, highp float power) {
highp float rad = (radius * ps.y) * 0.001;
highp float aspectratio = ps.x / ps.y;
highp vec2 totaldistance = ((ps * 0.5) - coord) / (rad * ps);
totaldistance.x *= aspectratio;
highp float length = (length(totaldistance) * ps.y) - dist;
return pow(clamp(length, 0.0, maxi), power);
}
// -- Utilities End --

View File

@@ -36,7 +36,7 @@ namespace Robust.Client.Graphics.Clyde
adapter->Release();
ThrowIfFailed("GetDesc", adapter3->GetDesc2(&desc));
var descString = new ReadOnlySpan<char>(desc.Description, 128).TrimEnd('\0');
var descString = ((ReadOnlySpan<char>)desc.Description).TrimEnd('\0');
shell.WriteLine(descString.ToString());
DXGI_QUERY_VIDEO_MEMORY_INFO memInfo;

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Robust.Shared;
@@ -65,7 +66,7 @@ namespace Robust.Client.Graphics.Clyde
return MouseButtonMap[button];
}
private static readonly Dictionary<GlfwButton, Button> MouseButtonMap = new()
private static readonly FrozenDictionary<GlfwButton, Button> MouseButtonMap = new Dictionary<GlfwButton, Button>()
{
{GlfwButton.Left, Button.Left},
{GlfwButton.Middle, Button.Middle},
@@ -75,10 +76,10 @@ namespace Robust.Client.Graphics.Clyde
{GlfwButton.Button6, Button.Button6},
{GlfwButton.Button7, Button.Button7},
{GlfwButton.Button8, Button.Button8},
};
}.ToFrozenDictionary();
private static readonly Dictionary<GlfwKey, Key> KeyMap;
private static readonly Dictionary<Key, GlfwKey> KeyMapReverse;
private static readonly FrozenDictionary<GlfwKey, Key> KeyMap;
private static readonly FrozenDictionary<Key, GlfwKey> KeyMapReverse;
internal static Key ConvertGlfwKey(GlfwKey key)
@@ -218,14 +219,16 @@ namespace Robust.Client.Graphics.Clyde
{GlfwKey.F24, Key.F24},
{GlfwKey.Pause, Key.Pause},
{GlfwKey.World1, Key.World1},
};
}.ToFrozenDictionary();
KeyMapReverse = new Dictionary<Key, GlfwKey>();
var keyMapReverse = new Dictionary<Key, GlfwKey>();
foreach (var (key, value) in KeyMap)
{
KeyMapReverse[value] = key;
keyMapReverse[value] = key;
}
KeyMapReverse = keyMapReverse.ToFrozenDictionary();
}
}
}

View File

@@ -10,8 +10,11 @@ using Robust.Shared.Maths;
using Robust.Shared.Utility;
using SixLabors.ImageSharp.PixelFormats;
using TerraFX.Interop.Windows;
using TerraFX.Interop.Xlib;
using GlfwImage = OpenToolkit.GraphicsLibraryFramework.Image;
using Monitor = OpenToolkit.GraphicsLibraryFramework.Monitor;
using Window = OpenToolkit.GraphicsLibraryFramework.Window;
using X11Window = TerraFX.Interop.Xlib.Window;
namespace Robust.Client.Graphics.Clyde
{
@@ -468,6 +471,11 @@ namespace Robust.Client.Graphics.Clyde
GLFW.MaximizeWindow(window);
}
if ((parameters.Styles & OSWindowStyles.NoTitleBar) != 0)
{
GLFW.WindowHint(WindowHintBool.Decorated, false);
}
if ((parameters.Styles & OSWindowStyles.NoTitleOptions) != 0)
{
if (OperatingSystem.IsWindows())
@@ -481,6 +489,35 @@ namespace Robust.Client.Graphics.Clyde
// Cast to long here to work around a bug in rider with nint bitwise operators.
(nint)((long)Windows.GetWindowLongPtrW(hWnd, GWL.GWL_STYLE) & ~WS.WS_SYSMENU));
}
else if (OperatingSystem.IsLinux())
{
try
{
var x11Window = (X11Window)GLFW.GetX11Window(window);
var x11Display = (Display*) GLFW.GetX11Display(window);
DebugTools.Assert(x11Window != X11Window.NULL);
// https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm46181547486832
var newPropValString = Marshal.StringToCoTaskMemUTF8("_NET_WM_WINDOW_TYPE_DIALOG");
var newPropVal = Xlib.XInternAtom(x11Display, (sbyte*)newPropValString, Xlib.False);
DebugTools.Assert(newPropVal != Atom.NULL);
var propNameString = Marshal.StringToCoTaskMemUTF8("_NET_WM_WINDOW_TYPE");
#pragma warning disable CA1806
// [display] [window] [property] [type] [format (8, 16,32)] [mode] [data] [element count]
Xlib.XChangeProperty(x11Display, x11Window,
Xlib.XInternAtom(x11Display, (sbyte*)propNameString, Xlib.False), // should never be null; part of spec
Xlib.XA_ATOM, 32, Xlib.PropModeReplace,
(byte*)&newPropVal, 1);
#pragma warning restore CA1806
Marshal.FreeCoTaskMem(newPropValString);
Marshal.FreeCoTaskMem(propNameString);
}
catch (EntryPointNotFoundException)
{
_sawmill.Warning("OSWindowStyles.NoTitleOptions not implemented on this windowing manager");
}
}
else
{
_sawmill.Warning("OSWindowStyles.NoTitleOptions not implemented on this platform");
@@ -500,6 +537,25 @@ namespace Robust.Client.Graphics.Clyde
GWLP.GWLP_HWNDPARENT,
ownerHWnd);
}
else if (OperatingSystem.IsLinux())
{
try
{
var x11Display = (Display*) GLFW.GetX11Display(window);
var thisWindow = (X11Window)GLFW.GetX11Window(window);
var parentWindow = (X11Window)GLFW.GetX11Window(ownerWindow);
DebugTools.Assert(thisWindow != X11Window.NULL);
DebugTools.Assert(parentWindow != X11Window.NULL);
#pragma warning disable CA1806
Xlib.XSetTransientForHint(x11Display, thisWindow, parentWindow);
#pragma warning restore CA1806
}
catch (EntryPointNotFoundException)
{
_sawmill.Warning("owner windows not implemented on this windowing manager");
}
}
else
{
_sawmill.Warning("owner windows not implemented on this platform");
@@ -598,7 +654,7 @@ namespace Robust.Client.Graphics.Clyde
private static void WinThreadGetClipboard(CmdGetClipboard cmd)
{
var clipboard = GLFW.GetClipboardString((Window*) cmd.Window);
var clipboard = GLFW.GetClipboardString((Window*) cmd.Window) ?? "";
// Don't have to care about synchronization I don't think so just fire this immediately.
cmd.Tcs.TrySetResult(clipboard);
}

View File

@@ -125,12 +125,6 @@ namespace Robust.Client.Graphics.Clyde
public GlfwException(string message, Exception inner) : base(message, inner)
{
}
protected GlfwException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using Robust.Client.Input;
@@ -16,7 +17,7 @@ internal partial class Clyde
{
// Indices are values of SDL_Scancode
private static readonly Key[] KeyMap;
private static readonly Dictionary<Key, SDL_Scancode> KeyMapReverse;
private static readonly FrozenDictionary<Key, SDL_Scancode> KeyMapReverse;
private static readonly Button[] MouseButtonMap;
// TODO: to avoid having to ask the windowing thread, key names are cached.
@@ -202,15 +203,17 @@ internal partial class Clyde
MapKey(SDL_SCANCODE_F24, Key.F24);
MapKey(SDL_SCANCODE_PAUSE, Key.Pause);
KeyMapReverse = new Dictionary<Key, SDL_Scancode>();
var keyMapReverse = new Dictionary<Key, SDL_Scancode>();
for (var code = 0; code < KeyMap.Length; code++)
{
var key = KeyMap[code];
if (key != Key.Unknown)
KeyMapReverse[key] = (SDL_Scancode) code;
keyMapReverse[key] = (SDL_Scancode) code;
}
KeyMapReverse = keyMapReverse.ToFrozenDictionary();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void MapKey(SDL_Scancode code, Key key)
{

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
@@ -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>
@@ -100,12 +114,43 @@ namespace Robust.Client.Graphics
DrawPrimitives(primitiveTopology, White, indices, drawVertices);
}
private void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> output, Color color)
private static void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> output, Color color)
{
Color colorLinear = Color.FromSrgb(color);
for (var i = 0; i < output.Length; i++)
if (input.Length == 0)
return;
if (input.Length != output.Length)
{
output[i] = new DrawVertexUV2DColor(input[i], new Vector2(0.5f, 0.5f), colorLinear);
throw new InvalidOperationException("Invalid lengths!");
}
var colorLinear = Color.FromSrgb(color);
var colorVec = Unsafe.As<Color, Vector128<float>>(ref colorLinear);
var uvVec = Vector128.Create(0, 0, 0.5f, 0.5f);
var maskVec = Vector128.Create(0xFFFFFFFF, 0xFFFFFFFF, 0, 0).AsSingle();
var simdVectors = (nuint)(input.Length / 2);
ref readonly var srcBase = ref Unsafe.As<Vector2, float>(ref Unsafe.AsRef(in input[0]));
ref var dstBase = ref Unsafe.As<DrawVertexUV2DColor, float>(ref output[0]);
for (nuint i = 0; i < simdVectors; i++)
{
var positions = Vector128.LoadUnsafe(in srcBase, i * 4);
var posColorLower = (positions & maskVec) | uvVec;
var posColorUpper = (Vector128.Shuffle(positions, Vector128.Create(2, 3, 0, 0)) & maskVec) | uvVec;
posColorLower.StoreUnsafe(ref dstBase, i * 16);
colorVec.StoreUnsafe(ref dstBase, i * 16 + 4);
posColorUpper.StoreUnsafe(ref dstBase, i * 16 + 8);
colorVec.StoreUnsafe(ref dstBase, i * 16 + 12);
}
var lastPos = (int)simdVectors * 2;
if (lastPos != output.Length)
{
// Odd number of vertices. Handle the last manually.
output[lastPos] = new DrawVertexUV2DColor(input[lastPos], new Vector2(0.5f, 0.5f), colorLinear);
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Numerics;
using System.Text;
using Robust.Client.GameObjects;
using Robust.Shared.Collections;
using Robust.Shared.GameObjects;
using Robust.Shared.Graphics;
using Robust.Shared.Maths;
@@ -14,6 +15,79 @@ namespace Robust.Client.Graphics
{
}
/// <summary>
/// Simialr to DrawLine but has dashes interspersed.
/// </summary>
/// <param name="offset">Offset from the start of the line.</param>
/// <param name="dashSize">How long a dash is.</param>
/// <param name="gapSize">How long the gap between dashes is.</param>
public void DrawDottedLine(Vector2 from, Vector2 to, Color color, float offset = 0f, float dashSize = 8f, float gapSize = 2f)
{
var lineVector = to - from;
// No drawing for you.
if (lineVector.LengthSquared() < 10f * float.Epsilon)
return;
var lineAndGap = gapSize + dashSize;
var lines = new ValueList<Vector2>();
// Minimum distance.
if (lineVector.Length() < lineAndGap)
{
lines.Add(from);
lines.Add(to);
}
else
{
var maxLength = lineVector.Length();
var normalizedLine = lineVector.Normalized();
var dashVector = normalizedLine * dashSize;
var gapVector = normalizedLine * gapSize;
var position = from;
offset %= (dashSize + gapSize);
var length = offset;
var dashLength = dashSize;
// If offset is less than gap size then start with a gap
// otherwise start with a partial line
if (offset > 0f)
{
if (offset < gapSize)
{
position += normalizedLine * offset;
length += offset;
}
else
{
dashLength = (offset - gapSize);
}
}
while (length < maxLength)
{
lines.Add(position);
position += normalizedLine * dashLength;
var lengthFromStart = (position - from).Length();
// if over length then cap the thing.
if (lengthFromStart > maxLength)
{
position = to;
}
lines.Add(position);
dashLength = dashVector.Length();
position += gapVector;
length = (position - from).Length();
}
}
DrawPrimitives(DrawPrimitiveTopology.LineList, lines.Span, color);
}
public abstract void DrawRect(UIBox2 rect, Color color, bool filled = true);
public abstract void DrawTextureRectRegion(Texture texture, UIBox2 rect, UIBox2? subRegion = null, Color? modulate = null);

View File

@@ -17,6 +17,7 @@ namespace Robust.Client.Graphics
/// The render target that is rendered to when rendering this viewport.
/// </summary>
IRenderTexture RenderTarget { get; }
IRenderTexture LightRenderTarget { get; }
IEye? Eye { get; set; }
Vector2i Size { get; }

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

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