Compare commits

..

143 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
289 changed files with 8905 additions and 2574 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

@@ -54,10 +54,379 @@ END TEMPLATE-->
*None yet*
## 203.0.2
## 214.2.2
## 203.0.1
## 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
@@ -73,8 +442,8 @@ END TEMPLATE-->
### 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.
* `IComponentFactory.RegisterIgnore()` now accepts more than one string.
* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components.
### Bugfixes
@@ -88,7 +457,7 @@ END TEMPLATE-->
* 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 `EnsureEntity<T>` state handling bug caused by improper handling of entity deletions.
* Fixed a bad NetSyncEnabled debug assert.
@@ -104,7 +473,7 @@ END TEMPLATE-->
### 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.
* 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

View File

@@ -490,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.
@@ -566,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

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

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

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();
@@ -170,26 +166,37 @@ public sealed partial class AudioSystem : SharedAudioSystem
return;
}
SetupSource(component, audioResource);
SetupSource((uid, component), audioResource);
component.Loaded = true;
}
private void SetupSource(AudioComponent component, AudioResource audioResource, TimeSpan? length = null)
private void SetupSource(Entity<AudioComponent> entity, AudioResource audioResource, TimeSpan? length = null)
{
var source = _audio.CreateAudioSource(audioResource);
if (source == 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;
@@ -209,6 +216,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
{
// Breaks with prediction?
component.Source.Dispose();
RemoveAudioLimit(component.FileName);
}
private void OnAudioAttenuation(int obj)
@@ -583,13 +592,13 @@ public sealed partial class AudioSystem : SharedAudioSystem
return PlayGlobal(filename, audioParams);
}
public override void LoadStream<T>(AudioComponent component, T stream)
public override void LoadStream<T>(Entity<AudioComponent> entity, T stream)
{
if (stream is AudioStream audioStream)
{
TryGetAudio(audioStream, out var audio);
SetupSource(component, audio!, audioStream.Length);
component.Loaded = true;
SetupSource(entity, audio!, audioStream.Length);
entity.Comp.Loaded = true;
}
}
@@ -628,7 +637,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
var audioP = audioParams ?? AudioParams.Default;
var entity = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace);
var comp = SetupAudio(entity, null, audioP, stream.Length);
LoadStream(comp, stream);
LoadStream((entity, comp), stream);
EntityManager.InitializeAndStartEntity(entity);
var source = comp.Source;

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

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

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

View File

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

View File

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

@@ -35,8 +35,6 @@ namespace Robust.Client
throw new InvalidOperationException("Cannot start twice!");
}
GlibcBug.Check();
_hasStarted = true;
if (CommandLineArgs.TryParse(args, out var parsed))

View File

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

View File

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

View File

@@ -27,7 +27,6 @@ internal sealed class ClientOccluderSystem : OccluderSystem
base.Initialize();
SubscribeLocalEvent<OccluderComponent, AnchorStateChangedEvent>(OnAnchorChanged);
SubscribeLocalEvent<OccluderComponent, ReAnchorEvent>(OnReAnchor);
SubscribeLocalEvent<OccluderComponent, ComponentShutdown>(OnShutdown);
}
@@ -90,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))
@@ -174,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

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

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

View File

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

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

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

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

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

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

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

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

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

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Numerics;
using Robust.Client.ResourceManagement;
@@ -69,7 +69,7 @@ namespace Robust.Client.Graphics
ShaderBlendMode? blend = null;
if (_rawBlendMode != null)
{
if (!Enum.TryParse<ShaderBlendMode>(_rawBlendMode.ToUpper(), out var parsed))
if (!Enum.TryParse<ShaderBlendMode>(_rawBlendMode, true, out var parsed))
Logger.Error($"invalid mode: {_rawBlendMode}");
else
blend = parsed;

View File

@@ -40,7 +40,7 @@ namespace Robust.Client.Input
void KeyDown(KeyEventArgs e);
void KeyUp(KeyEventArgs e);
IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified=true);
IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified=true, bool invalid=false);
void RemoveBinding(IKeyBinding binding, bool markModified=true);

View File

@@ -124,12 +124,19 @@ namespace Robust.Client.Input
var path = new ResPath(KeybindsPath);
if (_resourceMan.UserData.Exists(path))
{
LoadKeyFile(path, true);
try
{
LoadKeyFile(path, false, true);
}
catch (Exception e)
{
Logger.ErrorS("input", "Failed to load user keybindings: " + e);
}
}
if (_resourceMan.ContentFileExists(path))
{
LoadKeyFile(path, false);
LoadKeyFile(path, true);
}
}
@@ -489,7 +496,13 @@ namespace Robust.Client.Input
return true;
}
private void LoadKeyFile(ResPath file, bool userData)
/// <summary>
/// Loads a keybind file, configuring keybinds.
/// </summary>
/// <param name="file">File to load from the content package</param>
/// <param name="defaultRegistration">Whether or not this is a "default" keybind set. If it is, then it won't override the current configuration, only the defaults.</param>
/// <param name="userData">Whether or not to load from the user data directory instead of the content package.</param>
public void LoadKeyFile(ResPath file, bool defaultRegistration, bool userData = false)
{
TextReader reader;
if (userData)
@@ -510,16 +523,19 @@ namespace Robust.Client.Input
{
var baseKeyRegs = _serialization.Read<KeyBindingRegistration[]>(BaseKeyRegsNode, notNullableOverride: true);
foreach (var reg in baseKeyRegs)
{
var invalid = false;
if (reg.Type != KeyBindingType.Command && !NetworkBindMap.FunctionExists(reg.Function.FunctionName))
{
Logger.ErrorS("input", "Key function in {0} does not exist: '{1}'", file,
Logger.DebugS("input", "Key function in {0} does not exist: '{1}'.", file,
reg.Function);
continue;
invalid = true;
}
if (!userData)
if (defaultRegistration)
{
_defaultRegistrations.Add(reg);
@@ -531,19 +547,24 @@ namespace Robust.Client.Input
}
}
RegisterBinding(reg, markModified: userData);
RegisterBinding(reg, markModified: !defaultRegistration, invalid);
}
}
if (userData && mapping.TryGet("leaveEmpty", out var node))
if (!defaultRegistration && mapping.TryGet("leaveEmpty", out var node))
{
var leaveEmpty = _serialization.Read<BoundKeyFunction[]>(node, notNullableOverride: true);
if (leaveEmpty.Length > 0)
foreach (var bind in leaveEmpty)
{
// Adding to _modifiedKeyFunctions means that these keybinds won't be loaded from the base file.
// Because they've been explicitly cleared.
_modifiedKeyFunctions.UnionWith(leaveEmpty);
_modifiedKeyFunctions.Add(bind);
// Adding to bindingsByFunction because if the keybind is not valid(For example if it's from another
// server then we will have problems saving the file)
_bindingsByFunction.GetOrNew(bind);
}
}
}
@@ -571,7 +592,7 @@ namespace Robust.Client.Input
return binding;
}
public IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified = true)
public IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified = true, bool invalid = false)
{
var binding = new KeyBinding(this, reg.Function.FunctionName, reg.Type, reg.BaseKey, reg.CanFocus, reg.CanRepeat,
reg.AllowSubCombs, reg.Priority, reg.Mod1, reg.Mod2, reg.Mod3);
@@ -602,7 +623,7 @@ namespace Robust.Client.Input
public void InputModeChanged() => OnInputModeChanged?.Invoke();
private void RegisterBinding(KeyBinding binding, bool markModified = true)
private void RegisterBinding(KeyBinding binding, bool markModified = true, bool invalid = false)
{
// we sort larger combos first so they take priority over smaller (single key) combos,
// so they get processed first in KeyDown and such.
@@ -617,7 +638,8 @@ namespace Robust.Client.Input
_modifiedKeyFunctions.Add(binding.Function);
}
_bindings.Insert(pos, binding);
if (!invalid)
_bindings.Insert(pos, binding);
_bindingsByFunction.GetOrNew(binding.Function).Add(binding);
OnKeyBindingAdded?.Invoke(binding);
}

View File

@@ -37,7 +37,9 @@ public sealed class TileEdgeOverlay : GridOverlay
var tileDimensions = new Vector2(tileSize, tileSize);
var (_, _, worldMatrix, invMatrix) = xformSystem.GetWorldPositionRotationMatrixWithInv(Grid.Owner);
args.WorldHandle.SetTransform(worldMatrix);
var localAABB = invMatrix.TransformBox(args.WorldBounds);
var bounds = args.WorldBounds;
bounds = new Box2Rotated(bounds.Box.Enlarged(1), bounds.Rotation, bounds.Origin);
var localAABB = invMatrix.TransformBox(bounds);
var enumerator = mapSystem.GetLocalTilesEnumerator(Grid.Owner, Grid, localAABB, false);

View File

@@ -207,8 +207,8 @@ public sealed partial class PhysicsSystem
var contact = contacts[i];
var uidA = contact.EntityA;
var uidB = contact.EntityB;
var bodyATransform = GetPhysicsTransform(uidA, xformQuery.GetComponent(uidA), xformQuery);
var bodyBTransform = GetPhysicsTransform(uidB, xformQuery.GetComponent(uidB), xformQuery);
var bodyATransform = GetPhysicsTransform(uidA, xformQuery.GetComponent(uidA));
var bodyBTransform = GetPhysicsTransform(uidB, xformQuery.GetComponent(uidB));
contact.UpdateIsTouching(bodyATransform, bodyBTransform);
}

View File

@@ -495,7 +495,7 @@ namespace Robust.Client.Placement
{
// Try to get current map.
var map = MapId.Nullspace;
if (EntityManager.TryGetComponent(PlayerManager.LocalPlayer?.ControlledEntity, out TransformComponent? xform))
if (EntityManager.TryGetComponent(PlayerManager.LocalEntity, out TransformComponent? xform))
{
map = xform.MapID;
}
@@ -512,7 +512,7 @@ namespace Robust.Client.Placement
private bool CurrentEraserMouseCoordinates(out EntityCoordinates coordinates)
{
var ent = PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
var ent = PlayerManager.LocalEntity ?? EntityUid.Invalid;
if (ent == EntityUid.Invalid)
{
coordinates = new EntityCoordinates();
@@ -640,7 +640,7 @@ namespace Robust.Client.Placement
if (CurrentPermission is not {Range: > 0} ||
!CurrentMode.RangeRequired ||
PlayerManager.LocalPlayer?.ControlledEntity is not {Valid: true} controlled)
PlayerManager.LocalEntity is not {Valid: true} controlled)
return;
var worldPos = EntityManager.GetComponent<TransformComponent>(controlled).WorldPosition;

View File

@@ -216,7 +216,7 @@ namespace Robust.Client.Placement
{
if (!RangeRequired)
return true;
var controlled = pManager.PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
var controlled = pManager.PlayerManager.LocalEntity ?? EntityUid.Invalid;
if (controlled == EntityUid.Invalid)
{
return false;

View File

@@ -10,14 +10,13 @@ namespace Robust.Client.Player
public override Filter FromEntities(Filter filter, params EntityUid[] entities)
{
if (_playerManager.LocalPlayer is not { } localPlayer
|| localPlayer.Session.AttachedEntity is not {Valid: true} attachedUid)
if (_playerManager.LocalEntity is not {Valid: true} attachedUid)
return filter;
foreach (var uid in entities)
{
if (uid == attachedUid)
filter.AddPlayer(localPlayer.Session);
filter.AddPlayer(_playerManager.LocalSession!);
}
return filter;

View File

@@ -36,12 +36,12 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
private void OnRecordingStarted(MappingDataNode metadata, List<object> messages)
{
if (_player.LocalPlayer == null)
if (_player.LocalSession == null)
return;
// Add information about the user doing the recording. This is used to set the default replay observer position
// when playing back the replay.
var guid = _player.LocalPlayer.UserId.UserId.ToString();
var guid = _player.LocalUser.ToString();
metadata[ReplayConstants.MetaKeyRecordedBy] = new ValueDataNode(guid);
}

View File

@@ -5,33 +5,33 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>WinExe</OutputType>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<NoWarn>NU1701</NoWarn>
<NoWarn>NU1701;CA1416</NoWarn>
<OutputPath>../bin/Client</OutputPath>
<RobustILLink>true</RobustILLink>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.0.175" PrivateAssets="compile" />
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" PrivateAssets="compile" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.9" />
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />
<PackageReference Include="Robust.Natives" Version="0.1.1" />
<PackageReference Include="System.Numerics.Vectors" Version="4.4.0" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0" />
<PackageReference Include="TerraFX.Interop.Xlib" Version="6.4.0" />
<PackageReference Include="DiscordRichPresence" PrivateAssets="compile" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" PrivateAssets="compile" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.NFluidsynth" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" />
<PackageReference Include="OpenToolkit.Graphics" PrivateAssets="compile" />
<PackageReference Include="OpenTK.OpenAL" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.SharpFont" PrivateAssets="compile" />
<PackageReference Include="Robust.Natives" />
<PackageReference Include="System.Numerics.Vectors" />
<PackageReference Include="TerraFX.Interop.Windows" PrivateAssets="compile" />
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" PrivateAssets="compile" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" />
<PackageReference Include="TerraFX.Interop.Xlib" />
</ItemGroup>
<ItemGroup Condition="'$(EnableClientScripting)' == 'True'">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" PrivateAssets="compile" />
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
</ItemGroup>
@@ -40,7 +40,6 @@
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\OpenToolkit.GraphicsLibraryFramework\OpenToolkit.GraphicsLibraryFramework.csproj" />
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj" />
<ProjectReference Include="..\Robust.Physics\Robust.Physics.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
</ItemGroup>

View File

@@ -11,15 +11,17 @@ namespace Robust.Client.Upload.Commands;
public sealed class UploadFileCommand : IConsoleCommand
{
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
[Dependency] private readonly IFileDialogManager _dialog = default!;
[Dependency] private readonly INetManager _netManager = default!;
public string Command => "uploadfile";
public string Description => "Uploads a resource to the server.";
public string Help => $"{Command} [relative path for the resource]";
public async void Execute(IConsoleShell shell, string argStr, string[] args)
{
var cfgMan = IoCManager.Resolve<IConfigurationManager>();
if (!cfgMan.GetCVar(CVars.ResourceUploadingEnabled))
if (!_cfgManager.GetCVar(CVars.ResourceUploadingEnabled))
{
shell.WriteError("Network Resource Uploading is currently disabled by the server.");
return;
@@ -33,10 +35,8 @@ public sealed class UploadFileCommand : IConsoleCommand
var path = new ResPath(args[0]).ToRelativePath();
var dialog = IoCManager.Resolve<IFileDialogManager>();
var filters = new FileDialogFilters(new FileDialogFilters.Group(path.Extension));
await using var file = await dialog.OpenFile(filters);
await using var file = await _dialog.OpenFile(filters);
if (file == null)
{
@@ -44,7 +44,7 @@ public sealed class UploadFileCommand : IConsoleCommand
return;
}
var sizeLimit = cfgMan.GetCVar(CVars.ResourceUploadingLimitMb);
var sizeLimit = _cfgManager.GetCVar(CVars.ResourceUploadingLimitMb);
if (sizeLimit > 0f && file.Length * SharedNetworkResourceManager.BytesToMegabytes > sizeLimit)
{
@@ -54,12 +54,12 @@ public sealed class UploadFileCommand : IConsoleCommand
var data = file.CopyToArray();
var netManager = IoCManager.Resolve<INetManager>();
var msg = netManager.CreateNetMessage<NetworkResourceUploadMessage>();
var msg = new NetworkResourceUploadMessage
{
RelativePath = path,
Data = data
};
msg.RelativePath = path;
msg.Data = data;
netManager.ClientSendMessage(msg);
_netManager.ClientSendMessage(msg);
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.IO;
using Robust.Shared;
using Robust.Shared.Configuration;
@@ -13,21 +14,20 @@ namespace Robust.Client.Upload.Commands;
public sealed class UploadFolderCommand : IConsoleCommand
{
[Dependency] private IResourceManager _resourceManager = default!;
[Dependency] private IConfigurationManager _configManager = default!;
[Dependency] private INetManager _netMan = default!;
public string Command => "uploadfolder";
public string Description => Loc.GetString("uploadfolder-command-description");
public string Help => Loc.GetString("uploadfolder-command-help");
private static readonly ResPath BaseUploadFolderPath = new("/UploadFolder");
[Dependency] private IResourceManager _resourceManager = default!;
[Dependency] private IConfigurationManager _configManager = default!;
[Dependency] private INetManager _netMan = default!;
public async void Execute(IConsoleShell shell, string argStr, string[] args)
{
var fileCount = 0;
if (!_configManager.GetCVar(CVars.ResourceUploadingEnabled))
{
shell.WriteError( Loc.GetString("uploadfolder-command-resource-upload-disabled"));
@@ -52,8 +52,7 @@ public sealed class UploadFolderCommand : IConsoleCommand
//Grab all files in specified folder and upload them
foreach (var filepath in _resourceManager.UserData.Find($"{folderPath.ToRelativePath()}/").files )
{
await using var filestream = _resourceManager.UserData.Open(filepath,FileMode.Open);
await using var filestream = _resourceManager.UserData.Open(filepath, FileMode.Open);
{
var sizeLimit = _configManager.GetCVar(CVars.ResourceUploadingLimitMb);
if (sizeLimit > 0f && filestream.Length * SharedNetworkResourceManager.BytesToMegabytes > sizeLimit)
@@ -64,9 +63,11 @@ public sealed class UploadFolderCommand : IConsoleCommand
var data = filestream.CopyToArray();
var msg = _netMan.CreateNetMessage<NetworkResourceUploadMessage>();
msg.RelativePath = filepath.RelativeTo(BaseUploadFolderPath);
msg.Data = data;
var msg = new NetworkResourceUploadMessage
{
RelativePath = filepath.RelativeTo(BaseUploadFolderPath),
Data = data
};
_netMan.ClientSendMessage(msg);
fileCount++;

View File

@@ -226,6 +226,10 @@ namespace Robust.Client.UserInterface
/// <seealso cref="Rect"/>
public UIBox2i PixelRect => UIBox2i.FromDimensions(PixelPosition, PixelSize);
public UIBox2 GlobalRect => UIBox2.FromDimensions(GlobalPosition, _size);
public UIBox2i GlobalPixelRect => UIBox2i.FromDimensions(GlobalPixelPosition, PixelSize);
/// <summary>
/// Horizontal alignment mode.
/// This determines how the control should be laid out horizontally
@@ -464,6 +468,14 @@ namespace Robust.Client.UserInterface
}
}
/// <summary>
/// Gets the screen coordinates position relative to the control.
/// </summary>
public Vector2 GetLocalPosition(ScreenCoordinates coordinates)
{
return coordinates.Position - GlobalPixelPosition;
}
/// <summary>
/// Notify the layout system that this control's <see cref="Measure"/> result may have changed
/// and must be recalculated.

View File

@@ -545,6 +545,36 @@ namespace Robust.Client.UserInterface
Draw(renderHandle.DrawingHandleScreen);
}
protected internal virtual void PreRenderChildren(ref ControlRenderArguments args)
{
}
protected internal virtual void PostRenderChildren(ref ControlRenderArguments args)
{
}
protected internal virtual void RenderChildOverride(ref ControlRenderArguments args, int childIndex, Vector2i position)
{
RenderControl(ref args, childIndex, position);
}
public ref struct ControlRenderArguments
{
public IRenderHandle Handle;
public ref int Total;
public Vector2i Position;
public Color Modulate;
public UIBox2i? ScissorBox;
public ref Matrix3 CoordinateTransform;
}
protected void RenderControl(ref ControlRenderArguments args, int childIndex, Vector2i position)
{
UserInterfaceManagerInternal.RenderControl(args.Handle, ref args.Total, GetChild(childIndex), position, args.Modulate, args.ScissorBox, args.CoordinateTransform);
}
public void UpdateDraw()
{
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Client.Audio;
using Robust.Client.ResourceManagement;
using Robust.Shared.ContentPack;
@@ -37,8 +38,10 @@ namespace Robust.Client.UserInterface.Controls
get => _group;
set
{
if (value?.InternalButtons.Contains(this) ?? false)
return; // No work to do.
// Remove from old group.
_group?.Buttons.Remove(this);
_group?.InternalButtons.Remove(this);
_group = value;
@@ -47,11 +50,21 @@ namespace Robust.Client.UserInterface.Controls
return;
}
value.Buttons.Add(this);
value.InternalButtons.Add(this);
ToggleMode = true;
// Set us to pressed if we're the first button.
Pressed = value.Buttons.Count == 0;
if (value.IsNoneSetAllowed)
{
// Still UNPRESS if there's another pressed button, but don't PRESS it otherwise.
if (value.Pressed != this)
_pressed = false;
}
else
{
// Set us to pressed if we're the first button. Doesn't go through the setter to avoid setting off our own error check.
_pressed = value.InternalButtons.Count == 1;
}
DrawModeChanged();
}
}
@@ -95,7 +108,7 @@ namespace Robust.Client.UserInterface.Controls
return;
}
if (!value && Group != null)
if (!value && Group is { IsNoneSetAllowed: false })
{
throw new InvalidOperationException("Cannot directly unset a grouped button. Set another button in the group instead.");
}
@@ -326,7 +339,7 @@ namespace Robust.Client.UserInterface.Controls
return;
}
foreach (var button in _group.Buttons)
foreach (var button in _group.InternalButtons)
{
if (button != this && button.Pressed)
{
@@ -440,6 +453,29 @@ namespace Robust.Client.UserInterface.Controls
/// </remarks>
public sealed class ButtonGroup
{
internal readonly List<BaseButton> Buttons = new();
/// <summary>
/// Whether it is legal for this button group to have no selected button.
/// </summary>
/// <remarks>
/// If true, it's legal for no button in the group to be active.
/// This is then the initial state of a new group of buttons (no button is automatically selected),
/// and it becomes legal to manually clear the active button through code.
/// The user cannot manually unselect the active button regardless, only by selecting a difference button.
/// </remarks>
public bool IsNoneSetAllowed { get; }
/// <summary>
/// Create a new <see cref="ButtonGroup"/>
/// </summary>
/// <param name="isNoneSetAllowed">The value of <see cref="IsNoneSetAllowed"/> on the new button group.</param>
public ButtonGroup(bool isNoneSetAllowed = true)
{
IsNoneSetAllowed = isNoneSetAllowed;
}
internal readonly List<BaseButton> InternalButtons = new();
public IReadOnlyList<BaseButton> Buttons => InternalButtons;
public BaseButton? Pressed => InternalButtons.FirstOrDefault(x => x.Pressed);
}
}

View File

@@ -29,9 +29,11 @@ namespace Robust.Client.UserInterface.Controls
}
public Collapsible()
{}
{
Orientation = LayoutOrientation.Vertical;
}
public Collapsible(CollapsibleHeading header, CollapsibleBody body)
public Collapsible(CollapsibleHeading header, CollapsibleBody body) : this()
{
AddChild(header);
AddChild(body);
@@ -39,12 +41,9 @@ namespace Robust.Client.UserInterface.Controls
Initialize();
}
public Collapsible(string title, CollapsibleBody body)
public Collapsible(string title, CollapsibleBody body) : this(new CollapsibleHeading(title), body)
{
AddChild(new CollapsibleHeading(title));
AddChild(body);
Initialize();
}
protected internal override void Draw(DrawingHandleScreen handle)
@@ -105,11 +104,15 @@ namespace Robust.Client.UserInterface.Controls
set => _chevron.Margin = value;
}
private Label _title = new();
/// <summary>
/// Exposes the label for this heading.
/// </summary>
public Label Label { get; }
public string? Title
{
get => _title.Text;
set => _title.Text = value;
get => Label.Text;
set => Label.Text = value;
}
public CollapsibleHeading()
@@ -118,8 +121,8 @@ namespace Robust.Client.UserInterface.Controls
var box = new BoxContainer();
AddChild(box);
box.AddChild(_chevron);
_title = new Label();
box.AddChild(_title);
Label = new Label();
box.AddChild(Label);
}
public CollapsibleHeading(string title) : this()

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
@@ -9,6 +9,9 @@ using Robust.Shared.Input;
using Robust.Shared.Maths;
using Timer = Robust.Shared.Timing.Timer;
/// <summary>
/// Represents a scrollable list of items in a user interface.
/// </summary>
namespace Robust.Client.UserInterface.Controls
{
[Virtual]
@@ -29,6 +32,10 @@ namespace Robust.Client.UserInterface.Controls
public const string StylePropertySelectedItemBackground = "selected-item-background";
public const string StylePropertyDisabledItemBackground = "disabled-item-background";
/// <summary>
/// Gets or sets the ItemSeparation of individual list items
/// </summary>
public int ItemSeparation { get; set; } = 0; // Default value is 0px
public int Count => _itemList.Count;
public bool IsReadOnly => false;
@@ -68,8 +75,10 @@ namespace Robust.Client.UserInterface.Controls
itemHeight += ActualItemBackground.MinimumSize.Y * UIScale;
_totalContentHeight += (int)Math.Ceiling(itemHeight);
_totalContentHeight += ItemSeparation;
}
//Remove unneeded ItemSeparation on last item.
_totalContentHeight -= ItemSeparation;
_scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight);
_updateScrollbarVisibility();
}
@@ -390,6 +399,9 @@ namespace Robust.Client.UserInterface.Controls
}
offset += itemHeight;
// Add a ItemSeparation at the bottom of each item.
offset += ItemSeparation;
}
}
@@ -399,7 +411,7 @@ namespace Robust.Client.UserInterface.Controls
var color = ActualFontColor;
var offsetY = (int) (box.Height - font.GetHeight(UIScale)) / 2;
var baseLine = new Vector2i(0, offsetY + font.GetAscent(UIScale)) + box.TopLeft;
var baseLine = new Vector2i(5, offsetY + font.GetAscent(UIScale)) + box.TopLeft;
foreach (var rune in text.EnumerateRunes())
{

View File

@@ -12,6 +12,7 @@ namespace Robust.Client.UserInterface.Controls
public class OptionButton : ContainerButton
{
public const string StyleClassOptionButton = "optionButton";
public const string StyleClassPopup = "optionButtonPopup";
public const string StyleClassOptionTriangle = "optionTriangle";
public readonly ScrollContainer OptionsScroll;
@@ -74,7 +75,8 @@ namespace Robust.Client.UserInterface.Controls
_popup = new Popup()
{
Children = { OptionsScroll }
Children = { new PanelContainer(), OptionsScroll },
StyleClasses = { StyleClassPopup }
};
_popup.OnPopupHide += OnPopupHide;
@@ -99,6 +101,11 @@ namespace Robust.Client.UserInterface.Controls
AddItem(label, id);
}
public virtual void ButtonOverride(Button button)
{
}
public void AddItem(string label, int? id = null)
{
if (id == null)
@@ -132,6 +139,8 @@ namespace Robust.Client.UserInterface.Controls
{
Select(0);
}
ButtonOverride(button);
}
private void TogglePopup(bool show)
@@ -139,6 +148,8 @@ namespace Robust.Client.UserInterface.Controls
if (show)
{
var globalPos = GlobalPosition;
globalPos.Y += Size.Y + 1; // Place it below us, with a safety margin.
globalPos.Y -= Margin.SumVertical;
OptionsScroll.Measure(Window?.Size ?? Vector2Helpers.Infinity);
var (minX, minY) = OptionsScroll.DesiredSize;
var box = UIBox2.FromDimensions(globalPos, new Vector2(Math.Max(minX, Width), minY));

View File

@@ -15,13 +15,13 @@ namespace Robust.Client.UserInterface.Controls
{
base.Draw(handle);
var style = _getStyleBox();
var style = GetStyleBox();
style?.Draw(handle, PixelSizeBox, UIScale);
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
var styleSize = _getStyleBox()?.MinimumSize ?? Vector2.Zero;
var styleSize = GetStyleBox()?.MinimumSize ?? Vector2.Zero;
var measureSize = Vector2.Max(availableSize - styleSize, Vector2.Zero);
var childSize = Vector2.Zero;
foreach (var child in Children)
@@ -36,7 +36,7 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
var ourSize = UIBox2.FromDimensions(Vector2.Zero, finalSize);
var contentBox = _getStyleBox()?.GetContentBox(ourSize, 1) ?? ourSize;
var contentBox = GetStyleBox()?.GetContentBox(ourSize, 1) ?? ourSize;
foreach (var child in Children)
{
@@ -47,7 +47,7 @@ namespace Robust.Client.UserInterface.Controls
}
[System.Diagnostics.Contracts.Pure]
private StyleBox? _getStyleBox()
protected StyleBox? GetStyleBox()
{
if (PanelOverride != null)
{

View File

@@ -13,6 +13,7 @@ namespace Robust.Client.UserInterface.Controls
private float _value;
private float _page;
private bool _rounded;
private int _roundingDecimals = 0;
public event Action<Range>? OnValueChanged;
@@ -86,6 +87,17 @@ namespace Robust.Client.UserInterface.Controls
}
}
[ViewVariables]
public int RoundingDecimals
{
get => _roundingDecimals;
set
{
_roundingDecimals = value;
_ensureValueClamped();
}
}
public virtual void SetValueWithoutEvent(float newValue)
{
newValue = ClampValue(newValue);
@@ -107,7 +119,7 @@ namespace Robust.Client.UserInterface.Controls
{
if (_rounded)
{
value = MathF.Round(value);
value = MathF.Round(value, _roundingDecimals);
}
return MathHelper.Clamp(value, _minValue, _maxValue-_page);
}

View File

@@ -20,6 +20,11 @@ namespace Robust.Client.UserInterface.Controls
private bool _suppressScrollValueChanged;
/// <summary>
/// If true then if we have a y-axis scroll it will convert it to an x-axis scroll.
/// </summary>
public bool FallbackDeltaScroll { get; set; } = true;
public int ScrollSpeedX { get; set; } = 50;
public int ScrollSpeedY { get; set; } = 50;
@@ -246,9 +251,19 @@ namespace Robust.Client.UserInterface.Controls
if (_hScrollEnabled)
{
_hScrollBar.ValueTarget += args.Delta.X * ScrollSpeedX;
var delta =
args.Delta.X == 0f &&
!_vScrollEnabled &&
FallbackDeltaScroll ?
-args.Delta.Y :
args.Delta.X;
_hScrollBar.ValueTarget += delta * ScrollSpeedX;
}
if (!_vScrollVisible && !_hScrollVisible)
return;
args.Handle();
}

View File

@@ -16,7 +16,7 @@ namespace Robust.Client.UserInterface.Controls
{
private SpriteSystem? _sprite;
private SharedTransformSystem? _transform;
IEntityManager _entMan;
private readonly IEntityManager _entMan;
[ViewVariables]
public SpriteComponent? Sprite => Entity?.Comp1;
@@ -143,6 +143,8 @@ namespace Robust.Client.UserInterface.Controls
if (netEnt == NetEnt)
return;
// The Entity is getting set later in the ResolveEntity method
// because the client may not have received it yet.
Entity = null;
NetEnt = netEnt;
}
@@ -256,28 +258,19 @@ namespace Robust.Client.UserInterface.Controls
[NotNullWhen(true)] out SpriteComponent? sprite,
[NotNullWhen(true)] out TransformComponent? xform)
{
if (NetEnt != null && Entity == null && _entMan.TryGetEntity(NetEnt, out var ent))
SetEntity(ent);
if (Entity != null)
{
(uid, sprite, xform) = Entity.Value;
return true;
return !_entMan.Deleted(uid);
}
sprite = null;
xform = null;
uid = default;
if (NetEnt == null)
return false;
if (!_entMan.TryGetEntity(NetEnt, out var ent))
return false;
SetEntity(ent);
if (Entity == null)
return false;
(uid, sprite, xform) = Entity.Value;
return true;
return false;
}
}
}

View File

@@ -90,6 +90,8 @@ public sealed class TextEdit : Control
internal bool DebugOverlay;
private Vector2? _lastDebugMousePos;
public event Action<TextEditEventArgs>? OnTextChanged;
public TextEdit()
{
IoCManager.InjectDependencies(this);
@@ -315,7 +317,7 @@ public sealed class TextEdit : Control
if (changed)
{
_selectionStart = _cursorPosition;
// OnTextChanged?.Invoke(new LineEditEventArgs(this, _text));
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
// _updatePseudoClass();
// OnBackspace?.Invoke(new LineEditBackspaceEventArgs(oldText, _text, cursor, selectStart));
}
@@ -349,7 +351,7 @@ public sealed class TextEdit : Control
if (changed)
{
_selectionStart = _cursorPosition;
// OnTextChanged?.Invoke(new LineEditEventArgs(this, _text));
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
// _updatePseudoClass();
}
@@ -382,7 +384,10 @@ public sealed class TextEdit : Control
}
if (changed)
{
_selectionStart = _cursorPosition;
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
}
InvalidateHorizontalCursorPos();
args.Handle();
@@ -411,7 +416,10 @@ public sealed class TextEdit : Control
}
if (changed)
{
_selectionStart = _cursorPosition;
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
}
InvalidateHorizontalCursorPos();
args.Handle();
@@ -568,7 +576,7 @@ public sealed class TextEdit : Control
var newPos = CursorShiftedLeft();
// Explicit newlines work kinda funny with bias, so keep it at top there.
var bias = Rope.Index(TextRope, newPos) == '\n'
var bias = _cursorPosition.Index == TextLength || Rope.Index(TextRope, newPos) == '\n'
? LineBreakBias.Top
: LineBreakBias.Bottom;
@@ -748,6 +756,7 @@ public sealed class TextEdit : Control
var startPos = _cursorPosition;
TextRope = Rope.Insert(TextRope, startPos.Index, ev.Text);
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
_selectionStart = _cursorPosition = new CursorPos(startPos.Index + startChars, LineBreakBias.Top);
_imeData = (startPos, ev.Text.Length);
@@ -844,6 +853,7 @@ public sealed class TextEdit : Control
var upper = SelectionUpper.Index;
TextRope = Rope.ReplaceSubstring(TextRope, lower, upper - lower, text);
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
_selectionStart = _cursorPosition = new CursorPos(lower + text.Length, LineBreakBias.Top);
// OnTextChanged?.Invoke(new LineEditEventArgs(this, _text));
@@ -930,6 +940,13 @@ public sealed class TextEdit : Control
private CursorPos GetIndexAtHorizontalPos(int line, float horizontalPos)
{
// If the placeholder is visible, this function does not return correct results because it looks at TextRope,
// but _lineBreaks is configured for the display rope.
// Bail out early in this case, the function is not currently used in any situation in any location
// where something else is desired if the placeholder is visible.
if (IsPlaceholderVisible)
return default;
var contentBox = PixelSizeBox;
var font = GetFont();
var uiScale = UIScale;
@@ -1441,6 +1458,12 @@ public sealed class TextEdit : Control
AbortIme(delete: false);
}
public sealed class TextEditEventArgs(TextEdit control, Rope.Node textRope) : EventArgs
{
public TextEdit Control { get; } = control;
public Rope.Node TextRope { get; } = textRope;
}
/// <summary>
/// Specifies which line the cursor is positioned at when on a word-wrapping break.
/// </summary>

View File

@@ -17,6 +17,9 @@ namespace Robust.Client.UserInterface.Controls
{
public const string StylePropertyTexture = "texture";
public const string StylePropertyShader = "shader";
public const string StylePropertyTextureStretch = "texture-stretch";
public const string StylePropertyTextureScale = "texture-scale";
public const string StylePropertyTextureSizeTarget = "texture-size-target";
private bool _canShrink;
private Texture? _texture;
@@ -29,7 +32,18 @@ namespace Robust.Client.UserInterface.Controls
/// </summary>
public Texture? Texture
{
get => _texture;
get
{
if (_texture is null)
{
if (TryGetStyleProperty(StylePropertyTexture, out Texture? texture))
{
return texture;
}
}
return _texture;
}
set
{
var oldSize = _texture?.Size;
@@ -43,6 +57,7 @@ namespace Robust.Client.UserInterface.Controls
}
private string? _texturePath;
private StretchMode _stretch = StretchMode.Keep;
public string TexturePath
{
@@ -54,21 +69,45 @@ namespace Robust.Client.UserInterface.Controls
}
protected override void StylePropertiesChanged()
{
base.StylePropertiesChanged();
InvalidateMeasure();
}
protected override void OnThemeUpdated()
{
if (_texturePath != null) Texture = Theme.ResolveTexture(_texturePath);
base.OnThemeUpdated();
}
public Vector2 TextureSizeTarget
{
get
{
if (!TryGetStyleProperty(StylePropertyTextureSizeTarget, out Vector2 target))
target = _textureScale * Texture?.Size ?? Vector2.Zero;
return target;
}
}
/// <summary>
/// Scales the texture displayed.
/// </summary>
/// <remarks>
/// This does not apply to the following stretch modes: <see cref="StretchMode.Scale"/>.
/// This additionally does not apply if a size target is set.
/// </remarks>
public Vector2 TextureScale
{
get => _textureScale;
get
{
if (!TryGetStyleProperty(StylePropertyTextureScale, out Vector2 scale))
scale = _textureScale;
return scale;
}
set
{
_textureScale = value;
@@ -96,23 +135,27 @@ namespace Robust.Client.UserInterface.Controls
/// <summary>
/// Controls how the texture should be drawn if the control is larger than the size of the texture.
/// </summary>
public StretchMode Stretch { get; set; } = StretchMode.Keep;
public StretchMode Stretch
{
get
{
if (!TryGetStyleProperty(StylePropertyTextureStretch, out StretchMode stretch))
stretch = _stretch;
return stretch;
}
set => _stretch = value;
}
protected internal override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
var texture = _texture;
ShaderInstance? shader = null;
var texture = Texture;
if (texture == null)
{
TryGetStyleProperty(StylePropertyTexture, out texture);
if (texture == null)
{
return;
}
}
if (texture is null)
return;
ShaderInstance? shader = null;
if (ShaderOverride != null)
{
@@ -167,17 +210,17 @@ namespace Robust.Client.UserInterface.Controls
case StretchMode.Tile:
// TODO: Implement Tile.
case StretchMode.Keep:
return UIBox2.FromDimensions(Vector2.Zero, texture.Size * _textureScale * UIScale);
return UIBox2.FromDimensions(Vector2.Zero, TextureSizeTarget * UIScale);
case StretchMode.KeepCentered:
{
var position = (PixelSize - texture.Size * _textureScale * UIScale) / 2;
return UIBox2.FromDimensions(position, texture.Size * _textureScale * UIScale);
var position = (Size - TextureSizeTarget) / 2;
return UIBox2.FromDimensions(position, TextureSizeTarget * UIScale);
}
case StretchMode.KeepAspect:
case StretchMode.KeepAspectCentered:
{
var (texWidth, texHeight) = texture.Size * _textureScale;
var (texWidth, texHeight) = TextureSizeTarget;
var width = texWidth * (PixelSize.Y / texHeight);
var height = (float)PixelSize.Y;
if (width > PixelSize.X)
@@ -197,7 +240,7 @@ namespace Robust.Client.UserInterface.Controls
}
case StretchMode.KeepAspectCovered:
var texSize = texture.Size * _textureScale;
var texSize = TextureSizeTarget;
// Calculate the scale necessary to fit width and height to control size.
var (scaleX, scaleY) = PixelSize / texSize;
// Use whichever scale is greater.
@@ -259,19 +302,10 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
var texture = _texture;
if (texture == null)
{
TryGetStyleProperty(StylePropertyTexture, out texture);
}
if (texture == null || CanShrink)
{
if (CanShrink || Texture == null)
return Vector2.Zero;
}
return texture.Size * TextureScale;
return TextureSizeTarget;
}
}
}

View File

@@ -39,7 +39,7 @@ namespace Robust.Client.UserInterface.CustomControls
// And also if Update() stops firing due to an exception loop the console will still work.
// (At least from the main thread, which is what's throwing the exceptions..)
[GenerateTypedNameReferences]
public sealed partial class DebugConsole : Control, IDebugConsoleView, IPostInjectInit
public sealed partial class DebugConsole : Control, IDebugConsoleView
{
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IResourceManager _resourceManager = default!;
@@ -49,7 +49,7 @@ namespace Robust.Client.UserInterface.CustomControls
private static readonly ResPath HistoryPath = new("/debug_console_history.json");
private readonly ConcurrentQueue<FormattedMessage> _messageQueue = new();
private ISawmill _logger = default!;
private readonly ISawmill _logger;
public DebugConsole()
{
@@ -57,6 +57,8 @@ namespace Robust.Client.UserInterface.CustomControls
IoCManager.InjectDependencies(this);
_logger = _logMan.GetSawmill("dbgconsole");
InitCompletions();
CommandBar.OnTextChanged += OnCommandChanged;
@@ -282,10 +284,5 @@ namespace Robust.Client.UserInterface.CustomControls
}
});
}
void IPostInjectInit.PostInject()
{
_logger = _logMan.GetSawmill("dbgconsole");
}
}
}

View File

@@ -61,6 +61,9 @@ namespace Robust.Client.UserInterface
Vector2? CalcRelativeMousePositionFor(Control control, ScreenCoordinates mousePos);
Color GetMainClearColor();
void RenderControl(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
UIBox2i? scissorBox, Matrix3 coordinateTransform);
}
}

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using System;
using System.Numerics;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
@@ -41,20 +42,16 @@ namespace Robust.Client.UserInterface
tooltip.Measure(Vector2Helpers.Infinity);
var combinedMinSize = tooltip.DesiredSize;
LayoutContainer.SetPosition(tooltip, new Vector2(screenPosition.X, screenPosition.Y - combinedMinSize.Y));
// If it overflows right bounds then just place left on the edge.
var right = MathF.Min(screenPosition.X + combinedMinSize.X, screenBounds.X);
var right = tooltip.Position.X + combinedMinSize.X;
var top = tooltip.Position.Y;
// However, better to clamp the end of the tooltip instead of the start.
var left = MathF.Max(0f, right - combinedMinSize.X);
if (right > screenBounds.X)
{
LayoutContainer.SetPosition(tooltip, new(screenPosition.X - combinedMinSize.X, tooltip.Position.Y));
}
var bottom = MathF.Min(screenPosition.Y, screenBounds.Y);
var top = MathF.Max(0f, bottom - combinedMinSize.Y);
if (top < 0f)
{
LayoutContainer.SetPosition(tooltip, new(tooltip.Position.X, 0f));
}
LayoutContainer.SetPosition(tooltip, new Vector2(left, top));
}
}
}

View File

@@ -132,9 +132,10 @@ internal sealed partial class UserInterfaceManager
try
{
var total = 0;
_render(renderHandle, ref total, root, Vector2i.Zero, Color.White, null);
var drawingHandle = renderHandle.DrawingHandleScreen;
drawingHandle.SetTransform(Vector2.Zero, Angle.Zero, Vector2.One);
drawingHandle.SetTransform(Matrix3.Identity);
RenderControl(renderHandle, ref total, root, Vector2i.Zero, Color.White, null, Matrix3.Identity);
drawingHandle.SetTransform(Matrix3.Identity);
OnPostDrawUIRoot?.Invoke(new PostDrawUIRootEventArgs(root, drawingHandle));
_prof.WriteValue("Controls rendered", ProfData.Int32(total));

View File

@@ -75,7 +75,7 @@ namespace Robust.Client.UserInterface
foreach (var root in _roots)
{
if (root.Stylesheet != null)
if (root.Stylesheet == null)
{
root.StylesheetUpdateRecursive();
}
@@ -329,8 +329,8 @@ namespace Robust.Client.UserInterface
}
}
private void _render(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
UIBox2i? scissorBox)
public void RenderControl(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
UIBox2i? scissorBox, Matrix3 coordinateTransform)
{
if (!control.Visible)
{
@@ -377,7 +377,10 @@ namespace Robust.Client.UserInterface
total += 1;
var handle = renderHandle.DrawingHandleScreen;
handle.SetTransform(position, Angle.Zero, Vector2.One);
var oldXform = handle.GetTransform();
var xform = oldXform;
xform.Multiply(Matrix3.CreateTransform(position, Angle.Zero, Vector2.One));
handle.SetTransform(xform);
modulate *= control.Modulate;
if (_rendering || control.AlwaysRender)
@@ -389,16 +392,32 @@ namespace Robust.Client.UserInterface
handle.Modulate = oldMod;
handle.UseShader(null);
}
handle.SetTransform(oldXform);
var args = new Control.ControlRenderArguments()
{
Handle = renderHandle,
Total = ref total,
Modulate = modulate,
ScissorBox = scissorRegion,
CoordinateTransform = ref coordinateTransform
};
control.PreRenderChildren(ref args);
foreach (var child in control.Children)
{
_render(renderHandle, ref total, child, position + child.PixelPosition, modulate, scissorRegion);
var pos = position + (Vector2i) coordinateTransform.Transform(child.PixelPosition);
control.RenderChildOverride(ref args, child.GetPositionInParent(), pos);
}
control.PostRenderChildren(ref args);
if (clip)
{
renderHandle.SetScissor(scissorBox);
}
handle.SetTransform(oldXform);
}
public Color GetMainClearColor() => RootControl.ActualBgColor;

View File

@@ -126,6 +126,22 @@ namespace Robust.Client.ViewVariables
return new VVPropEditorString();
}
if (type == typeof(EntProtoId) ||
type == typeof(EntProtoId?))
{
return new VVPropEditorEntProtoId();
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ProtoId<>))
{
var editor =
(VVPropEditor)Activator.CreateInstance(
typeof(VVPropEditorProtoId<>).MakeGenericType(type.GenericTypeArguments[0]))!;
IoCManager.InjectDependencies(editor);
return editor;
}
if (typeof(IPrototype).IsAssignableFrom(type) || typeof(ViewVariablesBlobMembers.PrototypeReferenceToken).IsAssignableFrom(type))
{
return (VVPropEditor)Activator.CreateInstance(typeof(VVPropEditorIPrototype<>).MakeGenericType(type))!;

View File

@@ -0,0 +1,28 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Prototypes;
namespace Robust.Client.ViewVariables.Editors;
internal sealed class VVPropEditorEntProtoId : VVPropEditor
{
protected override Control MakeUI(object? value)
{
var lineEdit = new LineEdit
{
Text = (EntProtoId) (value ?? ""),
Editable = !ReadOnly,
HorizontalExpand = true,
};
if (!ReadOnly)
{
lineEdit.OnTextEntered += e =>
{
ValueChanged((EntProtoId) e.Text);
};
}
return lineEdit;
}
}

View File

@@ -0,0 +1,38 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Robust.Client.ViewVariables.Editors;
internal sealed class VVPropEditorProtoId<T> : VVPropEditor where T : class, IPrototype
{
[Dependency] private readonly IPrototypeManager _protoManager = default!;
protected override Control MakeUI(object? value)
{
var lineEdit = new LineEdit
{
Text = (ProtoId<T>) (value ?? ""),
Editable = !ReadOnly,
HorizontalExpand = true,
};
if (!ReadOnly)
{
lineEdit.OnTextEntered += e =>
{
var id = (ProtoId<T>)e.Text;
if (!_protoManager.HasIndex(id))
{
return;
}
ValueChanged(id);
};
}
return lineEdit;
}
}

View File

@@ -4,6 +4,7 @@ using Robust.Shared.Resources;
using Robust.Shared.Utility;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Png.Chunks;
using SixLabors.ImageSharp.PixelFormats;
namespace Robust.Packaging.AssetProcessing.Passes;
@@ -168,7 +169,7 @@ internal sealed class AssetPassPackRsis : AssetPass
}
var ms = new MemoryStream();
sheet.Metadata.GetPngMetadata().TextData.Add(new PngTextData("Description", metaJson, null, null));
sheet.Metadata.GetPngMetadata().TextData.Add(new PngTextData("Description", metaJson, "", ""));
sheet.SaveAsPng(ms);
sheet.Dispose();

View File

@@ -10,9 +10,5 @@
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NVorbis" Version="0.10.5" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Properties.targets" />
</Project>

View File

@@ -1,22 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\MSBuild\Robust.Engine.props" />
<PropertyGroup>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TRACE;RELEASE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Properties.targets" />
</Project>

View File

@@ -0,0 +1,46 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
namespace Robust.Roslyn.Shared;
#nullable enable
public static class AttributeHelper
{
public static bool HasAttribute(ISymbol symbol, string attributeMetadataName, [NotNullWhen(true)] out AttributeData? matchedAttribute)
{
foreach (var attribute in symbol.GetAttributes())
{
if (attribute.AttributeClass == null)
continue;
if (TypeSymbolHelper.ShittyTypeMatch(attribute.AttributeClass, attributeMetadataName))
{
matchedAttribute = attribute;
return true;
}
}
matchedAttribute = null;
return false;
}
public static bool GetNamedArgumentBool(AttributeData data, string name, bool defaultValue)
{
foreach (var kv in data.NamedArguments)
{
if (kv.Key != name)
continue;
if (kv.Value.Kind != TypedConstantKind.Primitive)
continue;
if (kv.Value.Value is not bool value)
continue;
return value;
}
return defaultValue;
}
}

View File

@@ -1,6 +1,6 @@
using Microsoft.CodeAnalysis;
namespace Robust.Analyzers;
namespace Robust.Roslyn.Shared;
public static class Diagnostics
{
@@ -24,6 +24,10 @@ public static class Diagnostics
public const string IdNestedDataDefinitionPartial = "RA0018";
public const string IdDataFieldWritable = "RA0019";
public const string IdDataFieldPropertyWritable = "RA0020";
public const string IdComponentPauseNotComponent = "RA0021";
public const string IdComponentPauseNoFields = "RA0022";
public const string IdComponentPauseNoParentAttribute = "RA0023";
public const string IdComponentPauseWrongTypeAttribute = "RA0024";
public static SuppressionDescriptor MeansImplicitAssignment =>
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");

View File

@@ -0,0 +1,201 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// Taken from https://github.com/CommunityToolkit/dotnet/blob/ecd1711b740f4f88d2bb943ce292ae4fc90df1bc/src/CommunityToolkit.Mvvm.SourceGenerators/Helpers/EquatableArray%7BT%7D.cs
using System.Collections;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Robust.Roslyn.Shared.Helpers;
#nullable enable
/// <summary>
/// Extensions for <see cref="EquatableArray{T}"/>.
/// </summary>
public static class EquatableArray
{
/// <summary>
/// Creates an <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.
/// </summary>
/// <typeparam name="T">The type of items in the input array.</typeparam>
/// <param name="array">The input <see cref="ImmutableArray{T}"/> instance.</param>
/// <returns>An <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.</returns>
public static EquatableArray<T> AsEquatableArray<T>(this ImmutableArray<T> array)
where T : IEquatable<T>
{
return new(array);
}
}
/// <summary>
/// An immutable, equatable array. This is equivalent to <see cref="ImmutableArray{T}"/> but with value equality support.
/// </summary>
/// <typeparam name="T">The type of values in the array.</typeparam>
public readonly struct EquatableArray<T> : IEquatable<EquatableArray<T>>, IEnumerable<T>
where T : IEquatable<T>
{
/// <summary>
/// The underlying <typeparamref name="T"/> array.
/// </summary>
private readonly T[]? array;
/// <summary>
/// Creates a new <see cref="EquatableArray{T}"/> instance.
/// </summary>
/// <param name="array">The input <see cref="ImmutableArray{T}"/> to wrap.</param>
public EquatableArray(ImmutableArray<T> array)
{
this.array = Unsafe.As<ImmutableArray<T>, T[]?>(ref array);
}
/// <summary>
/// Gets a reference to an item at a specified position within the array.
/// </summary>
/// <param name="index">The index of the item to retrieve a reference to.</param>
/// <returns>A reference to an item at a specified position within the array.</returns>
public ref readonly T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref AsImmutableArray().ItemRef(index);
}
/// <summary>
/// Gets a value indicating whether the current array is empty.
/// </summary>
public bool IsEmpty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => AsImmutableArray().IsEmpty;
}
/// <sinheritdoc/>
public bool Equals(EquatableArray<T> array)
{
return AsSpan().SequenceEqual(array.AsSpan());
}
/// <sinheritdoc/>
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is EquatableArray<T> array && Equals(this, array);
}
/// <sinheritdoc/>
public override int GetHashCode()
{
if (this.array is not T[] array)
{
return 0;
}
HashCode hashCode = default;
foreach (T item in array)
{
hashCode.Add(item);
}
return hashCode.ToHashCode();
}
/// <summary>
/// Gets an <see cref="ImmutableArray{T}"/> instance from the current <see cref="EquatableArray{T}"/>.
/// </summary>
/// <returns>The <see cref="ImmutableArray{T}"/> from the current <see cref="EquatableArray{T}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ImmutableArray<T> AsImmutableArray()
{
return Unsafe.As<T[]?, ImmutableArray<T>>(ref Unsafe.AsRef(in this.array));
}
/// <summary>
/// Creates an <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.
/// </summary>
/// <param name="array">The input <see cref="ImmutableArray{T}"/> instance.</param>
/// <returns>An <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.</returns>
public static EquatableArray<T> FromImmutableArray(ImmutableArray<T> array)
{
return new(array);
}
/// <summary>
/// Returns a <see cref="ReadOnlySpan{T}"/> wrapping the current items.
/// </summary>
/// <returns>A <see cref="ReadOnlySpan{T}"/> wrapping the current items.</returns>
public ReadOnlySpan<T> AsSpan()
{
return AsImmutableArray().AsSpan();
}
/// <summary>
/// Copies the contents of this <see cref="EquatableArray{T}"/> instance to a mutable array.
/// </summary>
/// <returns>The newly instantiated array.</returns>
public T[] ToArray()
{
return AsImmutableArray().ToArray();
}
/// <summary>
/// Gets an <see cref="ImmutableArray{T}.Enumerator"/> value to traverse items in the current array.
/// </summary>
/// <returns>An <see cref="ImmutableArray{T}.Enumerator"/> value to traverse items in the current array.</returns>
public ImmutableArray<T>.Enumerator GetEnumerator()
{
return AsImmutableArray().GetEnumerator();
}
/// <sinheritdoc/>
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)AsImmutableArray()).GetEnumerator();
}
/// <sinheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)AsImmutableArray()).GetEnumerator();
}
/// <summary>
/// Implicitly converts an <see cref="ImmutableArray{T}"/> to <see cref="EquatableArray{T}"/>.
/// </summary>
/// <returns>An <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.</returns>
public static implicit operator EquatableArray<T>(ImmutableArray<T> array)
{
return FromImmutableArray(array);
}
/// <summary>
/// Implicitly converts an <see cref="EquatableArray{T}"/> to <see cref="ImmutableArray{T}"/>.
/// </summary>
/// <returns>An <see cref="ImmutableArray{T}"/> instance from a given <see cref="EquatableArray{T}"/>.</returns>
public static implicit operator ImmutableArray<T>(EquatableArray<T> array)
{
return array.AsImmutableArray();
}
/// <summary>
/// Checks whether two <see cref="EquatableArray{T}"/> values are the same.
/// </summary>
/// <param name="left">The first <see cref="EquatableArray{T}"/> value.</param>
/// <param name="right">The second <see cref="EquatableArray{T}"/> value.</param>
/// <returns>Whether <paramref name="left"/> and <paramref name="right"/> are equal.</returns>
public static bool operator ==(EquatableArray<T> left, EquatableArray<T> right)
{
return left.Equals(right);
}
/// <summary>
/// Checks whether two <see cref="EquatableArray{T}"/> values are not the same.
/// </summary>
/// <param name="left">The first <see cref="EquatableArray{T}"/> value.</param>
/// <param name="right">The second <see cref="EquatableArray{T}"/> value.</param>
/// <returns>Whether <paramref name="left"/> and <paramref name="right"/> are not equal.</returns>
public static bool operator !=(EquatableArray<T> left, EquatableArray<T> right)
{
return !left.Equals(right);
}
}

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