Compare commits

...

109 Commits

Author SHA1 Message Date
Pieter-Jan Briers
420f690a77 Version: 210.1.2 2024-03-10 21:09:05 +01:00
Pieter-Jan Briers
612320b947 YIPPEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE IMAGESHARP VULNERABILITY
(cherry picked from commit 859f150404)
2024-03-10 21:09:05 +01:00
Pieter-Jan Briers
ef0bc1a2e4 Version: 210.1.1 2024-02-17 22:17:52 +01:00
Pieter-Jan Briers
72ba484f5b Changelog for key binding fix PRs 2024-02-17 22:12:33 +01:00
Pieter-Jan Briers
a70e511fcb Change default of ButtonGroup.IsNoneSetAllowed to true.
This brings default ButtonGroup behavior back to before #4841.

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

* Fix horrible thing I forgot to fix.

* Change error log to debug

---------

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

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

* Add UnicodeRanges too

* Changelog

---------

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

* wah

* ough

* huhwuhuahsdhsfdj

* loud incorrect buzzer

* wawa

* Allow XmlnsDefinition

* wawa

* Release notes.

* Expose keybind loading.

* address reviews and other things

---------

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-02-11 17:18:39 +01:00
Pieter-Jan Briers
74622bac83 Add DateTimeSerializer 2024-02-11 16:40:52 +01:00
Pieter-Jan Briers
a3047b1687 More warning fixes 2024-02-11 15:51:07 +01:00
Pieter-Jan Briers
3a55118143 Replace CVar OnValueChanged in systems with Subs.CVar 2024-02-11 13:29:27 +01:00
Pieter-Jan Briers
3c5fbc648a Add Subs.CVar helper for subscribing to CVar changes from entity systems 2024-02-11 13:29:27 +01:00
Pieter-Jan Briers
f9c39bce0b Use ValueList for EntitySystem subscriptions list.
That's a hundred something lists just gone and I don't have to ??=
2024-02-11 13:29:27 +01:00
Hannah Giovanna Dawson
0e8c803c0f Add GetEncoding to sandbox (#4892) 2024-02-10 17:59:13 +01:00
c4llv07e
6bb7b88c69 Save discord rich presense to the user config (#4884)
Signed-off-by: c4llv07e <kseandi@gmail.com>
2024-02-02 00:36:59 +01:00
metalgearsloth
9e0fc7017c Version: 209.0.1 2024-02-02 01:02:42 +11:00
metalgearsloth
76317b7ab3 Fix bad import (#4882)
fork why are you so SLOW.
2024-02-02 01:01:21 +11:00
metalgearsloth
d5f4d4bf2f Version: 209.0.0 2024-02-02 00:40:18 +11:00
metalgearsloth
156d1a6b14 Add SharedMapSystem helper for tile methods (#4845)
* Add SharedMapSystem helper for tile methods

* weh

* release

* Resolves

* Move some of these

* note

* Remove obsolete

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

* Un-ignore and adjust the test.

* review

---------

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

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

* weh

---------

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

This reverts commit 32049e34f2.

* IConfigurationManager.LoadDefaultsFromTomlStream now does required type conversions.

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

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

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

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

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

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

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

* a

---------

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

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

* weh

---------

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

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

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

* weh

---------

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

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

* Changed from ItemBottomMargin to ItemSeparation

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

Probably fine, saves us saving it to map files.

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

Also removed the 1 warning in there.

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

* More preliminary cleanup

* Fixes

* Fixes

* weh

* tweaks

* Tests

* weh

* Fix direction test

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

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

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

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

* Bunch of deduplication

* Remove some more duplication abuse

* Remove intersection duplication

* Bunch more cleanup

* MapManager rejig

* Fix some stuff

* More fixes

* Release notes

* Fix TryFindGrid

* Sensor check

* Fix query

* Fix map queries

* More cleanup

* Fix whatever this is.

* also dis

* Fix entity query

* Smol optimisations

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

* Disable PVS budged for the full update

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

* Add changelog line

* Move the changelog line to the proper place

* Update RELEASE-NOTES.md

* Revert "Disable PVS budged for the full update"

This reverts commit 6976ca04b8.

* Update RELEASE-NOTES.md

---------

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

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

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

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

Fixes #4836

* Register Robust.UnitTesting as assembly for reflection.

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

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

* Stop shallow cloning specific net messages in integration tests.

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

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

* Add test that serializer hash matches.

* Another test one I missed earlier.

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

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

* Update release

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

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

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

* Simplify mapping add abstract true call

* Fix docs

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

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

* Update NuGet packages and fix code for changes.

Notable:

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

* Remove dotnet-eng NuGet package source.

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

* Fix double loading of assemblies due to ALC shenanigans.

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

Chaos ensues.

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

* Remove Robust.Physics project.

Never used.

* Remove erroneous NVorbis reference.

Should be VorbisPizza and otherwise wasn't used.

* Sandbox fixes

* Remove unused unit test package references.

Castle.Core and NUnit.ConsoleRunner.

* Update NUnit to 4.0.1

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

* Mute CA1416 (platform check) errors

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

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

* Changelog

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

* Central package management for remaining 2 robust projects

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

* Oh right forgot to remove this one

* Update to a newer version of RemoteExecutor

* Disable RemoteExecutor test

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

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

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

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

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

* Sprinkle [ViewVariables] around BUI

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

I want it don't at me.

* cleanup (mraow)

* Revert "cleanup (mraow)"

This reverts commit e279957f21.

* flatpak

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

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

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

* Fix integration tests

* Also freeze _entTraitDict

* A

* I love integration test setup logic

* Re-add public method

* Freeze event bus

* Remove per-component dictionary lookup on EntAddComponent

* release notes + fix test jank

* Fix merge

* fix tests

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

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

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

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

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

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

* Fix integration tests

* Also freeze _entTraitDict

* A

* I love integration test setup logic

* Re-add public method

* changes

* Better notes

* And the new method

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

* remove autonetfield

---------

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

* Undo breaking change

* a
2024-01-03 16:07:33 +11:00
Leon Friedrich
6c6360e50a Avoid calling DirtyEntity() when repeatedly dirtying the same component. (#4797) 2024-01-03 16:06:10 +11:00
Pieter-Jan Briers
9877323195 Fix replay int overflow issues.
This came up while we were configuring stuff for the 24h round.
2024-01-03 01:18:50 +01:00
Letter N
b6f52f4c27 Implement OSWindowStyles.* on linux (#4790)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-01-03 01:04:03 +01:00
Leon Friedrich
c1789cbbaf Make MappingDataNode ordered (#4800) 2024-01-03 10:22:44 +11:00
256 changed files with 5490 additions and 2817 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

61
Directory.Packages.props Normal file
View File

@@ -0,0 +1,61 @@
<Project>
<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.3" />
<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="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" />
</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

@@ -54,6 +54,309 @@ END TEMPLATE-->
*None yet*
## 210.1.2
## 210.1.1
### Bugfixes
* Fixed multiple recent bugs with key binding storage.
### Other
* Change default of `ButtonGroup.IsNoneSetAllowed` to `true`. This makes it default again to the previous (unintentional) behavior.
## 210.1.0
### New features
* `NetUserId` implements `ISelfSerialize` so can be used in data fields.
* `ButtonGroup.IsNoneSetAllowed` to allow a button group to have no buttons pressed by default.
## 210.0.3
## 210.0.2
### Bugfixes
* Revert changes to `TextureRect` too.
## 210.0.1
### Bugfixes
* Revert changes to `TextureButton` that broke style property handling.
## 210.0.0
### New features
* Controls can now hook before, after, and during rendering of their children.
* IRenderHandle is now a public API, with the caveat that it's properties and methods are unstable.
* ButtonGroup now exposes what buttons it contains, alongside which is currently pressed.
* OptionButton has additional styleclasses, and has a hook for modifying it's internal buttons.
* PanelContainer.GetStyleBox() is now protected rather than private.
* TextureButton now uses a TextureRect instead of custom drawing code.
* TextureRect has additional style properties exposed.
* A new property, TextureSizeTarget, was added, which allows specifying a size in virtual pixels that the control should attempt to draw at.
* Stretch mode is now a style property.
* Scale is now a style property.
* Avalonia.Metadata.XmlnsDefinitionAttribute is now permitted by the sandbox.
* Add MaxDimension property to Box2 to return the higher of the Width or Height.
* Add GetLocalPosition to convert ScreenCoordinates to coordinates relative to the control. Ignores window.
* Add GlobalRect and GlobalPixelRect for controls to get their UIBox2i in screen terms.
* Add dotted line drawing to DrawingHandleScreen.
* You can use `Subs.CVar()` from an entity systems to subscribe to CVar changes. This is more convenient than `IConfigurationManager.OnValueChanged` as it automatically unsubscribes on system shutdown.
* There is now a built-in type serializer for `DateTime`, so you can put `DateTime`s in your data fields.
* `System.Text.Unicode.UnicodeRange` and `UnicodeRanges` are now available in the sandbox.
### Bugfixes
* UI drawing now properly accounts for a control's draw routine potentially mangling the current matrix.
* UI roots now properly update when the global stylesheet is changed. They previously only did so if they had a dedicated stylesheet (which is the one case where they would be unaffected by a global sheet update.
## 209.0.1
### Bugfixes
* Fix missed import from 209.0.0.
## 209.0.0
### Breaking changes
* `replay.max_compressed_size` and `replay.max_uncompressed_size` CVars are now `long`.
* Remove obsolete CoordinatesExtension for ToEntityCoordinates from GridUid / Vector2i.
### New features
* Add GetEntitiesOnMap / GetChildEntities to EntityLookupSystem to return components on the specified map and components with the specified parent respectively.
* Add MaxDimension property to Box2 to return the higher of the Width or Height.
* Add GetLocalPosition to convert ScreenCoordinates to coordinates relative to the control. Ignores window.
* Add GlobalRect and GlobalPixelRect for controls to get their UIBox2i in screen terms.
* Add dotted line drawing to DrawingHandleScreen.
* `IConfigurationManager.LoadDefaultsFromTomlStream` properly does type conversions. This fixes scenarios like loading of `long` CVars.
* Add helper methods for TileRef / Vector2i to SharedMapSystem for ToCenterCoordinates (tile center EntityCoordinates) and ToCoordinates (tile origin to EntityCoordinates).
* Copy some of the coordinates extensions to SharedTransformSystem.
### Bugfixes
* Fixed integer overflows in replay max size calculation.
* Explicitly capped `replay.replay_tick_batchSize` internally to avoid high values causing allocation failures.
### Other
* Important MIDI performance improvements.
## 208.0.0
### Breaking changes
* Metadata flags are no longer serialized as they get rebuilt on entity startup.
### Bugfixes
* Log failing to load user keybinds and handle the exception.
## 207.1.0
### New features
* Add the ability to merge grids via GridFixtureSystem.
## 207.0.0
### Breaking changes
* Update EntityLookup internally so non-approximate queries use the GJK solver and are much more accurate. This also means the approximate flag matters much more if you don't need narrowphase checks.
* Add shape versions of queries for both EntityLookup and MapManager.
### Bugfixes
* Fix PVS full state updates not clearing session entities and causing exceptions.
### Other
* Integration tests now run `NetMessage`s through serialization rather than passing the objects between client and server. This causes tests that missed `[NetSerializer]` attributes on any objects that need them to fail.
### Internal
* Remove a lot of duplicate code internally from EntityLookup and MapManager.
## 206.0.0
### Breaking changes
* tpto will teleport you to physics-center instead of transform center instead.
* Rename local EntityLookup methods to reflect they take local AABBs and not world AABBs.
### New features
* Add some additional EntityLookup methods for local queries.
* Add support to PrototypeManager for parsing specific files / directories as abstract.
### Bugfixes
* Fix tpto short-circuiting if one of the listed entities isn't found.
* Fix tpto not allowing grids as targets.
### Other
* Reduce MIDI source update rate from 10hz to 4hz.
### Internal
* Remove some duplicate internal code in EntityLookupSystem.
* Skip serialization sourcegen in GLFW and Lidgren.
## 205.0.0
### Breaking changes
* The unused `Robust.Physics` project has been deleted.
* The project now uses [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management).
* (Almost) all the NuGet packages have been updated. This causes many problems. I am so sorry.
* Cleaned up some unused packages as well.
## 204.1.0
### New features
* New `EntitySystem` subscription helper for working with Bound User Interface events. You can find them by doing `Subs.BuiEvents<>()` in a system.
* The `EntityManager.Subscriptions` type (for building helper extension methods) now uses
### Bugfixes
* Avoid loading assemblies from content `/Assemblies` if Robust ships its own copy. This avoid duplicate or weird mismatching version issues.
### Other
* Removed glibc version check warning.
## 204.0.0
### Breaking changes
* Make EntityManager abstract and make IEntityManager.EntityNetManager not nullable.
* Make VVAccess.ReadWrite default for all Datafields instead of VVAccess.ReadOnly
### New features
* `TextEdit.OnTextChanged`
* Add Pick and PickAndTake versions for System.Random for ICollections.
### Bugfixes
* Fix `IClipboardManager.GetText()` returning null in some cases.
* Fix possible NRE in server-side console command completion code.
* Fix possible NRE on DebugConsole logs.
* Fix exception when VVing non-networked components.
### Other
* Remove "Do not use from content" from IComponent.
## 203.0.0
### Breaking changes
* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered.
* Event bus subscriptions are now locked after `IEntityManager` has started, instead of after the first component gets added. Any event subscriptions now need to happen before startup (but after init).
* Event bus subscriptions must now be locked before raising any events.
* Delete FodyWeavers.xsd as it hasn't been used for a long time.
* Remove physics sleep cancelling as it was, in hindsight, a bad idea.
### New features
* `RobustUnitTest` now has a `ExtraComponents` field for automatically registering additional components.
* `IComponentFactory.RegisterIgnore()` now accepts more than one string.
* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components.
### Bugfixes
* Clamp volume calculations for audio rather than throwing.
## 202.1.1
### Bugfixes
* Reverted some map/grid initialisation changes that might've been causing broadphase/physics errors.
* Fixed PVS sometimes sending entities without first sending their children.
* Fixed a container state handling bug caused by containers not removing expected entities when shutting down.
* Fixed a `EnsureEntity<T>` state handling bug caused by improper handling of entity deletions.
* Fixed a bad NetSyncEnabled debug assert.
## 202.1.0
### New features
* Add GetLocalEntitiesIntersecting overload that takes in a griduid and a Vector2i tile.
## 202.0.0
### Breaking changes
* Various entity manager methods now have a new `where T : IComponent` constraint.
* The `IComponentFactory.ComponentAdded` event has been renamed to `ComponentsAdded` and now provides an array of component registrations.
* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered.
### New features
* Added `IComponentFactory.GetAllRegistrations()`
* Add IComponentState interface support for component states so structs can be used in lieu of classes.
## 201.0.0
### Breaking changes
* The `zCircleGradient` shader function arguments have changed. It now requires a pixel-size to ensure that the gradient is properly entered.
### Bugfixes
* Fixed some PVS null reference errors.
## 200.0.0
### Breaking changes
* MappingDataNode is now ordered.
* Make invalid AutoNetworkedFields compiler errors.
### New features
* `OSWindowStyles.NoTitleBar` (supported only on Linux X11 for now).
### Bugfixes
* Avoid calling DirtyEntity when a component's last modified tick is not current.
* Fix `tpgrid` allowing moving grids to nullspace.
### Other
* `OSWindowStyles.NoTitleOptions` is now supported on Linux X11.
## 199.0.0
### Breaking changes

View File

@@ -9,6 +9,7 @@ cmd-parse-failure-float = {$arg} is not a valid float.
cmd-parse-failure-bool = {$arg} is not a valid bool.
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
cmd-parse-failure-grid = {$arg} is not a valid grid.
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
cmd-error-file-not-found = Could not find file: {$file}.

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

@@ -1,23 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\MSBuild\Robust.Properties.targets" />
<Import Project="..\MSBuild\Robust.Engine.props" />
<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"/>
</ItemGroup>

View File

@@ -3,19 +3,20 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</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" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" />
</ItemGroup>
<ItemGroup>
<!-- Needed for NotNullableFlagAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" />
</ItemGroup>
<ItemGroup>
<!-- Needed for FriendAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" />

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

@@ -2,11 +2,12 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</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.CSharp" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>

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

@@ -106,8 +106,8 @@ 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);
}
private void OnAudioState(EntityUid uid, AudioComponent component, ref AfterAutoHandleStateEvent args)
@@ -133,13 +133,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();

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

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

View File

@@ -420,7 +420,7 @@ namespace Robust.Client.Debugging
if (mapPos.MapId != args.MapId)
return;
var player = _playerManager.LocalPlayer?.ControlledEntity;
var player = _playerManager.LocalEntity;
if (!_entityManager.TryGetComponent<TransformComponent>(player, out var playerXform) ||
playerXform.MapID != args.MapId)

View File

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

View File

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

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

@@ -221,7 +221,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

@@ -63,6 +63,17 @@ namespace Robust.Client.GameObjects
Insert((uid, TransformQuery.GetComponent(uid), MetaQuery.GetComponent(uid), null), container);
}
public override void ShutdownContainer(BaseContainer container)
{
foreach (var ent in container.ExpectedEntities)
{
if (ExpectedEntities.Remove(ent, out var c))
DebugTools.Assert(c == container);
}
base.ShutdownContainer(container);
}
private void HandleComponentState(EntityUid uid, ContainerManagerComponent component, ref ComponentHandleState args)
{
if (args.Current is not ContainerManagerComponentState cast)

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

@@ -49,16 +49,16 @@ namespace Robust.Client.GameStates
// Game state dictionaries that get used every tick.
private readonly Dictionary<EntityUid, (NetEntity NetEntity, MetaDataComponent Meta, bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
private readonly Dictionary<ushort, (IComponent Component, IComponentState? curState, IComponentState? nextState)> _compStateWork = new();
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
private readonly HashSet<NetEntity> _stateEnts = new();
private readonly List<EntityUid> _toDelete = new();
private readonly List<IComponent> _toRemove = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _outputData = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _outputData = new();
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
private readonly ObjectPool<Dictionary<ushort, ComponentState>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, ComponentState>>(new DictPolicy<ushort, ComponentState>(), 256);
private readonly ObjectPool<Dictionary<ushort, IComponentState>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, IComponentState>>(new DictPolicy<ushort, IComponentState>(), 256);
private uint _metaCompNetId;
@@ -232,9 +232,9 @@ namespace Robust.Client.GameStates
return default;
}
DebugTools.AssertNotNull(_players.LocalPlayer);
DebugTools.Assert(_players.LocalSession != null);
var evArgs = new EntitySessionEventArgs(_players.LocalPlayer!.Session);
var evArgs = new EntitySessionEventArgs(_players.LocalSession);
_pendingSystemMessages.Enqueue((_nextInputCmdSeq, _timing.CurTick, message,
new EntitySessionMessage<T>(evArgs, message)));
@@ -257,7 +257,7 @@ namespace Robust.Client.GameStates
public void UpdateFullRep(GameState state, bool cloneDelta = false)
=> _processor.UpdateFullRep(state, cloneDelta);
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
=> _processor.GetFullRep();
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
@@ -779,15 +779,13 @@ namespace Robust.Client.GameStates
newMeta.LastStateApplied = curState.ToSequence;
// Check if there's any component states awaiting this entity.
if (_entityManager.PendingNetEntityStates.TryGetValue(es.NetEntity, out var value))
if (_entityManager.PendingNetEntityStates.Remove(es.NetEntity, out var value))
{
foreach (var (type, owner) in value)
{
var pending = _pendingReapplyNetStates.GetOrNew(owner);
pending.Add(type);
}
_entityManager.PendingNetEntityStates.Remove(es.NetEntity);
}
}
@@ -851,6 +849,17 @@ namespace Robust.Client.GameStates
if (!metas.TryGetComponent(uid, out var meta))
continue;
// It may also have been queued for deletion, in which case its last server state entry has already been removed.
// I love me some spaghetti order-of-operation dependent code
if (!_processor._lastStateFullRep.ContainsKey(meta.NetEntity))
{
DebugTools.Assert(curState.EntityDeletions.Value.Contains(meta.NetEntity));
continue;
}
DebugTools.Assert(!curState.EntityDeletions.Value.Contains(meta.NetEntity));
// State already being re-applied so don't bulldoze it.
ref var state = ref CollectionsMarshal.GetValueRefOrAddDefault(_toApply, uid, out var exists);

View File

@@ -32,7 +32,7 @@ namespace Robust.Client.GameStates
/// <summary>
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
/// </summary>
internal readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _lastStateFullRep
internal readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _lastStateFullRep
= new();
/// <inheritdoc />
@@ -212,7 +212,7 @@ Had full state: {LastFullState != null}"
{
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
{
compData = new Dictionary<ushort, ComponentState>();
compData = new Dictionary<ushort, IComponentState>();
_lastStateFullRep.Add(entityState.NetEntity, compData);
}
@@ -391,7 +391,7 @@ Had full state: {LastFullState != null}"
LastFullStateRequested = null;
}
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> implicitData)
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> implicitData)
{
foreach (var (netEntity, implicitEntState) in implicitData)
{
@@ -425,18 +425,18 @@ Had full state: {LastFullState != null}"
}
}
public Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity netEntity)
public Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity netEntity)
{
return _lastStateFullRep[netEntity];
}
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
{
return _lastStateFullRep;
}
public bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary)
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary)
{
return _lastStateFullRep.TryGetValue(entity, out dictionary);
}

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -193,8 +193,13 @@ highp float zFBM(highp vec2 uv) {
// -- generative --
// Function that creates a circular gradient. Screenspace shader bread n butter.
highp float zCircleGradient(highp vec2 center, highp vec2 coord, highp float maxi, highp float radius, highp float dist, highp float power) {
return pow(clamp((length(center - coord) / radius) - dist, 0.0, maxi), power);
highp float zCircleGradient(highp vec2 ps, highp vec2 coord, highp float maxi, highp float radius, highp float dist, highp float power) {
highp float rad = (radius * ps.y) * 0.001;
highp float aspectratio = ps.x / ps.y;
highp vec2 totaldistance = ((ps * 0.5) - coord) / (rad * ps);
totaldistance.x *= aspectratio;
highp float length = (length(totaldistance) * ps.y) - dist;
return pow(clamp(length, 0.0, maxi), power);
}
// -- Utilities End --

View File

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

View File

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

View File

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

@@ -59,6 +59,11 @@ namespace Robust.Client.Graphics
/// <summary>
/// Hide title buttons such as close and minimize.
/// </summary>
NoTitleOptions = 1 << 0
NoTitleOptions = 1 << 0,
/// <summary>
/// Completely hide the title bar
/// </summary>
NoTitleBar = 1 << 1,
}
}

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

@@ -44,7 +44,7 @@ namespace Robust.Client.Placement.Modes
var closestEntity = snapToEntities[0];
var closestTransform = pManager.EntityManager.GetComponent<TransformComponent>(closestEntity);
if (!pManager.EntityManager.TryGetComponent<SpriteComponent?>(closestEntity, out var component) || component.BaseRSI == null)
if (!pManager.EntityManager.TryGetComponent(closestEntity, out SpriteComponent? component) || component.BaseRSI == null)
{
return;
}

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

@@ -162,11 +162,14 @@ namespace Robust.Client.Player
return true;
}
if (!EntManager.EnsureComponent(uid.Value, out EyeComponent eye))
if (!EntManager.HasComponent<EyeComponent>(uid.Value))
{
if (_client.RunLevel != ClientRunLevel.SinglePlayerGame)
Sawmill.Warning($"Attaching local player to an entity {EntManager.ToPrettyString(uid)} without an eye. This eye will not be netsynced and may cause issues.");
var eye = (EyeComponent) Factory.GetComponent(typeof(EyeComponent));
eye.Owner = uid.Value;
eye.NetSyncEnabled = false;
EntManager.AddComponent(uid.Value, eye);
}
Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(uid)}.");

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,32 +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.3" />
<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="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>
@@ -39,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>
@@ -54,6 +54,7 @@
<RobustLinkRoots Include="Robust.Client" />
<RobustLinkRoots Include="Robust.Shared" />
<RobustLinkAssemblies Include="TerraFX.Interop.Windows" />
<RobustLinkAssemblies Include="TerraFX.Interop.Xlib" />
<RobustLinkAssemblies Include="OpenToolkit.Graphics" />
</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

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

@@ -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();
@@ -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));
@@ -1441,6 +1451,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

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

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

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

@@ -4,12 +4,13 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>11</LangVersion>
<Nullable>enable</Nullable>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.4.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" />
</ItemGroup>
</Project>

View File

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

View File

@@ -259,6 +259,9 @@ namespace Robust.Server.Console
}
done:
result ??= CompletionResult.Empty;
var msg = new MsgConCompletionResp
{
Result = result,

View File

@@ -19,13 +19,15 @@ internal sealed partial class MetricsManager
{
private readonly ISawmill _sawmill;
private readonly HttpListener _listener;
private readonly CollectorRegistry _registry;
public ManagedHttpListenerMetricsServer(ISawmill sawmill, string host, int port, string url = "metrics/",
CollectorRegistry? registry = null) : base(registry)
CollectorRegistry? registry = null)
{
_sawmill = sawmill;
_listener = new HttpListener();
_listener.Prefixes.Add($"http://{host}:{port}/{url}");
_registry = registry ?? Metrics.DefaultRegistry;
}
protected override Task StartServer(CancellationToken cancel)

View File

@@ -1211,7 +1211,7 @@ public sealed class MapLoaderSystem : EntitySystem
// information that needs to be written.
if (compMapping.Children.Count != 0 || protMapping == null)
{
compMapping.Add("type", new ValueDataNode(compName));
compMapping.InsertAt(0, "type", new ValueDataNode(compName));
// Something actually got written!
components.Add(compMapping);
}

View File

@@ -28,7 +28,7 @@ namespace Robust.Server.GameObjects
base.Initialize();
SubscribeLocalEvent<MapGridComponent, EmptyGridEvent>(HandleGridEmpty);
_cfg.OnValueChanged(CVars.GameDeleteEmptyGrids, SetGridDeletion, true);
Subs.CVar(_cfg, CVars.GameDeleteEmptyGrids, SetGridDeletion, true);
}
protected override void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args)
@@ -64,13 +64,6 @@ namespace Robust.Server.GameObjects
return !(grid.GetAllTiles().Any());
}
public override void Shutdown()
{
base.Shutdown();
_cfg.UnsubValueChanged(CVars.GameDeleteEmptyGrids, SetGridDeletion);
}
private void HandleGridEmpty(EntityUid uid, MapGridComponent component, EmptyGridEvent args)
{
if (!_deleteEmptyGrids || TerminatingOrDeleted(uid) || HasComp<MapComponent>(uid))

View File

@@ -20,7 +20,8 @@ namespace Robust.Server.GameObjects
{
base.Initialize();
LoadMetricCVar();
_configurationManager.OnValueChanged(CVars.MetricsEnabled, _ => LoadMetricCVar());
Subs.CVar(_configurationManager, CVars.MetricsEnabled, _ => LoadMetricCVar());
}
private void LoadMetricCVar()

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using Prometheus;
using Robust.Shared.Enums;
using Robust.Shared.Log;
using Robust.Shared.Player;
using Robust.Shared.Threading;
using Robust.Shared.Timing;
@@ -43,7 +45,8 @@ internal sealed partial class PvsSystem
foreach (var session in PendingAcks)
{
_toAck.Add(GetOrNewPvsSession(session));
if (session.Status != SessionStatus.Disconnected)
_toAck.Add(GetOrNewPvsSession(session));
}
PendingAcks.Clear();
@@ -66,7 +69,14 @@ internal sealed partial class PvsSystem
public void Execute(int index)
{
_pvs.ProcessQueuedAck(_pvs._toAck[index]);
try
{
_pvs.ProcessQueuedAck(_pvs._toAck[index]);
}
catch (Exception e)
{
_pvs.Log.Log(LogLevel.Error, e, $"Caught exception while processing PVS acks.");
}
}
}
@@ -78,7 +88,14 @@ internal sealed partial class PvsSystem
public void Execute(int index)
{
_pvs.UpdateDirtyChunks(index);
try
{
_pvs.UpdateDirtyChunks(index);
}
catch (Exception e)
{
_pvs.Log.Log(LogLevel.Error, e, $"Caught exception while updating dirty PVS chunks.");
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Runtime.InteropServices;
using System.Threading;
using Prometheus;
using Robust.Shared.Enums;
using Robust.Shared.Log;
using Robust.Shared.Network.Messages;
using Robust.Shared.Player;
using Robust.Shared.Threading;
@@ -77,7 +78,14 @@ internal sealed partial class PvsSystem
public void Execute(int index)
{
_pvs.ProcessLeavePvs(_sessions[index]);
try
{
_pvs.ProcessLeavePvs(_sessions[index]);
}
catch (Exception e)
{
_pvs.Log.Log(LogLevel.Error, e, $"Caught exception while processing pvs-leave messages.");
}
}
public void Setup(ICommonSession[] sessions)

View File

@@ -24,6 +24,8 @@ internal sealed partial class PvsSystem
internal readonly Dictionary<ICommonSession, PvsSession> PlayerData = new();
private List<ICommonSession> _disconnected = new();
private void SendStateUpdate(ICommonSession session, PvsThreadResources resources)
{
var data = GetOrNewPvsSession(session);
@@ -174,13 +176,8 @@ internal sealed partial class PvsSystem
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus != SessionStatus.Disconnected)
return;
if (!PlayerData.Remove(e.Session, out var session))
return;
ClearSendHistory(session);
if (e.NewStatus == SessionStatus.Disconnected)
_disconnected.Add(e.Session);
}
private void ClearSendHistory(PvsSession session)
@@ -196,5 +193,6 @@ internal sealed partial class PvsSystem
session.PreviouslySent.Clear();
session.LastSent = null;
session.Entities.Clear();
}
}

View File

@@ -24,7 +24,7 @@ internal sealed partial class PvsSystem
}
/// <summary>
/// A chunks that is visible to a player and add entities to the game-state.
/// Add all entities on a given PVS chunk to a clients game-state.
/// </summary>
private void AddPvsChunk(PvsChunk chunk, float distance, PvsSession session)
{
@@ -48,17 +48,28 @@ internal sealed partial class PvsSystem
// We add chunk-size here so that its consistent with the normal PVS range setting.
// I.e., distance here is the Chebyshev distance to the centre of each chunk, but the normal pvs range only
// required that the chunk be touching the box, not the centre.
var count = distance < (_lowLodDistance + ChunkSize) / 2
var limit = distance < (_lowLodDistance + ChunkSize) / 2
? chunk.Contents.Count
: chunk.LodCounts[0];
// If the PVS budget is exceeded, it should still be safe to send all of the chunk's direct children, though
// after that we have no guarantee that an entity's parent got sent.
var directChildren = Math.Min(limit, chunk.LodCounts[2]);
// Send entities on the chunk.
var span = CollectionsMarshal.AsSpan(chunk.Contents);
for (var i = 0; i < count; i++)
for (var i = 0; i < limit; i++)
{
var ent = span[i];
if ((mask & ent.Comp.VisibilityMask) == ent.Comp.VisibilityMask)
AddEntity(session, ent, fromTick);
if ((mask & ent.Comp.VisibilityMask) != ent.Comp.VisibilityMask)
continue;
// TODO PVS improve this somehow
// Having entities "leave" pvs view just because the pvs entry budget was exceeded sucks.
// This probably requires changing client game state manager to support receiving entities with unknown parents.
// Probably needs to do something similar to pending net entity states, but for entity spawning.
if (!AddEntity(session, ent, fromTick))
limit = directChildren;
}
}

View File

@@ -126,11 +126,12 @@ internal sealed partial class PvsSystem : EntitySystem
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_transform.OnGlobalMoveEvent += OnEntityMove;
_configManager.OnValueChanged(CVars.NetPVS, SetPvs, true);
_configManager.OnValueChanged(CVars.NetMaxUpdateRange, OnViewsizeChanged, true);
_configManager.OnValueChanged(CVars.NetLowLodRange, OnLodChanged, true);
_configManager.OnValueChanged(CVars.NetForceAckThreshold, OnForceAckChanged, true);
_configManager.OnValueChanged(CVars.NetPvsAsync, OnAsyncChanged, true);
Subs.CVar(_configManager, CVars.NetPVS, SetPvs, true);
Subs.CVar(_configManager, CVars.NetMaxUpdateRange, OnViewsizeChanged, true);
Subs.CVar(_configManager, CVars.NetLowLodRange, OnLodChanged, true);
Subs.CVar(_configManager, CVars.NetForceAckThreshold, OnForceAckChanged, true);
Subs.CVar(_configManager, CVars.NetPvsAsync, OnAsyncChanged, true);
Subs.CVar(_configManager, CVars.NetPvsCompressLevel, ResetParallelism, true);
_serverGameStateManager.ClientAck += OnClientAck;
_serverGameStateManager.ClientRequestFull += OnClientRequestFull;
@@ -138,7 +139,6 @@ internal sealed partial class PvsSystem : EntitySystem
InitializeDirty();
_parallelMgr.ParallelCountChanged += ResetParallelism;
_configManager.OnValueChanged(CVars.NetPvsCompressLevel, ResetParallelism, true);
}
public override void Shutdown()
@@ -148,10 +148,6 @@ internal sealed partial class PvsSystem : EntitySystem
_playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
_transform.OnGlobalMoveEvent -= OnEntityMove;
_configManager.UnsubValueChanged(CVars.NetPVS, SetPvs);
_configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, OnViewsizeChanged);
_configManager.UnsubValueChanged(CVars.NetForceAckThreshold, OnForceAckChanged);
_configManager.UnsubValueChanged(CVars.NetPvsCompressLevel, ResetParallelism);
_parallelMgr.ParallelCountChanged -= ResetParallelism;
_serverGameStateManager.ClientAck -= OnClientAck;
@@ -252,6 +248,8 @@ internal sealed partial class PvsSystem : EntitySystem
private void ForceFullState(PvsSession session)
{
_leaveTask?.WaitOne();
_leaveTask = null;
session.LastReceivedAck = _gameTiming.CurTick;
session.RequestedFull = true;
ClearSendHistory(session);
@@ -405,6 +403,15 @@ internal sealed partial class PvsSystem : EntitySystem
DebugTools.Assert(_chunks.Values.All(x => Exists(x.Map) && Exists(x.Root)));
DebugTools.Assert(_chunkSets.Keys.All(Exists));
_leaveTask?.WaitOne();
_leaveTask = null;
foreach (var session in _disconnected)
{
if (PlayerData.Remove(session, out var pvsSession))
ClearSendHistory(pvsSession);
}
var ackJob = ProcessQueuedAcks();
// Figure out what chunks players can see and cache some chunk data.
@@ -415,10 +422,6 @@ internal sealed partial class PvsSystem : EntitySystem
}
ackJob?.WaitOne();
// Ensure any pvs leave tasks have finished before we get the next state & overwrite the "last sent" list.
_leaveTask?.WaitOne();
_leaveTask = null;
}
internal void CacheSessionData(ICommonSession[] players)

View File

@@ -27,7 +27,7 @@ namespace Robust.Server.Log
if (valid)
{
message.AddOrUpdateProperty(sawmillProperty);
message.AddOrUpdateProperty(sawmillProperty!);
}
_sLogger.Write(message);

View File

@@ -0,0 +1,138 @@
using System.Collections.Generic;
using System.Numerics;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Server.Physics;
public sealed partial class GridFixtureSystem
{
/*
* Something to keep in mind is that they rotate around the origin of a tile (e.g. 0,0), so a rotation of -90 degrees
* moves the origin to 0, -1.
*/
/// <summary>
/// Merges GridB into GridA.
/// </summary>
/// <param name="offset">Origin of GridB relative to GridA</param>
/// <param name="rotation">Rotation to apply to GridB when being merged.
/// Note that the rotation is applied before the offset so the offset itself will be rotated.</param>
public void Merge(
EntityUid gridAUid,
EntityUid gridBUid,
Vector2i offset,
Angle rotation,
MapGridComponent? gridA = null,
MapGridComponent? gridB = null,
TransformComponent? xformA = null,
TransformComponent? xformB = null)
{
var matrix = Matrix3.CreateTransform(offset, rotation);
Merge(gridAUid, gridBUid, matrix, gridA, gridB, xformA, xformB);
}
/// <summary>
/// Merges GridB into GridA.
/// </summary>
/// <param name="offset">Origin of GridB relative to GridA</param>
/// <param name="matrix">Matrix to apply to gridB when being merged.
/// Note that rotation is applied first and then offset so the offset itself will be rotated.</param>
public void Merge(
EntityUid gridAUid,
EntityUid gridBUid,
Matrix3 matrix,
MapGridComponent? gridA = null,
MapGridComponent? gridB = null,
TransformComponent? xformA = null,
TransformComponent? xformB = null)
{
if (!Resolve(gridAUid, ref gridA, ref xformA))
return;
if (!Resolve(gridBUid, ref gridB, ref xformB))
return;
var sw = new Stopwatch();
var tiles = new List<(Vector2i Indices, Tile Tile)>();
var enumerator = _maps.GetAllTilesEnumerator(gridBUid, gridB);
while (enumerator.MoveNext(out var tileRef))
{
var offsetTile = matrix.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector);
tiles.Add((offsetTile.Floored(), tileRef.Value.Tile));
}
_maps.SetTiles(gridAUid, gridA, tiles);
enumerator = _maps.GetAllTilesEnumerator(gridBUid, gridB);
var rotationDiff = matrix.Rotation();
while (enumerator.MoveNext(out var tileRef))
{
var chunkOrigin = SharedMapSystem.GetChunkIndices(tileRef.Value.GridIndices, gridB.ChunkSize);
if (!_maps.TryGetChunk(gridBUid, gridB, chunkOrigin, out var chunk))
{
continue;
}
var chunkLocalTile = SharedMapSystem.GetChunkRelative(tileRef.Value.GridIndices, gridB.ChunkSize);
var snapgrid = chunk.GetSnapGrid((ushort) chunkLocalTile.X, (ushort) chunkLocalTile.Y);
if (snapgrid == null || snapgrid.Count == 0)
continue;
var offsetTile = matrix.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector);
var tileIndex = offsetTile.Floored();
for (var j = snapgrid.Count - 1; j >= 0; j--)
{
var ent = snapgrid[j];
var xform = _xformQuery.GetComponent(ent);
_xformSystem.ReAnchor(ent, xform,
gridB, gridA,
tileRef.Value.GridIndices, tileIndex,
gridBUid, gridAUid,
xformB, xformA,
rotationDiff);
DebugTools.Assert(xform.ParentUid == gridAUid);
}
DebugTools.Assert(snapgrid.Count == 0);
}
enumerator = _maps.GetAllTilesEnumerator(gridBUid, gridB);
while (enumerator.MoveNext(out var tileRef))
{
var bounds = _lookup.GetLocalBounds(tileRef.Value.GridIndices, gridB.TileSize);
_entSet.Clear();
_lookup.GetLocalEntitiesIntersecting(gridBUid, bounds, _entSet, LookupFlags.All | ~LookupFlags.Contained | LookupFlags.Approximate);
foreach (var ent in _entSet)
{
// Consider centre of entity position maybe?
var entXform = _xformQuery.GetComponent(ent);
if (entXform.ParentUid != gridBUid ||
!bounds.Contains(entXform.LocalPosition)) continue;
var newPos = matrix.Transform(entXform.LocalPosition);
_xformSystem.SetCoordinates(ent, entXform, new EntityCoordinates(gridAUid, newPos), entXform.LocalRotation + rotationDiff, oldParent: xformB, newParent: xformA);
}
}
DebugTools.Assert(xformB.ChildCount == 0);
Del(gridBUid);
Log.Debug($"Merged grids in {sw.Elapsed.TotalMilliseconds}ms");
}
}

View File

@@ -23,16 +23,16 @@ namespace Robust.Server.Physics
/// <summary>
/// Handles generating fixtures for MapGrids.
/// </summary>
public sealed class GridFixtureSystem : SharedGridFixtureSystem
public sealed partial class GridFixtureSystem : SharedGridFixtureSystem
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IConGroupController _conGroup = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
private ISawmill _logger = default!;
private readonly Dictionary<EntityUid, Dictionary<Vector2i, ChunkNodeGroup>> _nodes = new();
/// <summary>
@@ -47,15 +47,24 @@ namespace Robust.Server.Physics
internal bool SplitAllowed = true;
private HashSet<EntityUid> _entSet = new();
private EntityQuery<MapGridComponent> _gridQuery;
private EntityQuery<PhysicsComponent> _bodyQuery;
private EntityQuery<TransformComponent> _xformQuery;
public override void Initialize()
{
base.Initialize();
_logger = Logger.GetSawmill("gsplit");
_gridQuery = GetEntityQuery<MapGridComponent>();
_bodyQuery = GetEntityQuery<PhysicsComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoval);
SubscribeNetworkEvent<RequestGridNodesMessage>(OnDebugRequest);
SubscribeNetworkEvent<StopGridNodesMessage>(OnDebugStopRequest);
_cfg.OnValueChanged(CVars.GridSplitting, SetSplitAllowed, true);
Subs.CVar(_cfg, CVars.GridSplitting, SetSplitAllowed, true);
}
private void SetSplitAllowed(bool value) => SplitAllowed = value;
@@ -64,7 +73,6 @@ namespace Robust.Server.Physics
{
base.Shutdown();
_subscribedSessions.Clear();
_cfg.UnsubValueChanged(CVars.GridSplitting, SetSplitAllowed);
}
/// <summary>
@@ -197,7 +205,7 @@ namespace Robust.Server.Physics
}
_isSplitting = true;
_logger.Debug($"Started split check for {ToPrettyString(uid)}");
Log.Debug($"Started split check for {ToPrettyString(uid)}");
var splitFrontier = new Queue<ChunkSplitNode>(4);
var grids = new List<HashSet<ChunkSplitNode>>(1);
@@ -234,7 +242,7 @@ namespace Robust.Server.Physics
// Split time
if (grids.Count > 1)
{
_logger.Info($"Splitting {ToPrettyString(uid)} into {grids.Count} grids.");
Log.Info($"Splitting {ToPrettyString(uid)} into {grids.Count} grids.");
var sw = new Stopwatch();
sw.Start();
@@ -244,13 +252,10 @@ namespace Robust.Server.Physics
x.Sum(o => o.Indices.Count)
.CompareTo(y.Sum(o => o.Indices.Count)));
var xformQuery = GetEntityQuery<TransformComponent>();
var bodyQuery = GetEntityQuery<PhysicsComponent>();
var gridQuery = GetEntityQuery<MapGridComponent>();
var oldGridXform = xformQuery.GetComponent(oldGridUid);
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(oldGridXform, xformQuery);
var mapBody = bodyQuery.GetComponent(oldGridUid);
var oldGridComp = gridQuery.GetComponent(oldGridUid);
var oldGridXform = _xformQuery.GetComponent(oldGridUid);
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(oldGridXform);
var mapBody = _bodyQuery.GetComponent(oldGridUid);
var oldGridComp = _gridQuery.GetComponent(oldGridUid);
var newGrids = new EntityUid[grids.Count - 1];
var mapId = oldGridXform.MapID;
@@ -259,17 +264,17 @@ namespace Robust.Server.Physics
var group = grids[i];
var newGrid = _mapManager.CreateGridEntity(mapId);
var newGridUid = newGrid.Owner;
var newGridXform = xformQuery.GetComponent(newGridUid);
var newGridXform = _xformQuery.GetComponent(newGridUid);
newGrids[i] = newGridUid;
// Keep same origin / velocity etc; this makes updating a lot faster and easier.
_xformSystem.SetWorldPosition(newGridXform, gridPos);
_xformSystem.SetWorldPositionRotation(newGridXform, gridPos, gridRot);
var splitBody = bodyQuery.GetComponent(newGridUid);
_xformSystem.SetWorldPositionRotation(newGridUid, gridPos, gridRot, newGridXform);
var splitBody = _bodyQuery.GetComponent(newGridUid);
_physics.SetLinearVelocity(newGridUid, mapBody.LinearVelocity, body: splitBody);
_physics.SetAngularVelocity(newGridUid, mapBody.AngularVelocity, body: splitBody);
var gridComp = gridQuery.GetComponent(newGridUid);
var gridComp = _gridQuery.GetComponent(newGridUid);
var tileData = new List<(Vector2i GridIndices, Tile Tile)>(group.Sum(o => o.Indices.Count));
// Gather all tiles up front and set once to minimise fixture change events
@@ -284,7 +289,7 @@ namespace Robust.Server.Physics
}
}
newGrid.Comp.SetTiles(tileData);
_maps.SetTiles(newGrid.Owner, newGrid.Comp, tileData);
DebugTools.Assert(_mapManager.IsGrid(newGridUid), "A split grid had no tiles?");
// Set tiles on new grid + update anchored entities
@@ -303,8 +308,13 @@ namespace Robust.Server.Physics
for (var j = snapgrid.Count - 1; j >= 0; j--)
{
var ent = snapgrid[j];
var xform = xformQuery.GetComponent(ent);
_xformSystem.ReAnchor(ent, xform, oldGridComp, gridComp, tilePos, oldGridUid, newGridUid, oldGridXform, newGridXform, xformQuery);
var xform = _xformQuery.GetComponent(ent);
_xformSystem.ReAnchor(ent, xform,
oldGridComp, gridComp,
tilePos, tilePos,
oldGridUid, newGridUid,
oldGridXform, newGridXform,
Angle.Zero);
DebugTools.Assert(xform.Anchored);
}
}
@@ -317,15 +327,18 @@ namespace Robust.Server.Physics
var tilePos = offset + tile;
var bounds = _lookup.GetLocalBounds(tilePos, oldGrid.TileSize);
foreach (var ent in _lookup.GetEntitiesIntersecting(oldGridUid, tilePos, 0f, LookupFlags.Dynamic | LookupFlags.Sundries))
_entSet.Clear();
_lookup.GetLocalEntitiesIntersecting(oldGridUid, tilePos, _entSet, 0f, LookupFlags.All | ~LookupFlags.Uncontained | LookupFlags.Approximate);
foreach (var ent in _entSet)
{
// Consider centre of entity position maybe?
var entXform = xformQuery.GetComponent(ent);
var entXform = _xformQuery.GetComponent(ent);
if (entXform.ParentUid != oldGridUid ||
!bounds.Contains(entXform.LocalPosition)) continue;
_xformSystem.SetParent(ent, entXform, newGridUid, xformQuery, newGridXform);
_xformSystem.SetParent(ent, entXform, newGridUid, _xformQuery, newGridXform);
}
}
@@ -365,10 +378,10 @@ namespace Robust.Server.Physics
var ev = new GridSplitEvent(newGrids, oldGridUid);
RaiseLocalEvent(uid, ref ev, true);
_logger.Debug($"Split {grids.Count} grids in {sw.Elapsed}");
Log.Debug($"Split {grids.Count} grids in {sw.Elapsed}");
}
_logger.Debug($"Stopped split check for {ToPrettyString(uid)}");
Log.Debug($"Stopped split check for {ToPrettyString(uid)}");
_isSplitting = false;
SendNodeDebug(oldGridUid);
}

View File

@@ -0,0 +1,69 @@
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Toolshed;
namespace Robust.Server.Physics;
public sealed class MergeGridsCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
public override string Command => "merge_grids";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 4)
{
return;
}
if (!NetEntity.TryParse(args[0], out var gridANet) ||
!NetEntity.TryParse(args[1], out var gridBNet))
{
return;
}
var gridAUid = _entManager.GetEntity(gridANet);
var gridBUid = _entManager.GetEntity(gridBNet);
if (!_entManager.TryGetComponent<MapGridComponent>(gridAUid, out var gridA) ||
!_entManager.TryGetComponent<MapGridComponent>(gridBUid, out var gridB))
{
return;
}
if (!int.TryParse(args[2], out var x) ||
!int.TryParse(args[3], out var y))
{
return;
}
Angle rotation = Angle.Zero;
if (args.Length >= 5 && int.TryParse(args[4], out var rotationInt))
{
rotation = Angle.FromDegrees(rotationInt);
}
var offset = new Vector2i(x, y);
var fixtureSystem = _entManager.System<GridFixtureSystem>();
fixtureSystem.Merge(gridAUid, gridBUid, offset, rotation, gridA: gridA, gridB: gridB);
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
return args.Length switch
{
1 => CompletionResult.FromHintOptions(CompletionHelper.Components<MapGridComponent>(args[0], _entManager), Loc.GetString("cmd-merge_grids-hintA")),
2 => CompletionResult.FromHintOptions(CompletionHelper.Components<MapGridComponent>(args[1], _entManager), Loc.GetString("cmd-merge_grids-hintB")),
3 => CompletionResult.FromHint(Loc.GetString("cmd-merge_grids-xOffset")),
4 => CompletionResult.FromHint(Loc.GetString("cmd-merge_grids-yOffset")),
5 => CompletionResult.FromHint(Loc.GetString("cmd-merge_grids-angle")),
_ => CompletionResult.Empty
};
}
}

View File

@@ -255,7 +255,7 @@ namespace Robust.Server.Placement
/// </summary>
public void SendPlacementBegin(EntityUid mob, int range, string objectType, string alignOption)
{
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
return;
var playerConnection = actor.PlayerSession.Channel;
@@ -276,7 +276,7 @@ namespace Robust.Server.Placement
/// </summary>
public void SendPlacementBeginTile(EntityUid mob, int range, string tileType, string alignOption)
{
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
return;
var playerConnection = actor.PlayerSession.Channel;
@@ -297,7 +297,7 @@ namespace Robust.Server.Placement
/// </summary>
public void SendPlacementCancel(EntityUid mob)
{
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
return;
var playerConnection = actor.PlayerSession.Channel;

View File

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

View File

@@ -10,27 +10,30 @@
<!-- Try to fix sporadic errors against Robust.Packaging, apparently?? -->
<ValidateExecutableReferencesMatchSelfContained>false</ValidateExecutableReferencesMatchSelfContained>
<RobustILLink>true</RobustILLink>
<NoWarn>CA1416</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
<PackageReference Include="SpaceWizards.HttpListener" Version="0.1.0" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.4" Condition="'$(UseSystemSqlite)' == 'True'" /> <!-- Cannot be private since Content.Server/Database/ServerDbManager.cs depends on SQLitePCL.raw -->
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.4" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
<PackageReference Include="prometheus-net" Version="4.1.1" />
<PackageReference Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="6.0.0" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.2" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.2-beta2" PrivateAssets="compile" />
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
<PackageReference Include="SpaceWizards.HttpListener" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Condition="'$(UseSystemSqlite)' == 'True'" /> <!-- Cannot be private since Content.Server/Database/ServerDbManager.cs depends on SQLitePCL.raw -->
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
<PackageReference Include="prometheus-net" />
<PackageReference Include="Serilog.Sinks.Loki" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Extensions.Primitives" />
<!-- Needed to pull-up the dependency from prometheus-net -->
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="prometheus-net.DotNetRuntime" />
<PackageReference Include="TerraFX.Interop.Windows" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" PrivateAssets="compile" />
<PackageReference Include="SharpZstd.Interop" PrivateAssets="compile" />
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\Robust.Physics\Robust.Physics.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />

View File

@@ -3,6 +3,7 @@ using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
@@ -294,27 +295,22 @@ namespace Robust.Server.Scripting
loader: TextLoader.From(TextAndVersion.Create(SourceText.From(message.Code), VersionStamp.Create()))
));
var results = await CompletionService
.GetService(document)
.GetCompletionsAsync(document, message.Cursor);
var results = await (CompletionService
.GetService(document)?
.GetCompletionsAsync(document, message.Cursor) ?? Task.FromResult(CompletionList.Empty));
if (results is not null)
{
var ires = ImmutableArray.CreateBuilder<LiteResult>();
foreach (var r in results.Items)
ires.Add(new LiteResult(
displayText: r.DisplayText,
displayTextPrefix: r.DisplayTextPrefix,
displayTextSuffix: r.DisplayTextSuffix,
inlineDescription: r.InlineDescription,
tags: r.Tags,
properties: r.Properties
));
var ires = ImmutableArray.CreateBuilder<LiteResult>();
foreach (var r in results.ItemsList)
ires.Add(new LiteResult(
displayText: r.DisplayText,
displayTextPrefix: r.DisplayTextPrefix,
displayTextSuffix: r.DisplayTextSuffix,
inlineDescription: r.InlineDescription,
tags: r.Tags,
properties: r.Properties
));
replyMessage.Results = ires.ToImmutable();
}
else
replyMessage.Results = ImmutableArray<LiteResult>.Empty;
replyMessage.Results = ires.ToImmutable();
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
}

View File

@@ -308,7 +308,7 @@ namespace Robust.Server.ServerStatus
private static bool RequestWantsZStd(IStatusHandlerContext context)
{
// Yeah this isn't a good parser for Accept-Encoding but who cares.
return context.RequestHeaders.TryGetValue("Accept-Encoding", out var ac) && ac.Count > 0 && ac[0].Contains("zstd");
return context.RequestHeaders.TryGetValue("Accept-Encoding", out var ac) && ac.Count > 0 && ac[0]?.Contains("zstd") == true;
}
// Only call this if the download URL is not available!

View File

@@ -97,7 +97,7 @@ namespace Robust.Server.ViewVariables
// Auto-dirty component. Only works when modifying a field that is directly on a component,
// Does not work for nested objects.
if (Object is Component comp)
if (Object is Component { NetSyncEnabled: true } comp)
EntityManager.Dirty(comp.Owner, comp);
}

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