Compare commits

...

244 Commits

Author SHA1 Message Date
Vera Aguilera Puerto
1611fb00d8 Version 0.8.4 2021-12-11 14:19:40 +01:00
Vera Aguilera Puerto
57a1f2743e Some minor PVS optimizations (#2324) 2021-12-11 14:17:38 +01:00
metalgearsloth
fd1a1d326c Fix broadphase RemoveBody issue (#2318) 2021-12-11 14:08:57 +01:00
metalgearsloth
ca7fdacfeb Make RunDeferred use worldpos+rot method (#2314) 2021-12-11 14:02:43 +01:00
Vera Aguilera Puerto
80f05d7467 Fix botched delete inline in EntityLookup 2021-12-11 12:38:32 +01:00
Paul Ritter
e84604f7e3 Fixes pvs culling & make budgets more granular (#2322) 2021-12-11 12:11:16 +01:00
Vera Aguilera Puerto
66c3013e39 VV EntityUid Name/Description setter.
For all your adminbus needs!
2021-12-10 11:53:00 +01:00
Vera Aguilera Puerto
717802fe54 Fix wrong grid placement direction when grid is rotated
Something to note is that grid placement is still very scuffed. On certain rotations, it begins acting weird.
2021-12-10 11:24:39 +01:00
Paul
977a840253 version bump 2021-12-10 10:17:25 +01:00
Paul
6051d4d358 removes statecache 2021-12-10 10:16:22 +01:00
Vera Aguilera Puerto
0a00a8a109 Fix build 2021-12-09 21:30:50 +01:00
wrexbe
9d1aff3a75 Fix NextMultipleOf for int/long (#2289) 2021-12-09 12:27:08 -08:00
E F R
3332279e75 SharedPhysicsSystem: convert parents' angular velocity to linear when leaving (#2208)
* SharedPhysicsSystem: convert parents' angular velocity to linear when leaving

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

* SharedPhysicsSystem: Use Center of Mass now that it supposedly works

* SharedPhysicsSystem: Use REAL MATH™ to handle rotation when reparenting

* SharedPhysicsSystem: Cache some reused matrices

Addresses:
https://github.com/space-wizards/RobustToolbox/pull/2208#discussion_r762503056
https://github.com/space-wizards/RobustToolbox/pull/2208#discussion_r762502704

* SharedPhysicsSystem: More caching, I guess

* SharedPhysicsSystem: save ONE WHOLE ACCESS

What's next, shortening variable names to make it load faster?

Co-authored-by: ElectroJr <60421075+ElectroJr@users.noreply.github.com>
Co-authored-by: Tomeno <Tomeno@users.noreply.github.com>
2021-12-09 12:18:37 -08:00
Vera Aguilera Puerto
d8059cacf4 Version 0.8.2
I hate myself sometimes
2021-12-09 14:55:15 +01:00
Vera Aguilera Puerto
374e80aad3 Version 0.8.1
Fixes bug regarding actors and player sessions on RELEASE.
2021-12-09 14:53:05 +01:00
Vera Aguilera Puerto
2fd15b0375 Version 0.8.0 2021-12-09 14:27:12 +01:00
Vera Aguilera Puerto
9293421f41 Merge branch '2021-12-03-remove-IEntity-komm-süsser-todd' 2021-12-09 14:25:49 +01:00
Vera Aguilera Puerto
4f358b3a5e proper VV for EntityUid 2021-12-09 13:24:19 +01:00
Vera Aguilera Puerto
76a804d427 TempQualifier moment 2021-12-09 12:54:49 +01:00
Vera Aguilera Puerto
f8bf877b61 Fix weird lifestage autorefactors. 2021-12-09 12:39:42 +01:00
Vera Aguilera Puerto
1f99f43755 Resolve IEntityManager as needed in MetaDataComponent 2021-12-09 11:22:56 +01:00
Vera Aguilera Puerto
457abf323b Merge branch 'master' into 2021-12-03-remove-IEntity-komm-süsser-todd
# Conflicts:
#	Robust.Shared/GameObjects/EntitySystem.Resolve.cs
2021-12-09 10:39:00 +01:00
Vera Aguilera Puerto
35d81974a8 Resolve no longer debug-asserts that the entity exists. 2021-12-09 01:59:02 +01:00
Paul
c6a9113144 version bump 2021-12-08 19:56:32 +01:00
Paul
c76d1d8c5c Merge branch 'master' of https://github.com/space-wizards/RobustToolbox 2021-12-08 19:55:42 +01:00
Paul
0e289873f5 pvs fixes 2021-12-08 19:55:24 +01:00
Paul
7e59febb47 nukes more resolves 2021-12-08 19:38:59 +01:00
Vera Aguilera Puerto
fb7a231b78 Fix VV crash 2021-12-08 13:33:21 +01:00
Vera Aguilera Puerto
85ded7a5a5 Fix build 2021-12-08 12:23:33 +01:00
Vera Aguilera Puerto
db996b9fb3 A bunch more resolves removed wooo 2021-12-08 11:37:11 +01:00
Vera Aguilera Puerto
52b09cef71 Fix build on RELEASE
EXCEPTION TOLERANCEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaAAaAAaaAAaAAaaAaaAAaaAaaAAAAAAaaaaaaaAAAAaaAAAAAaaaAAAAaaaaAAAAAaaaaaAAAAaaaaaaaAAAAAAAaaaaAAAAAaaaaaaaaAAAAAAAAAAAAAaaaaaaAAAAAAAAAAAaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAa
2021-12-08 11:11:13 +01:00
Vera Aguilera Puerto
26dcef4a11 Fix more useless resolves, makes entity system proxy "Deleted" not throw. 2021-12-08 11:10:11 +01:00
Vera Aguilera Puerto
9133879bd3 Remove more IoCManager IEntityManager resolves
Also adds convenience "Deleted" method to IEntityManager
2021-12-08 10:53:24 +01:00
Paul
76b2909116 removes resolves 2021-12-08 00:09:41 +01:00
Paul
9dab8e77a7 oh nein, kringe 2021-12-07 23:50:05 +01:00
Paul
0464b89cfe nonsense 2021-12-07 23:37:05 +01:00
Vera Aguilera Puerto
877e64f6be Merge pull request #12 from wrexbe/fork123 2021-12-07 18:52:59 +01:00
Wrexbe
4df03f859b Fix release building 2021-12-07 09:50:07 -08:00
metalgearsloth
66435e6022 optimise the shit out of rendertrees 2021-12-07 23:47:12 +11:00
Vera Aguilera Puerto
dc4f3b8f6f Merge pull request #10 from metalgearsloth/2021-12-03-remove-IEntity-komm-süsser-todd 2021-12-07 13:43:42 +01:00
metalgearsloth
8377acb672 more cast memes 2021-12-07 22:14:44 +11:00
metalgearsloth
84c14a4657 Remove redundant IComponent casts 2021-12-07 21:53:44 +11:00
Vera Aguilera Puerto
1d5476e01b More crash fixes 2021-12-06 15:56:14 +01:00
Vera Aguilera Puerto
73d21da0dd entity spawn panel fix 2021-12-06 15:41:38 +01:00
Vera Aguilera Puerto
81672fbe29 Goes in-game now 2021-12-06 15:34:46 +01:00
DrSmugleaf
5655dea1c7 Fix more errors, client and server now build 2021-12-06 14:00:38 +01:00
DrSmugleaf
2616f3cedd Merge branch 'master' of https://github.com/space-wizards/RobustToolbox into 2021-12-03-remove-IEntity-komm-süsser-todd 2021-12-06 12:44:57 +01:00
DrSmugleaf
511f272e96 Merge branch '2021-12-03-remove-IEntity-komm-süsser-todd' of https://github.com/Zumorica/RobustToolbox into 2021-12-03-remove-IEntity-komm-süsser-todd 2021-12-06 12:44:52 +01:00
DrSmugleaf
3c7f87b8c3 Add GetComponentOrNull extension for nullable EUid 2021-12-06 12:44:11 +01:00
DrSmugleaf
7f89aabbb0 Merge branch 'master' of https://github.com/space-wizards/RobustToolbox into 2021-12-03-remove-IEntity-komm-süsser-todd 2021-12-06 12:41:36 +01:00
DrSmugleaf
d6c9420a74 Add an overload for nullable EUid CompOrNull 2021-12-06 12:38:04 +01:00
Vera Aguilera Puerto
43e67c0db9 Add new EntitySystem proxy methods. (#2310) 2021-12-06 12:33:04 +01:00
metalgearsloth
2e1aa16d38 Merge pull request #9 from wrexbe/fixingitagain 2021-12-06 15:48:51 +11:00
metalgearsloth
849bcaeb74 Merge branch '2021-12-03-remove-IEntity-komm-süsser-todd' into fixingitagain 2021-12-06 15:48:44 +11:00
metalgearsloth
dce320d0f5 AttachedEntity stuff 2021-12-06 15:07:12 +11:00
metalgearsloth
d40804b41e testing push perms 2021-12-06 14:27:59 +11:00
Wrexbe
90596c8979 Fixed It 2021-12-05 18:12:56 -08:00
DrSmugleaf
36f582a712 Fix errors 2021-12-05 21:02:03 +01:00
DrSmugleaf
163deff564 Fix 3000 errors 2021-12-05 18:13:33 +01:00
metalgearsloth
ca68041199 Optimise showchunkbb a lot (#2308) 2021-12-05 13:38:40 +11:00
Vera Aguilera Puerto
850d7ba032 More error fixes. 2021-12-04 12:47:09 +01:00
Vera Aguilera Puerto
4c5fefc3b2 Merge pull request #8 from wrexbe/fork3 2021-12-04 11:50:26 +01:00
Wrexbe
84ee039f62 Fix the tests 2021-12-03 18:48:29 -08:00
Vera Aguilera Puerto
a84b481143 Merge pull request #6 from wrexbe/fork2 2021-12-03 23:27:28 +01:00
Wrexbe
69fa2eed64 Building Tests 2021-12-03 14:22:53 -08:00
Vera Aguilera Puerto
71f8275ae2 Merge pull request #4 from 20kdc/susser-todd 2021-12-03 22:46:57 +01:00
20kdc
b89bf5739c Move porting convenience properties 2021-12-03 21:44:14 +00:00
20kdc
e9ec2ee1d3 Susser Todd: Robust.Server, Error Pass 2 2021-12-03 21:33:10 +00:00
Vera Aguilera Puerto
a44a25fb96 Merge pull request #3 from wrexbe/fork1 2021-12-03 22:09:32 +01:00
Wrexbe
5356523305 Client builds 2021-12-03 12:58:37 -08:00
Vera Aguilera Puerto
2004ee9040 Merge pull request #2 from 20kdc/susser-todd 2021-12-03 21:00:09 +01:00
20kdc
7b6d9bd719 Susser Todd: Robust.Client/Robust.Server: Error Pass 1 2021-12-03 19:57:19 +00:00
Vera Aguilera Puerto
287e57f717 Merge pull request #1 from 20kdc/susser-todd 2021-12-03 20:44:39 +01:00
20kdc
add06753e9 Susser Todd: Robust.Shared.Scripting, Error Pass 2021-12-03 19:38:13 +00:00
20kdc
8aa6fb478b Susser Todd: Robust.Shared, Error Pass 2 "It Now Compiles" 2021-12-03 19:36:16 +00:00
20kdc
5e7d617736 Susser Todd: Robust.Shared, Error Pass 1 2021-12-03 18:40:55 +00:00
Vera Aguilera Puerto
fa6c37778e Minor EntityLookup fixes. 2021-12-03 17:50:37 +01:00
Vera Aguilera Puerto
4f92301d25 More manual fixes. 2021-12-03 17:35:20 +01:00
Vera Aguilera Puerto
bd6be35b4d ForAll, Spawn, etc 2021-12-03 17:11:29 +01:00
Vera Aguilera Puerto
c39f6d09eb Lots of manual fixing. More to come. 2021-12-03 17:01:46 +01:00
Vera Aguilera Puerto
5eda1b326d Inline OwnerUid 2021-12-03 16:30:34 +01:00
Vera Aguilera Puerto
5c3b058e81 Literally fucking remove IEntity 2021-12-03 16:08:41 +01:00
Vera Aguilera Puerto
7a06db60cf Inline UID 2021-12-03 15:53:10 +01:00
Vera Aguilera Puerto
aafb15aff5 Perform a slight amount of trolling 2021-12-03 15:35:18 +01:00
Vera Aguilera Puerto
c437fadd25 Inline GetComponentOrNull 2021-12-03 15:32:05 +01:00
Vera Aguilera Puerto
bab37587c5 Inline Name 2021-12-03 15:25:51 +01:00
Vera Aguilera Puerto
c4cd1fb6ee Some manual GetComponentOrNull inlines 2021-12-03 15:09:45 +01:00
Vera Aguilera Puerto
94e6886a85 Inline Transform 2021-12-03 14:20:35 +01:00
Vera Aguilera Puerto
e93ae73e50 Inline TryGetComponent completely, for real 2021-12-03 14:17:01 +01:00
Vera Aguilera Puerto
9739eaa34a Inline TryGetComponent completely 2021-12-03 14:05:36 +01:00
Vera Aguilera Puerto
2aa5e8c07f Version 0.7.21 2021-12-03 13:07:22 +01:00
Vera Aguilera Puerto
f8b2412855 holy fucking shit, fuck exception tolerance only being in RELEASE 2021-12-03 13:07:22 +01:00
metalgearsloth
04782b83ab Add overload to draw rotated position (#2304) 2021-12-03 12:41:58 +01:00
metalgearsloth
e2f2b3a26d Get rid of unused dependency on EffectOverlay 2021-12-03 22:24:23 +11:00
Vera Aguilera Puerto
b44a1cde88 Inline HasComponent entirely 2021-12-03 12:23:18 +01:00
Vera Aguilera Puerto
af964b52ed Inline one TryGetComponent overload 2021-12-03 12:18:25 +01:00
Vera Aguilera Puerto
ebca38b57a Inline one HasComponent overload 2021-12-03 12:18:21 +01:00
Vera Aguilera Puerto
86d05dfa1e Inline GetComponent 2021-12-03 11:55:25 +01:00
Vera Aguilera Puerto
e10bd576ff Inline SendNetworkMessage 2021-12-03 11:47:46 +01:00
Vera Aguilera Puerto
20ff1e86c0 Inline SendMessage 2021-12-03 11:47:24 +01:00
Vera Aguilera Puerto
9d2a2ef4ba Inline ToString 2021-12-03 11:46:53 +01:00
Vera Aguilera Puerto
70d2457514 Inline QueueDelete 2021-12-03 11:43:22 +01:00
Vera Aguilera Puerto
ba8deea700 Inline Delete 2021-12-03 11:43:03 +01:00
Vera Aguilera Puerto
f44d797057 Inline GetAllComponents 2021-12-03 11:42:24 +01:00
Vera Aguilera Puerto
e72d29a677 Remove Debug asserts 2021-12-03 11:39:36 +01:00
Vera Aguilera Puerto
5730465c36 Inline RemoveComponent 2021-12-03 11:34:00 +01:00
Vera Aguilera Puerto
19f50d60c9 Inline AddComponent 2021-12-03 11:33:40 +01:00
Vera Aguilera Puerto
bbf5e92f32 Inline IsValid 2021-12-03 11:32:53 +01:00
Vera Aguilera Puerto
419f4f36ab Inline Paused 2021-12-03 11:30:39 +01:00
Vera Aguilera Puerto
7769252109 Inline Deleted 2021-12-03 11:30:03 +01:00
Vera Aguilera Puerto
3785777983 Remove Initializing 2021-12-03 11:29:13 +01:00
Vera Aguilera Puerto
61aa73ffe3 Fix Prototype inline errors 2021-12-03 11:28:14 +01:00
Vera Aguilera Puerto
ce11bf4564 Inline Initialized 2021-12-03 11:24:56 +01:00
Vera Aguilera Puerto
0cf97780f0 Inline LastModifiedTick 2021-12-03 11:15:40 +01:00
Vera Aguilera Puerto
fc2f6d4910 Inline Description 2021-12-03 11:15:12 +01:00
Vera Aguilera Puerto
69158de99b Inline Prototype 2021-12-03 11:13:58 +01:00
Vera Aguilera Puerto
7bec76d8d1 Inline LifeStage 2021-12-03 11:13:18 +01:00
Vera Aguilera Puerto
6e86f98406 Inline MetaData 2021-12-03 11:12:35 +01:00
Vera Aguilera Puerto
f782e76671 Inline EntityManager 2021-12-03 11:11:52 +01:00
metalgearsloth
5a8464b518 Get rid of grid log spam 2021-12-03 21:08:01 +11:00
Vera Aguilera Puerto
5f1feb9bb1 Version 0.7.20 2021-12-03 11:02:48 +01:00
Vera Aguilera Puerto
196e2bb427 The End of Entity (Komm, Süsser Todd: Part 1.0) (#2295)
Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com>
2021-12-03 11:01:59 +01:00
Acruid
f8bebee904 Remove Entity Transform/MetaData Component Cache (#2296)
Co-authored-by: Vera Aguilera Puerto <gradientvera@outlook.com>
2021-12-03 10:33:06 +01:00
Vera Aguilera Puerto
6515b08b41 Better ToPrettyString with EntityStringRepresentation (#2301) 2021-12-03 10:09:49 +01:00
Vera Aguilera Puerto
6e2f18d0d8 Add joints to RobustServerSimulation. 2021-12-02 12:33:40 +01:00
metalgearsloth
a2ecd63e9d Version: 0.7.19 2021-12-01 18:31:44 +11:00
metalgearsloth
6da9176410 Fixture 2 electric boogaloo (#2297) 2021-12-01 18:31:13 +11:00
metalgearsloth
ae9b771c8c Version: 0.7.18 2021-12-01 13:57:48 +11:00
metalgearsloth
d1e206864c Revert "Quick hotfix for fixtures"
This reverts commit 7316d9e950.
2021-12-01 13:56:32 +11:00
metalgearsloth
f812eb1e27 Revert "Move physics fixtures to its own component (#2220)"
This reverts commit ebc0fc9c60.
2021-12-01 13:54:32 +11:00
metalgearsloth
1dec0dd980 Version: 0.7.17 2021-12-01 13:07:10 +11:00
metalgearsloth
7316d9e950 Quick hotfix for fixtures 2021-12-01 13:06:54 +11:00
metalgearsloth
f7c2305bce Version: 0.7.16 2021-12-01 13:02:55 +11:00
metalgearsloth
ebc0fc9c60 Move physics fixtures to its own component (#2220) 2021-12-01 12:59:42 +11:00
metalgearsloth
d157aab786 Hotfix effect rotation (#2291) 2021-12-01 12:59:28 +11:00
Pieter-Jan Briers
70a5d1bad6 Return of ExpandPvsEvent
Paul has been declared irrational and is extremely coping rn.
2021-11-30 18:35:50 +01:00
Paul
2471bf8b4b we do a little trolling 2021-11-30 18:13:36 +01:00
Paul
e7c5706b04 comments out tests until i fix inventorycode 2021-11-30 18:09:19 +01:00
Paul
7b6d8a1465 fixes at least one test 2021-11-30 18:04:43 +01:00
Paul
31b750bd5b fixes that goddamn AngleSerializerTest 2021-11-30 17:50:52 +01:00
Paul
6ebd0eb4ae fuck mapmanager 2021-11-30 16:59:44 +01:00
Paul
37eac8c73f version 0.7.14 2021-11-30 15:19:13 +01:00
Paul Ritter
44649eea1c pvs refactor (#2247)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
2021-11-30 15:07:08 +01:00
Pieter-Jan Briers
caa0212282 Swap ANGLE-related CVars around.
ANGLE is now off by default, angle_custom_swap_chain on if you set ANGLE.
2021-11-29 18:36:14 +01:00
Pieter-Jan Briers
7f9d08c8f9 Fix duplicate GL init on ANGLE GL context. 2021-11-29 18:35:24 +01:00
Pieter-Jan Briers
1c128e6b74 Return of the VRAM command. 2021-11-29 13:23:58 +01:00
Pieter-Jan Briers
099e7c5c48 IDXGIFactory6::EnumAdapterByGpuPreference for GLContextAngle. 2021-11-29 13:14:59 +01:00
Pieter-Jan Briers
2f76908efb Remove PlatformTarget from WebView project.
Fixes a compiler warning.
2021-11-29 08:35:39 +01:00
Pieter-Jan Briers
5adde7d588 Version: 0.7.13 2021-11-29 00:20:45 +01:00
Pieter-Jan Briers
7b1bb7df47 Expand PVS event (#2287) 2021-11-28 23:43:05 +01:00
Pieter-Jan Briers
8e5eb6ebbb Version: 0.7.12 2021-11-28 23:14:42 +01:00
Pieter-Jan Briers
87ef010348 Remove another NativeLibrary.Load -> exception catch.
This time with Optimus stuff.
2021-11-28 23:13:50 +01:00
wrexbe
229d1c248b Make benchmark debuggable (#2285) 2021-11-28 23:11:58 +01:00
wrexbe
8db606c4e4 Remove duplicate PluralRules.Generator in RobustToolbox.sln (#2286) 2021-11-28 23:03:46 +01:00
Pieter-Jan Briers
6b5181269b Do some trimming for client publishes.
Almost completely removes TerraFX and OpenToolkit.Graphics from client publishes size wise. This cuts publish size in half.
2021-11-28 22:26:05 +01:00
Pieter-Jan Briers
17b84c3520 Update to latest TerraFX, compile custom swapchain ANGLE in. 2021-11-28 22:12:17 +01:00
Pieter-Jan Briers
42875fc101 Remove RobustTaskScheduler entirely.
No longer necessary thanks to #2263.
2021-11-28 15:09:01 +01:00
wrexbe
72a7bc2ae7 Unwrap ParsedMain (#2263) 2021-11-28 15:07:25 +01:00
Leon Friedrich
bef4f75419 Fix AudioParams DataDefinition (#2283) 2021-11-28 15:00:23 +01:00
metalgearsloth
216509f89d Fix multi-subscriber crash for PVS (#2282) 2021-11-28 13:22:13 +01:00
20kdc
b7991204f1 Cleanup NativeLibrary.Load usages by replacing them with TryLoad (#2259)
Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com>
2021-11-28 00:41:53 +01:00
metalgearsloth
02987ac703 Update some hotpaths to use new transform method (#2281) 2021-11-28 00:41:08 +01:00
ike709
5cf8cb262f Updates cefglue (#2258)
Co-authored-by: ike709 <ike709@github.com>
2021-11-28 00:40:10 +01:00
metalgearsloth
49dfca169c Add scale command (#2256) 2021-11-28 00:38:41 +01:00
Acruid
33008a2bce Cannot use 'System.Runtime.CompilerServices.DefaultInterpolatedStringHandler' as a type argument 2021-11-26 19:24:31 -08:00
Acruid
c2e90132c0 Makes the scene command actually check for scene types instead of trying to instantiate random classes like ecs event args. 2021-11-26 08:30:18 -08:00
Vera Aguilera Puerto
9859b5b090 IConsoleCommands get dependencies injected by default now. (#2264)
As the DI gods intended.
2021-11-26 02:28:07 -08:00
metalgearsloth
20aec0a8f9 Add method to get worldpos and worldrot at the same time (#2226)
* Add method to get worldpos and worldrot at the same time

Somewhat of a gain because it halves the number of GetComponent<TransformComponent> + _parent.IsValid() calls

* Remove redundant methods

* Also inverse

* Test

* Import

* Add benchmark

* Delete benchmark
2021-11-26 01:47:51 -08:00
Vera Aguilera Puerto
a9ee78e40d MIDI player looping fix.
SetLoop sets the amount of times to loop a song. If you set it to 1, it'll loop once, if you set it to -1, it'll loop infinitely. This should have been 0 from the start, so it doesn't loop when disabled.
2021-11-25 15:06:55 +01:00
Vera Aguilera Puerto
69f36aac6f Add ToPrettyString method to EntityManager (#2257) 2021-11-24 12:04:18 +01:00
Pieter-Jan Briers
173b41ab9e Send level as log label again in loki. 2021-11-24 08:36:01 +01:00
Vera Aguilera Puerto
7796d7f065 IEntityManager.Clear() actually drops entity system as its xmldoc says. 2021-11-23 11:07:16 +01:00
metalgearsloth
175c111be9 Version: 0.7.11 2021-11-23 18:21:23 +11:00
Acruid
279cc0f83f AppearanceComponent Cleanup (#2253) 2021-11-23 18:16:37 +11:00
DrSmugleaf
b334d927a5 Version: 0.7.10 2021-11-22 19:02:28 +01:00
Vera Aguilera Puerto
bdb9b9af2b Remove PlayerHelpers static class. 2021-11-21 17:58:54 +01:00
Pieter-Jan Briers
5aa634b5eb Don't enable Lidgren UPnP support unless requested.
It wasn't being *used* but discovery was still happening, so...
2021-11-21 17:15:08 +01:00
Javier Guardia Fernández
60d7430fe7 Whitelist CallerArgumentExpressionAttribute, InterpolatedStringHandlerAttribute and IsByRefLikeAttribute (#2251) 2021-11-21 16:04:00 +01:00
Pieter-Jan Briers
a9aeff9b78 Version: 0.7.9 2021-11-21 15:20:41 +01:00
20kdc
e7a0409645 Status host uses net.port by default, add UPnP port forwarding option (#2237)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2021-11-21 15:19:39 +01:00
ShadowCommander
3e718575ff Change Button Pressed to work with non-focus keybinds (#1978) 2021-11-21 14:52:41 +01:00
Acruid
746ec9eab7 Marked PlayerSession find methods on PlayerManager Obsolete (#2244) 2021-11-21 14:51:17 +01:00
Pieter-Jan Briers
60e6ecb0cc Fix loading of command binds from keybinds file. 2021-11-21 14:48:17 +01:00
metalgearsloth
3a00e0d497 Expose physics contacts as an enumerable (#2250) 2021-11-21 18:41:12 +11:00
Leon Friedrich
24a5020b42 fix rotated box contains (#2248) 2021-11-19 22:59:49 -08:00
20kdc
bbb9e94ce9 Arbitrary occluder rotation (#2218)
* All forms of rotated occluders now work

* Utility getters for vector CW/CCW rotations
2021-11-19 15:09:45 -08:00
pointer-to-null
2bea9576f0 Public FpsCounter class. (#2246) 2021-11-19 15:00:14 -08:00
Tomeno
6e0be2b8c9 Clear placementManager when searching/clearing/closing window (#2245)
Co-authored-by: Tomeno <tomeno@lulzsec.co.uk>
2021-11-19 17:46:40 +01:00
Vera Aguilera Puerto
0a348dd1be Version 0.7.8 2021-11-18 19:34:43 +01:00
metalgearsloth
a95283913b Optimise broadphase a LOT for grids moving (#2243)
Previously it went through the entire movebuffer. Now it just uses the localbounds.
2021-11-18 15:40:28 +01:00
metalgearsloth
ebc73a71c2 Optimise GetBroadphases (#2242)
* Optimise GetBroadphases

Only used in public APIs. Should be significantly faster as it doesn't iterate every fixture on a grid.

* Fix the staged stuff
2021-11-18 15:24:18 +01:00
metalgearsloth
05321f0381 Miscellaneous fixes for thrusters (#2239) 2021-11-17 22:42:00 +01:00
Javier Guardia Fernández
6554144c42 Add CollectionsMarshal.AsSpan<T>(List<T>) to the sandbox whitelist (#2241) 2021-11-17 22:37:57 +01:00
20kdc
28e6cdc92f Clyde audio cleanup & fallback if audio device is not available (#2240) 2021-11-17 14:18:04 +01:00
metalgearsloth
2ec715b70e Remove fixture removal error
Client can predict these removals so it just spams the log.
2021-11-17 22:25:36 +11:00
Acruid
424e0768c8 Fixed some code that I forgot to change to help IoC instantiate protected constructors. 2021-11-17 03:10:12 -08:00
Acruid
e1b9327ec0 Misc Engine Tweaks (#2238) 2021-11-17 09:39:18 +01:00
Flipp Syder
80308a799f Adds collapsible widgets (#2199)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2021-11-16 19:09:59 +01:00
20kdc
e30f8f3e69 Async status host handlers to reduce ACZ stalling (#2235) 2021-11-16 13:27:29 +01:00
20kdc
302b910cf3 ACZ: Work around exception handling issues in ManagedHttpListener (#2236) 2021-11-16 13:26:10 +01:00
Pieter-Jan Briers
5a6a16e7dc Upgrade Loki sink to 4.0.0-beta3.
This fixes many bugs with it.
2021-11-15 13:42:19 +01:00
Pieter-Jan Briers
8d84c56a72 Remove testing profile opt stuff. 2021-11-15 11:32:51 +01:00
Pieter-Jan Briers
13f821be5f Grid entity deletion, attempt two.
Previous attempt broke an integration test.
2021-11-15 11:06:30 +01:00
Pieter-Jan Briers
d647ab1c61 Fix some warnings. 2021-11-15 10:53:39 +01:00
Pieter-Jan Briers
b902ef290a Fix directly deleting grid entities.
A bad return statement meant that the associated grid was not being properly deleted.

This fix means the game won't effectively crash PVS if a grid entity is directly deleted.
2021-11-15 10:49:05 +01:00
Pieter-Jan Briers
058d897529 Version: 0.7.7 2021-11-15 02:43:49 +01:00
Javier Guardia Fernández
8e173a7a18 Fix FormattedMessage serialization and add tests (#2233) 2021-11-15 02:42:47 +01:00
20kdc
5443f77526 Automatic Client Zipping (#2225) 2021-11-15 02:42:20 +01:00
20kdc
b406526592 Version update and tag script (#2234) 2021-11-15 02:18:20 +01:00
Pieter-Jan Briers
48697da450 Add version to Robust.Engine.props 2021-11-14 22:46:13 +01:00
Javier Guardia Fernández
06f20ea722 Add a method to get a FormattedMessage with markup tags included (#2223) 2021-11-14 19:42:36 +01:00
metalgearsloth
427378f94d Optimise physics broadphase cache (#2224) 2021-11-14 19:42:02 +01:00
Vera Aguilera Puerto
6cf5021efa Add Resolves to IoCManager and EntitySystemManager (#2231) 2021-11-14 19:41:34 +01:00
Tomeno
ad9bda2efe Adds setters on SS14Window for styling header & title (#2232)
Co-authored-by: T <tomeno@lulzsec.co.uk>
2021-11-14 19:19:12 +01:00
Pieter-Jan Briers
f0bf251acf Improve IUserInterfaceManager.RootControl comment 2021-11-14 19:08:14 +01:00
Pieter-Jan Briers
21f5fed32f Fix IsVisibleInTree calculation with secondary window roots 2021-11-14 19:08:14 +01:00
metalgearsloth
cc67a47c32 Pass around filter dependencies (#2228) 2021-11-14 02:08:09 +01:00
Javier Guardia Fernández
e78e7bacfe Add bool Contains(string, System.StringComparison) to the whitelist (#2230) 2021-11-14 01:37:33 +01:00
Vera Aguilera Puerto
6301008ac3 Revert "adds required check to the yamllinter (#2216)"
This reverts commit 772076826a.
2021-11-12 11:27:53 +01:00
Pieter-Jan Briers
dfd572a0aa Change Microsoft.Data.Sqlite reference to SQLitePCLRaw.bundle_e_sqlite, use version to fix .NET 6 build thing. 2021-11-11 18:23:16 +01:00
Pieter-Jan Briers
97fab99971 You can now bind console commands to keybinds. 2021-11-11 18:05:08 +01:00
Pieter-Jan Briers
25d6bd908b Remove BGRA32 uploads from Clyde.
BGRA uploads are not supported on GLES. Use a shader to swizzle WebViewControl contents instead.
2021-11-11 18:05:04 +01:00
Pieter-Jan Briers
fd5a8bf207 Fix GLContextAngle unbinding GL context when resizing secondary windows. 2021-11-11 18:05:03 +01:00
Paul Ritter
772076826a adds required check to the yamllinter (#2216)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
2021-11-11 17:39:41 +01:00
Vera Aguilera Puerto
52d669e032 Fixes exceptions on shutdown. (#2219) 2021-11-11 17:39:25 +01:00
metalgearsloth
75fc9089c3 Add weld joints and other misc changes for docking (#2197) 2021-11-11 19:54:08 +11:00
Kara D
0972601a43 Revert "entity creation crash fixes"
This reverts commit 603c252c48.
2021-11-10 12:09:31 -07:00
Paul
603c252c48 entity creation crash fixes 2021-11-10 18:57:47 +01:00
Paul
d5b1c044b7 fixes the crash for realsies 2021-11-10 18:34:21 +01:00
metalgearsloth
4600f0531d Fix centre of mass (#2212) 2021-11-10 17:25:38 +01:00
Leon Friedrich
c88498eca9 Add function to directly perform lookups using an EntityLookupComponent (#2200) 2021-11-11 01:12:49 +11:00
Paul Ritter
f15f1eb345 adds gridremovalevent (#2201)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
2021-11-10 23:27:11 +11:00
Pieter-Jan Briers
5be3ced05a UI scaling for WebViewControl. 2021-11-10 11:57:58 +01:00
Pieter-Jan Briers
7f03e88e97 OSWindow not adjusts SetSize when window is resized. 2021-11-10 11:57:12 +01:00
Pieter-Jan Briers
8e3fa3e52d Pass + as command line arg to client/server to execute commands after init. 2021-11-10 02:01:31 +01:00
metalgearsloth
f9ae3e1fc2 Significantly optimise server when PVS disabled (#2196) 2021-11-10 01:38:37 +01:00
mirrorcult
bf9e95fa8a Update README.md 2021-11-09 17:21:37 -07:00
ZorenZal
030a7d265b Added set English language checkbox (#2136) 2021-11-10 01:12:36 +01:00
Pieter-Jan Briers
df70e94743 ununupdated comment 2021-11-10 01:09:06 +01:00
metalgearsloth
d68cd4d7eb Add overlay for physics COM (#2210) 2021-11-10 01:05:08 +01:00
Pieter-Jan Briers
d098881bff Make BVH tree expansion exponential.
Fixes #2215
2021-11-10 00:59:53 +01:00
ike709
b8fbe32c27 Bump Robust.Client.WebView TargetFramework to .NET 6 (#2214) 2021-11-09 20:17:37 +01:00
Pieter-Jan Briers
02d2bd31e7 Update Lidgren submodule; re-enable encryption. 2021-11-09 19:34:55 +01:00
Pieter-Jan Briers
bd0dba0df0 Disable network encryption to bandaid it for .NET 6. 2021-11-09 18:07:59 +01:00
380 changed files with 10639 additions and 6320 deletions

View File

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

View File

@@ -6,4 +6,5 @@
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>
<Import Project="Robust.Engine.Version.props" />
</Project>

View File

@@ -0,0 +1,122 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<!--
Stuff for using ILLink trimming without self-contained deployments.
This is not something officially supported by the .NET SDK currently, but we can simply run ILLink ourselves.
-->
<!--
A lot of stuff taken from Microsoft.NET.ILLink.targets in the SDK files.
-->
<ItemDefinitionGroup>
<RobustLinkRoots>
<Visible>false</Visible>
</RobustLinkRoots>
<RobustLinkAssemblies>
<Visible>false</Visible>
</RobustLinkAssemblies>
</ItemDefinitionGroup>
<Target Name="RobustILLink"
BeforeTargets="ILLink"
Condition="'$(PublishTrimmed)' != 'true' And
'$(RobustILLink)' == 'true'"
DependsOnTargets="_ComputeAssembliesToPostprocessOnPublish">
<ComputeManagedAssemblies Assemblies="@(ResolvedFileToPublish)">
<Output TaskParameter="ManagedAssemblies" ItemName="_ResolvedFileToPublishFiltered" />
</ComputeManagedAssemblies>
<JoinItems Left="@(_ResolvedFileToPublishFiltered)" LeftKey="FileName" LeftMetadata="*"
Right="@(RobustLinkRoots)"
ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_RobustLinkRootsJoined" />
</JoinItems>
<JoinItems Left="@(_ResolvedFileToPublishFiltered)" LeftKey="FileName" LeftMetadata="*"
Right="@(RobustLinkAssemblies)"
ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_RobustLinkAssembliesJoined" />
</JoinItems>
<PropertyGroup>
<TrimMode Condition=" '$(TrimMode)' == '' ">link</TrimMode>
<TrimmerDefaultAction Condition=" '$(TrimmerDefaultAction)' == '' ">copy</TrimmerDefaultAction>
<_ExtraTrimmerArgs>--skip-unresolved true $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
<ILLinkTreatWarningsAsErrors Condition=" '$(ILLinkTreatWarningsAsErrors)' == '' ">$(TreatWarningsAsErrors)</ILLinkTreatWarningsAsErrors>
<TrimmerSingleWarn Condition=" '$(TrimmerSingleWarn)' == '' ">true</TrimmerSingleWarn>
</PropertyGroup>
<ItemGroup>
<RobustAssemblyToLink Include="@(_RobustLinkRootsJoined)">
<TrimMode>Copy</TrimMode>
</RobustAssemblyToLink>
<RobustAssemblyToLink Include="@(_RobustLinkAssembliesJoined)">
<TrimMode>Link</TrimMode>
</RobustAssemblyToLink>
</ItemGroup>
<ItemGroup>
<!-- The linker implicitly picks up PDBs next to input assemblies. We will filter these out of the publish set. -->
<__PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(RobustAssemblyToLink->'%(RelativeDir)%(Filename).pdb')" />
<_PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(__PDBToLink)" />
</ItemGroup>
<ItemGroup>
<_LinkedResolvedFileToPublishCandidate Include="@(RobustAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" />
<_LinkedResolvedFileToPublishCandidate Include="@(_PDBToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" />
</ItemGroup>
<!--<Message Text="@(ResolvedFileToPublish)" Importance="high" />-->
<ItemGroup>
<_TrimmerFeatureSettings Include="@(RuntimeHostConfigurationOption)"
Condition="'%(RuntimeHostConfigurationOption.Trim)' == 'true'" />
</ItemGroup>
<Delete Files="@(_LinkedResolvedFileToPublishCandidate)" />
<ILLink AssemblyPaths="@(RobustAssemblyToLink)"
ReferenceAssemblyPaths="@(ReferencePath)"
RootAssemblyNames="@(RobustLinkRoots)"
TrimMode="Skip"
DefaultAction="$(TrimmerDefaultAction)"
RemoveSymbols="false"
FeatureSettings="@(_TrimmerFeatureSettings)"
CustomData="@(_TrimmerCustomData)"
BeforeFieldInit="$(_TrimmerBeforeFieldInit)"
OverrideRemoval="$(_TrimmerOverrideRemoval)"
UnreachableBodies="$(_TrimmerUnreachableBodies)"
UnusedInterfaces="$(_TrimmerUnusedInterfaces)"
IPConstProp="$(_TrimmerIPConstProp)"
Sealer="$(_TrimmerSealer)"
Warn="$(ILLinkWarningLevel)"
NoWarn="$(NoWarn)"
TreatWarningsAsErrors="$(ILLinkTreatWarningsAsErrors)"
WarningsAsErrors="$(WarningsAsErrors)"
WarningsNotAsErrors="$(WarningsNotAsErrors)"
SingleWarn="$(TrimmerSingleWarn)"
CustomSteps="@(_TrimmerCustomSteps)"
RootDescriptorFiles="@(TrimmerRootDescriptor)"
OutputDirectory="$(IntermediateLinkDir)"
DumpDependencies="$(_TrimmerDumpDependencies)"
ExtraArgs="$(_ExtraTrimmerArgs)"
ToolExe="$(_DotNetHostFileName)"
ToolPath="$(_DotNetHostDirectory)"
ContinueOnError="ErrorAndContinue">
<Output TaskParameter="ExitCode" PropertyName="_ILLinkExitCode" />
</ILLink>
<Touch Files="$(_LinkSemaphore)" AlwaysCreate="true" Condition=" '$(_ILLinkExitCode)' == '0' " />
<ItemGroup>
<_LinkedResolvedFileToPublish Include="@(_LinkedResolvedFileToPublishCandidate)" Condition="Exists('%(Identity)')" />
<ResolvedFileToPublish Remove="@(RobustAssemblyToLink)" />
<ResolvedFileToPublish Remove="@(_PDBToLink)" />
<ResolvedFileToPublish Include="@(_LinkedResolvedFileToPublish)" />
</ItemGroup>
</Target>
</Project>

View File

@@ -18,20 +18,18 @@ namespace OpenToolkit.GraphicsLibraryFramework
NativeLibrary.SetDllImportResolver(typeof(GLFWNative).Assembly, (name, assembly, path) =>
{
// Please keep in sync with what Robust.Shared/DllMapHelper.cs does.
if (name != "glfw3.dll")
{
return IntPtr.Zero;
}
if (OperatingSystem.IsLinux())
{
return NativeLibrary.Load("libglfw.so.3", assembly, path);
}
string rName = null;
if (OperatingSystem.IsLinux()) rName = "libglfw.so.3";
else if (OperatingSystem.IsMacOS()) rName = "libglfw.3.dylib";
if (OperatingSystem.IsMacOS())
{
return NativeLibrary.Load("libglfw.3.dylib", assembly, path);
}
if ((rName != null) && NativeLibrary.TryLoad(rName, assembly, path, out var handle))
return handle;
return IntPtr.Zero;
});

View File

@@ -10,7 +10,7 @@ Use the [content repo](https://github.com/space-wizards/space-station-14) for ac
## Documentation/Wiki
The [HackMD Wiki](https://hackmd.io/@ss14/docs/wiki) has documentation on SS14s content, engine, game design and more. We also have lots of resources for new contributors to the project.
The [wiki](https://docs.spacestation14.io/) has documentation on SS14s content, engine, game design and more. We also have lots of resources for new contributors to the project.
## Contributing

View File

@@ -7,3 +7,8 @@
- type: shader
id: shaded
kind: canvas
- type: shader
id: bgra
kind: source
path: "/Shaders/Internal/bgra.swsl"

View File

@@ -0,0 +1,6 @@
// Swaps B and R channel so you can render BGRA stuff without swizzling at upload time.
// Currently used by CEF.
void fragment() {
COLOR = zTexture(UV).bgra;
}

View File

@@ -1,4 +1,6 @@
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using System;
namespace Robust.Benchmarks
{
@@ -8,7 +10,14 @@ namespace Robust.Benchmarks
// --anyCategories=ctg1,ctg2
public static void Main(string[] args)
{
#if DEBUG
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("\nWARNING: YOU ARE RUNNING A DEBUG BUILD, USE A RELEASE BUILD FOR AN ACCURATE BENCHMARK");
Console.WriteLine("THE DEBUG BUILD IS ONLY GOOD FOR FIXING A CRASHING BENCHMARK\n");
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig());
#else
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
#endif
}
}
}

View File

@@ -301,7 +301,7 @@ namespace {nameSpace}
foreach (var candidateClass in receiver.CandidateClasses)
{
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
var typeSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(candidateClass);
var typeSymbol = model.GetDeclaredSymbol(candidateClass);
var relevantAttribute = typeSymbol.GetAttributes().FirstOrDefault(attr =>
attr.AttributeClass != null &&
attr.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));

View File

@@ -9,14 +9,16 @@ namespace Robust.Client.WebView.Cef
{
internal sealed class ImageBuffer
{
public Image<Bgra32> Buffer { get; private set; } = new(1, 1);
public Image<Rgba32> Buffer { get; private set; } = new(1, 1);
public unsafe void UpdateBuffer(int width, int height, IntPtr buffer, CefRectangle dirtyRect)
{
if (width != Buffer.Width || height != Buffer.Height)
UpdateSize(width, height);
var span = new ReadOnlySpan<Bgra32>((void*) buffer, width * height);
// NOTE: Image data from CEF is actually BGRA32, not RGBA32.
// OpenGL ES does not allow uploading BGRA data, so we pretend it's RGBA32 and use a shader to swizzle it.
var span = new ReadOnlySpan<Rgba32>((void*) buffer, width * height);
ImageSharpExt.Blit(
span,
@@ -28,7 +30,7 @@ namespace Robust.Client.WebView.Cef
private void UpdateSize(int width, int height)
{
Buffer = new Image<Bgra32>(width, height);
Buffer = new Image<Rgba32>(width, height);
}
}
}

View File

@@ -18,7 +18,9 @@ namespace Robust.Client.WebView.Cef
{
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
{
var impl = new ControlImpl(owner);
var shader = _prototypeManager.Index<ShaderPrototype>("bgra");
var shaderInstance = shader.Instance();
var impl = new ControlImpl(owner, shaderInstance);
_dependencyCollection.InjectDependencies(impl);
return impl;
}
@@ -131,10 +133,12 @@ namespace Robust.Client.WebView.Cef
[Dependency] private readonly IInputManager _inputMgr = default!;
public readonly WebViewControl Owner;
private readonly ShaderInstance _shaderInstance;
public ControlImpl(WebViewControl owner)
public ControlImpl(WebViewControl owner, ShaderInstance shaderInstance)
{
Owner = owner;
_shaderInstance = shaderInstance;
}
private const int ScrollSpeed = 50;
@@ -183,7 +187,7 @@ namespace Robust.Client.WebView.Cef
// Create the web browser! And by default, we go to about:blank.
var browser = CefBrowserHost.CreateBrowserSync(info, client, settings, _startUrl);
var texture = _clyde.CreateBlankTexture<Bgra32>(Vector2i.One);
var texture = _clyde.CreateBlankTexture<Rgba32>(Vector2i.One);
_data = new LiveData(texture, client, browser, renderer);
}
@@ -386,7 +390,7 @@ namespace Robust.Client.WebView.Cef
_data.Browser.GetHost().NotifyMoveOrResizeStarted();
_data.Browser.GetHost().WasResized();
_data.Texture.Dispose();
_data.Texture = _clyde.CreateBlankTexture<Bgra32>((Owner.PixelWidth, Owner.PixelHeight));
_data.Texture = _clyde.CreateBlankTexture<Rgba32>((Owner.PixelWidth, Owner.PixelHeight));
}
public void Draw(DrawingHandleScreen handle)
@@ -404,6 +408,7 @@ namespace Robust.Client.WebView.Cef
Math.Min(Owner.PixelWidth, bufImg.Width),
Math.Min(Owner.PixelHeight, bufImg.Height)));
handle.UseShader(_shaderInstance);
handle.DrawTexture(_data.Texture, Vector2.Zero);
}
@@ -533,8 +538,7 @@ namespace Robust.Client.WebView.Cef
if (_control.Owner.Disposed)
return false;
// TODO CEF: Get actual scale factor?
screenInfo.DeviceScaleFactor = 1.0f;
screenInfo.DeviceScaleFactor = _control.Owner.UIScale;
return true;
}

View File

@@ -3,6 +3,7 @@ using System.IO;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
@@ -12,6 +13,7 @@ namespace Robust.Client.WebView.Cef
private CefApp _app = default!;
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public void Initialize()
{

View File

@@ -3,8 +3,7 @@
<Import Project="..\MSBuild\Robust.Engine.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>WinExe</OutputType>
</PropertyGroup>

View File

@@ -2,6 +2,7 @@ using System;
using JetBrains.Annotations;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Client.Animations
{
@@ -18,8 +19,8 @@ namespace Robust.Client.Animations
throw new InvalidOperationException("Must set parameters to non-null values.");
}
var entity = (IEntity) context;
var component = entity.GetComponent(ComponentType);
var entity = (EntityUid) context;
var component = IoCManager.Resolve<IEntityManager>().GetComponent(entity, ComponentType);
if (component is IAnimationProperties properties)
{

View File

@@ -25,7 +25,7 @@ namespace Robust.Client.Animations
public override (int KeyFrameIndex, float FramePlayingTime)
AdvancePlayback(object context, int prevKeyFrameIndex, float prevPlayingTime, float frameTime)
{
var entity = (IEntity) context;
var entity = (EntityUid) context;
var playingTime = prevPlayingTime + frameTime;
var keyFrameIndex = prevKeyFrameIndex;

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Robust.Client.Animations
@@ -38,8 +39,8 @@ namespace Robust.Client.Animations
{
DebugTools.AssertNotNull(LayerKey);
var entity = (IEntity) context;
var sprite = entity.GetComponent<ISpriteComponent>();
var entity = (EntityUid) context;
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(entity);
var playingTime = prevPlayingTime + frameTime;
var keyFrameIndex = prevKeyFrameIndex;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
@@ -299,7 +299,7 @@ namespace Robust.Client.Audio.Midi
}
else if (renderer.TrackingEntity != null)
{
mapPos = renderer.TrackingEntity.Transform.MapPosition;
mapPos = _entityManager.GetComponent<TransformComponent>(renderer.TrackingEntity.Value).MapPosition;
}
if (mapPos != null)
@@ -335,7 +335,7 @@ namespace Robust.Client.Audio.Midi
if (renderer.TrackingEntity != null)
{
renderer.Source.SetVelocity(renderer.TrackingEntity.GlobalLinearVelocity());
renderer.Source.SetVelocity(renderer.TrackingEntity.Value.GlobalLinearVelocity());
}
if (float.IsNaN(pos.Position.X) || float.IsNaN(pos.Position.Y))

View File

@@ -150,7 +150,7 @@ namespace Robust.Client.Audio.Midi
/// The entity whose position will be used for positional audio.
/// This is only used if <see cref="Mono"/> is set to True.
/// </summary>
IEntity? TrackingEntity { get; set; }
EntityUid? TrackingEntity { get; set; }
/// <summary>
/// The position that will be used for positional audio.
@@ -297,7 +297,7 @@ namespace Robust.Client.Audio.Midi
set
{
lock (_playerStateLock)
_player?.SetLoop(value ? -1 : 1);
_player?.SetLoop(value ? -1 : 0);
_loopMidi = value;
}
}
@@ -306,7 +306,7 @@ namespace Robust.Client.Audio.Midi
public bool VolumeBoost { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public IEntity? TrackingEntity { get; set; } = null;
public EntityUid? TrackingEntity { get; set; } = null;
[ViewVariables(VVAccess.ReadWrite)]
public EntityCoordinates? TrackingCoordinates { get; set; } = null;

View File

@@ -5,6 +5,7 @@ using Robust.Client.Debugging;
using Robust.Client.GameObjects;
using Robust.Client.GameStates;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Audio;
using Robust.Client.Graphics.Clyde;
using Robust.Client.Input;
using Robust.Client.Map;
@@ -82,8 +83,9 @@ namespace Robust.Client
case GameController.DisplayMode.Headless:
IoCManager.Register<IClyde, ClydeHeadless>();
IoCManager.Register<IClipboardManager, ClydeHeadless>();
IoCManager.Register<IClydeAudio, ClydeHeadless>();
IoCManager.Register<IClydeInternal, ClydeHeadless>();
IoCManager.Register<IClydeAudio, ClydeAudioHeadless>();
IoCManager.Register<IClydeAudioInternal, ClydeAudioHeadless>();
IoCManager.Register<IInputManager, InputManager>();
IoCManager.Register<IFileDialogManager, DummyFileDialogManager>();
IoCManager.Register<IUriOpener, UriOpenerDummy>();
@@ -91,8 +93,9 @@ namespace Robust.Client
case GameController.DisplayMode.Clyde:
IoCManager.Register<IClyde, Clyde>();
IoCManager.Register<IClipboardManager, Clyde>();
IoCManager.Register<IClydeAudio, Clyde>();
IoCManager.Register<IClydeInternal, Clyde>();
IoCManager.Register<IClydeAudio, FallbackProxyClydeAudio>();
IoCManager.Register<IClydeAudioInternal, FallbackProxyClydeAudio>();
IoCManager.Register<IInputManager, ClydeInputManager>();
IoCManager.Register<IFileDialogManager, FileDialogManager>();
IoCManager.Register<IUriOpener, UriOpener>();

View File

@@ -4,177 +4,186 @@ using Robust.Shared;
using Robust.Shared.Utility;
using C = System.Console;
namespace Robust.Client
namespace Robust.Client;
internal sealed class CommandLineArgs
{
internal sealed class CommandLineArgs
public MountOptions MountOptions { get; }
public bool Headless { get; }
public bool SelfContained { get; }
public bool Connect { get; }
public string ConnectAddress { get; }
public string? Ss14Address { get; }
public bool Launcher { get; }
public string? Username { get; }
public IReadOnlyCollection<(string key, string value)> CVars { get; }
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
public IReadOnlyList<string> ExecCommands { get; set; }
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
// Also I don't like spending 100ms parsing command line args. Do you?
public static bool TryParse(IReadOnlyList<string> args, [NotNullWhen(true)] out CommandLineArgs? parsed)
{
public MountOptions MountOptions { get; }
public bool Headless { get; }
public bool SelfContained { get; }
public bool Connect { get; }
public string ConnectAddress { get; }
public string? Ss14Address { get; }
public bool Launcher { get; }
public string? Username { get; }
public IReadOnlyCollection<(string key, string value)> CVars { get; }
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
parsed = null;
var headless = false;
var selfContained = false;
var connect = false;
var connectAddress = "localhost";
string? ss14Address = null;
var launcher = false;
string? username = null;
var cvars = new List<(string, string)>();
var logLevels = new List<(string, string)>();
var mountOptions = new MountOptions();
var execCommands = new List<string>();
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
// Also I don't like spending 100ms parsing command line args. Do you?
public static bool TryParse(IReadOnlyList<string> args, [NotNullWhen(true)] out CommandLineArgs? parsed)
using var enumerator = args.GetEnumerator();
while (enumerator.MoveNext())
{
parsed = null;
var headless = false;
var selfContained = false;
var connect = false;
var connectAddress = "localhost";
string? ss14Address = null;
var launcher = false;
string? username = null;
var cvars = new List<(string, string)>();
var logLevels = new List<(string, string)>();
var mountOptions = new MountOptions();
using var enumerator = args.GetEnumerator();
while (enumerator.MoveNext())
var arg = enumerator.Current;
if (arg == "--connect")
{
var arg = enumerator.Current;
if (arg == "--connect")
connect = true;
}
else if (arg == "--connect-address")
{
if (!enumerator.MoveNext())
{
connect = true;
}
else if (arg == "--connect-address")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing connection address.");
return false;
}
connectAddress = enumerator.Current;
}
else if (arg == "--ss14-address")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing SS14 address.");
return false;
}
ss14Address = enumerator.Current;
}
else if (arg == "--self-contained")
{
selfContained = true;
}
else if (arg == "--launcher")
{
launcher = true;
}
else if (arg == "--headless")
{
headless = true;
}
else if (arg == "--username")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing username.");
return false;
}
username = enumerator.Current;
}
else if (arg == "--cvar")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing cvar value.");
return false;
}
var cvar = enumerator.Current;
DebugTools.AssertNotNull(cvar);
var pos = cvar.IndexOf('=');
if (pos == -1)
{
C.WriteLine("Expected = in cvar.");
return false;
}
cvars.Add((cvar[..pos], cvar[(pos + 1)..]));
}
else if (arg == "--mount-zip")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing mount path");
return false;
}
mountOptions.ZipMounts.Add(enumerator.Current);
}
else if (arg == "--mount-dir")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing mount path");
return false;
}
mountOptions.DirMounts.Add(enumerator.Current);
}
else if (arg == "--loglevel")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing loglevel sawmill.");
return false;
}
var loglevel = enumerator.Current;
DebugTools.AssertNotNull(loglevel);
var pos = loglevel.IndexOf('=');
if (pos == -1)
{
C.WriteLine("Expected = in loglevel.");
return false;
}
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
}
else if (arg == "--help")
{
PrintHelp();
C.WriteLine("Missing connection address.");
return false;
}
else
{
C.WriteLine("Unknown argument: {0}", arg);
}
connectAddress = enumerator.Current;
}
else if (arg == "--ss14-address")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing SS14 address.");
return false;
}
parsed = new CommandLineArgs(
headless,
selfContained,
connect,
launcher,
username,
cvars,
logLevels,
connectAddress,
ss14Address,
mountOptions);
ss14Address = enumerator.Current;
}
else if (arg == "--self-contained")
{
selfContained = true;
}
else if (arg == "--launcher")
{
launcher = true;
}
else if (arg == "--headless")
{
headless = true;
}
else if (arg == "--username")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing username.");
return false;
}
return true;
username = enumerator.Current;
}
else if (arg == "--cvar")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing cvar value.");
return false;
}
var cvar = enumerator.Current;
DebugTools.AssertNotNull(cvar);
var pos = cvar.IndexOf('=');
if (pos == -1)
{
C.WriteLine("Expected = in cvar.");
return false;
}
cvars.Add((cvar[..pos], cvar[(pos + 1)..]));
}
else if (arg == "--mount-zip")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing mount path");
return false;
}
mountOptions.ZipMounts.Add(enumerator.Current);
}
else if (arg == "--mount-dir")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing mount path");
return false;
}
mountOptions.DirMounts.Add(enumerator.Current);
}
else if (arg == "--loglevel")
{
if (!enumerator.MoveNext())
{
C.WriteLine("Missing loglevel sawmill.");
return false;
}
var loglevel = enumerator.Current;
DebugTools.AssertNotNull(loglevel);
var pos = loglevel.IndexOf('=');
if (pos == -1)
{
C.WriteLine("Expected = in loglevel.");
return false;
}
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
}
else if (arg == "--help")
{
PrintHelp();
return false;
}
else if (arg.StartsWith("+"))
{
execCommands.Add(arg[1..]);
}
else
{
C.WriteLine("Unknown argument: {0}", arg);
}
}
private static void PrintHelp()
{
C.WriteLine(@"
parsed = new CommandLineArgs(
headless,
selfContained,
connect,
launcher,
username,
cvars,
logLevels,
connectAddress,
ss14Address,
mountOptions,
execCommands);
return true;
}
private static void PrintHelp()
{
C.WriteLine(@"
Usage: Robust.Client [options] [+command [+command]]
Options:
--headless Run without graphics/audio/input.
--self-contained Store data relative to executable instead of user-global locations.
@@ -189,30 +198,34 @@ Options:
--mount-dir Resource directory to mount.
--mount-zip Resource zip to mount.
--help Display this help text and exit.
");
}
private CommandLineArgs(
bool headless,
bool selfContained,
bool connect,
bool launcher,
string? username,
IReadOnlyCollection<(string key, string value)> cVars,
IReadOnlyCollection<(string key, string value)> logLevels,
string connectAddress, string? ss14Address,
MountOptions mountOptions)
{
Headless = headless;
SelfContained = selfContained;
Connect = connect;
Launcher = launcher;
Username = username;
CVars = cVars;
LogLevels = logLevels;
ConnectAddress = connectAddress;
Ss14Address = ss14Address;
MountOptions = mountOptions;
}
+command: You can pass a set of commands, prefixed by +,
to be executed in the console in order after the game has finished initializing.
");
}
private CommandLineArgs(
bool headless,
bool selfContained,
bool connect,
bool launcher,
string? username,
IReadOnlyCollection<(string key, string value)> cVars,
IReadOnlyCollection<(string key, string value)> logLevels,
string connectAddress, string? ss14Address,
MountOptions mountOptions,
IReadOnlyList<string> execCommands)
{
Headless = headless;
SelfContained = selfContained;
Connect = connect;
Launcher = launcher;
Username = username;
CVars = cVars;
LogLevels = logLevels;
ConnectAddress = connectAddress;
Ss14Address = ss14Address;
MountOptions = mountOptions;
ExecCommands = execCommands;
}
}

View File

@@ -21,13 +21,12 @@ namespace Robust.Client.Console.Commands
return;
}
var entityUid = EntityUid.Parse(args[0]);
var entity = EntityUid.Parse(args[0]);
var componentName = args[1];
var compFactory = IoCManager.Resolve<IComponentFactory>();
var entityManager = IoCManager.Resolve<IEntityManager>();
var entity = entityManager.GetEntity(entityUid);
var component = (Component) compFactory.GetComponent(componentName);
component.Owner = entity;

View File

@@ -15,15 +15,15 @@ namespace Robust.Client.Console.Commands
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
if (player?.ControlledEntity == null)
var controlled = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
if (controlled == EntityUid.Invalid)
{
shell.WriteLine("You don't have an attached entity.");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
entityManager.SpawnEntity(args[0], player.ControlledEntity.Transform.Coordinates);
entityManager.SpawnEntity(args[0], entityManager.GetComponent<TransformComponent>(controlled).Coordinates);
}
}
}

View File

@@ -4,13 +4,12 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Text;
using System.Text.RegularExpressions;
using Robust.Client.Input;
using Robust.Client.Debugging;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -41,9 +40,9 @@ namespace Robust.Client.Console.Commands
{
var entityManager = IoCManager.Resolve<IEntityManager>();
foreach (var e in entityManager.GetEntities().OrderBy(e => e.Uid))
foreach (var e in entityManager.GetEntities().OrderBy(e => e))
{
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.");
shell.WriteLine($"entity {e}, {entityManager.GetComponent<MetaDataComponent>(e).EntityPrototype?.ID}, {entityManager.GetComponent<TransformComponent>(e).Coordinates}.");
}
}
}
@@ -238,15 +237,15 @@ namespace Robust.Client.Console.Commands
var uid = EntityUid.Parse(args[0]);
var entmgr = IoCManager.Resolve<IEntityManager>();
if (!entmgr.TryGetEntity(uid, out var entity))
if (!entmgr.EntityExists(uid))
{
shell.WriteError("That entity does not exist. Sorry lad.");
return;
}
shell.WriteLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
shell.WriteLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
foreach (var component in entity.GetAllComponents())
var meta = entmgr.GetComponent<MetaDataComponent>(uid);
shell.WriteLine($"{uid}: {meta.EntityPrototype?.ID}/{meta.EntityName}");
shell.WriteLine($"init/del/lmt: {meta.EntityInitialized}/{meta.EntityDeleted}/{meta.EntityLastModifiedTick}");
foreach (var component in entmgr.GetComponents(uid))
{
shell.WriteLine(component.ToString() ?? "");
if (component is IComponentDebug debug)

View File

@@ -24,6 +24,9 @@ namespace Robust.Client.Console.Commands
case "aabbs":
system.Flags ^= PhysicsDebugFlags.AABBs;
break;
case "com":
system.Flags ^= PhysicsDebugFlags.COM;
break;
case "contactnormals":
system.Flags ^= PhysicsDebugFlags.ContactNormals;
break;

View File

@@ -79,13 +79,13 @@ namespace Robust.Client.Debugging
foreach (var ent in grid.GetAnchoredEntities(spot))
{
if (!EntityManager.TryGetEntity(ent, out var entity))
if (EntityManager.TryGetComponent<MetaDataComponent>(ent, out var meta))
{
text.AppendLine($"uid: {ent}, invalid");
text.AppendLine($"uid: {ent}, {meta.EntityName}");
}
else
{
text.AppendLine($"uid: {ent}, {entity.Name}");
text.AppendLine($"uid: {ent}, invalid");
}
}

View File

@@ -12,6 +12,7 @@ namespace Robust.Client.Debugging
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private bool _debugPositions;
@@ -30,7 +31,7 @@ namespace Robust.Client.Debugging
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
{
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _eyeManager));
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _eyeManager, _entityManager));
}
else
{
@@ -43,13 +44,15 @@ namespace Robust.Client.Debugging
{
private readonly IEntityLookup _lookup;
private readonly IEyeManager _eyeManager;
private readonly IEntityManager _entityManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager)
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager, IEntityManager entityManager)
{
_lookup = lookup;
_eyeManager = eyeManager;
_entityManager = entityManager;
}
protected internal override void Draw(in OverlayDrawArgs args)
@@ -61,7 +64,7 @@ namespace Robust.Client.Debugging
foreach (var entity in _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, viewport))
{
var transform = entity.Transform;
var transform = _entityManager.GetComponent<TransformComponent>(entity);
var center = transform.WorldPosition;
var worldRotation = transform.WorldRotation;

View File

@@ -162,6 +162,7 @@ namespace Robust.Client.Debugging
ShapeInfo = 1 << 3,
Joints = 1 << 4,
AABBs = 1 << 5,
COM = 1 << 6,
}
internal sealed class PhysicsDebugOverlay : Overlay
@@ -190,17 +191,17 @@ namespace Robust.Client.Debugging
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
private void DrawWorld(DrawingHandleWorld worldHandle)
private void DrawWorld(DrawingHandleWorld worldHandle, OverlayDrawArgs args)
{
var viewport = _eyeManager.GetWorldViewport();
var viewBounds = _eyeManager.GetWorldViewbounds();
var viewBounds = args.WorldBounds;
var viewAABB = args.WorldAABB;
var mapId = _eyeManager.CurrentMap;
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0 && !viewport.IsEmpty())
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0)
{
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
{
if (physBody.Owner.HasComponent<MapGridComponent>()) continue;
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
var xform = physBody.GetTransform();
@@ -237,11 +238,36 @@ namespace Robust.Client.Debugging
}
}
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.AABBs) != 0 && !viewport.IsEmpty())
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.COM) != 0)
{
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
{
if (physBody.Owner.HasComponent<MapGridComponent>()) continue;
Color color;
const float Alpha = 0.25f;
float size;
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner))
{
color = Color.Orange.WithAlpha(Alpha);
size = 1f;
}
else
{
color = Color.Purple.WithAlpha(Alpha);
size = 0.2f;
}
var transform = physBody.GetTransform();
worldHandle.DrawCircle(Transform.Mul(transform, physBody.LocalCenter), size, color);
}
}
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.AABBs) != 0)
{
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
{
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
var xform = physBody.GetTransform();
@@ -270,8 +296,8 @@ namespace Robust.Client.Debugging
foreach (var jointComponent in _entityManager.EntityQuery<JointComponent>(true))
{
if (jointComponent.JointCount == 0 ||
!_entityManager.TryGetComponent(jointComponent.Owner.Uid, out TransformComponent? xf1) ||
!viewport.Contains(xf1.WorldPosition)) continue;
!_entityManager.TryGetComponent(jointComponent.Owner, out TransformComponent? xf1) ||
!viewAABB.Contains(xf1.WorldPosition)) continue;
foreach (var (_, joint) in jointComponent.Joints)
{
@@ -312,7 +338,7 @@ namespace Robust.Client.Debugging
}
}
private void DrawScreen(DrawingHandleScreen screenHandle)
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
{
var mapId = _eyeManager.CurrentMap;
var mousePos = _inputManager.MouseScreenPosition;
@@ -324,7 +350,7 @@ namespace Robust.Client.Debugging
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, bounds))
{
if (physBody.Owner.HasComponent<MapGridComponent>()) continue;
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
hoverBodies.Add(physBody);
}
@@ -359,10 +385,10 @@ namespace Robust.Client.Debugging
switch (args.Space)
{
case OverlaySpace.ScreenSpace:
DrawScreen((DrawingHandleScreen) args.DrawingHandle);
DrawScreen((DrawingHandleScreen) args.DrawingHandle, args);
break;
case OverlaySpace.WorldSpace:
DrawWorld((DrawingHandleWorld) args.DrawingHandle);
DrawWorld((DrawingHandleWorld) args.DrawingHandle, args);
break;
}
}
@@ -382,8 +408,8 @@ namespace Robust.Client.Debugging
if (edge.OneSided)
{
worldHandle.DrawCircle(v1, 0.5f, color);
worldHandle.DrawCircle(v2, 0.5f, color);
worldHandle.DrawCircle(v1, 0.1f, color);
worldHandle.DrawCircle(v2, 0.1f, color);
}
break;
@@ -416,11 +442,45 @@ namespace Robust.Client.Debugging
var p1 = matrix1.Transform(joint.LocalAnchorA);
var p2 = matrix2.Transform(joint.LocalAnchorB);
var xfa = new Transform(xf1, xform1.WorldRotation);
var xfb = new Transform(xf2, xform2.WorldRotation);
switch (joint)
{
case DistanceJoint:
worldHandle.DrawLine(xf1, xf2, JointColor);
break;
case PrismaticJoint prisma:
var pA = Transform.Mul(xfa, joint.LocalAnchorA);
var pB = Transform.Mul(xfb, joint.LocalAnchorB);
var axis = Transform.Mul(xfa.Quaternion2D, prisma._localXAxisA);
Color c1 = new(0.7f, 0.7f, 0.7f);
Color c2 = new(0.3f, 0.9f, 0.3f);
Color c3 = new(0.9f, 0.3f, 0.3f);
Color c4 = new(0.3f, 0.3f, 0.9f);
Color c5 = new(0.4f, 0.4f, 0.4f);
worldHandle.DrawLine(pA, pB, c5);
if (prisma.EnableLimit)
{
var lower = pA + axis * prisma.LowerTranslation;
var upper = pA + axis * prisma.UpperTranslation;
var perp = Transform.Mul(xfa.Quaternion2D, prisma._localYAxisA);
worldHandle.DrawLine(lower, upper, c1);
worldHandle.DrawLine(lower - perp * 0.5f, lower + perp * 0.5f, c2);
worldHandle.DrawLine(upper - perp * 0.5f, upper + perp * 0.5f, c3);
}
else
{
worldHandle.DrawLine(pA - axis * 1.0f, pA + axis * 1.0f, c1);
}
worldHandle.DrawCircle(pA, 0.5f, c1);
worldHandle.DrawCircle(pB, 0.5f, c4);
break;
default:
worldHandle.DrawLine(xf1, p1, JointColor);
worldHandle.DrawLine(p1, p2, JointColor);

View File

@@ -59,6 +59,7 @@ namespace Robust.Client
[Dependency] private readonly IViewVariablesManagerInternal _viewVariablesManager = default!;
[Dependency] private readonly IDiscordRichPresence _discord = default!;
[Dependency] private readonly IClydeInternal _clyde = default!;
[Dependency] private readonly IClydeAudioInternal _clydeAudio = default!;
[Dependency] private readonly IFontManagerInternal _fontManager = default!;
[Dependency] private readonly IModLoaderInternal _modLoader = default!;
[Dependency] private readonly IScriptClient _scriptClient = default!;
@@ -86,6 +87,7 @@ namespace Robust.Client
internal bool StartupContinue(DisplayMode displayMode)
{
_clyde.InitializePostWindowing();
_clydeAudio.InitializePostWindowing();
_clyde.SetWindowTitle(Options.DefaultWindowTitle);
_taskManager.Initialize();
@@ -198,6 +200,8 @@ namespace Robust.Client
_client.ConnectToServer(LaunchState.ConnectEndpoint);
}
ProgramShared.RunExecCommands(_console, _commandLineArgs?.ExecCommands);
return true;
}
@@ -437,6 +441,7 @@ namespace Robust.Client
private void Update(FrameEventArgs frameEventArgs)
{
_webViewHook?.Update();
_clydeAudio.FrameProcess(frameEventArgs);
_clyde.FrameProcess(frameEventArgs);
_modLoader.BroadcastUpdate(ModUpdateLevel.FramePreEngine, frameEventArgs);
_stateManager.FrameUpdate(frameEventArgs);
@@ -535,6 +540,7 @@ namespace Robust.Client
IoCManager.Resolve<IEntityLookup>().Shutdown();
_entityManager.Shutdown();
_clyde.Shutdown();
_clydeAudio.Shutdown();
}
private sealed record ResourceManifestData(string[] Modules);

View File

@@ -27,7 +27,6 @@ namespace Robust.Client.GameObjects
RegisterClass<ClientOccluderComponent>();
RegisterClass<OccluderTreeComponent>();
RegisterClass<EyeComponent>();
RegisterClass<AppearanceComponent>();
RegisterClass<AnimationPlayerComponent>();
RegisterClass<TimerComponent>();

View File

@@ -33,19 +33,19 @@ namespace Robust.Client.GameObjects
base.Initialize();
}
IEntity IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid? uid)
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid uid)
{
return base.CreateEntity(prototypeName, uid);
}
void IClientEntityManagerInternal.InitializeEntity(IEntity entity)
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity)
{
base.InitializeEntity((Entity)entity);
base.InitializeEntity(entity);
}
void IClientEntityManagerInternal.StartEntity(IEntity entity)
void IClientEntityManagerInternal.StartEntity(EntityUid entity)
{
base.StartEntity((Entity)entity);
base.StartEntity(entity);
}
#region IEntityNetworkManager impl
@@ -107,7 +107,7 @@ namespace Robust.Client.GameObjects
/// <inheritdoc />
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public void SendComponentNetworkMessage(INetChannel? channel, IEntity entity, IComponent component, ComponentMessage message)
public void SendComponentNetworkMessage(INetChannel? channel, EntityUid entity, IComponent component, ComponentMessage message)
{
var netId = ComponentFactory.GetRegistration(component.GetType()).NetID;
@@ -116,7 +116,7 @@ namespace Robust.Client.GameObjects
var msg = _networkManager.CreateNetMessage<MsgEntity>();
msg.Type = EntityMessageType.ComponentMessage;
msg.EntityUid = entity.Uid;
msg.EntityUid = entity;
msg.NetId = netId.Value;
msg.ComponentMessage = message;
msg.SourceTick = _gameTiming.CurTick;

View File

@@ -1,161 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using YamlDotNet.RepresentationModel;
namespace Robust.Client.GameObjects
{
[ComponentReference(typeof(SharedAppearanceComponent))]
public sealed class AppearanceComponent : SharedAppearanceComponent
{
[ViewVariables]
private Dictionary<object, object> data = new();
[ViewVariables]
[DataField("visuals")]
internal List<AppearanceVisualizer> Visualizers = new();
[ViewVariables]
private bool _appearanceDirty;
public override void SetData(string key, object value)
{
SetData(key, value);
}
public override void SetData(Enum key, object value)
{
SetData(key, value);
}
public override T GetData<T>(string key)
{
return (T) data[key];
}
public override T GetData<T>(Enum key)
{
return (T) data[key];
}
internal T GetData<T>(object key)
{
return (T) data[key];
}
public override bool TryGetData<T>(Enum key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
public override bool TryGetData<T>(string key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
internal bool TryGetData<T>(object key, [NotNullWhen(true)] out T data)
{
if (this.data.TryGetValue(key, out var dat))
{
data = (T) dat;
return true;
}
data = default!;
return false;
}
private void SetData(object key, object value)
{
if (data.TryGetValue(key, out var existing) && existing.Equals(value)) return;
data[key] = value;
MarkDirty();
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState is not AppearanceComponentState actualState)
return;
var stateDiff = data.Count != actualState.Data.Count;
if (!stateDiff)
{
foreach (var (key, value) in data)
{
if (!actualState.Data.TryGetValue(key, out var stateValue) ||
!value.Equals(stateValue))
{
stateDiff = true;
break;
}
}
}
if (!stateDiff) return;
data = actualState.Data;
MarkDirty();
}
internal void MarkDirty()
{
if (_appearanceDirty)
{
return;
}
EntitySystem.Get<AppearanceSystem>().EnqueueUpdate(this);
_appearanceDirty = true;
}
internal void UnmarkDirty()
{
_appearanceDirty = false;
}
protected override void Initialize()
{
base.Initialize();
foreach (var visual in Visualizers)
{
visual.InitializeEntity(Owner);
}
MarkDirty();
}
}
/// <summary>
/// Handles the visualization of data inside of an appearance component.
/// Implementations of this class are NOT bound to a specific entity, they are flyweighted across multiple.
/// </summary>
[ImplicitDataDefinitionForInheritors]
public abstract class AppearanceVisualizer
{
/// <summary>
/// Initializes an entity to be managed by this appearance controller.
/// DO NOT assume this is your only entity. Visualizers are shared.
/// </summary>
public virtual void InitializeEntity(IEntity entity)
{
}
/// <summary>
/// Called whenever appearance data for an entity changes.
/// Update its visuals here.
/// </summary>
/// <param name="component">The appearance component of the entity that might need updating.</param>
public virtual void OnChangeData(AppearanceComponent component)
{
}
}
}

View File

@@ -0,0 +1,29 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Client.GameObjects;
/// <summary>
/// Handles the visualization of data inside of an appearance component.
/// Implementations of this class are NOT bound to a specific entity, they are flyweighted across multiple.
/// </summary>
[ImplicitDataDefinitionForInheritors]
public abstract class AppearanceVisualizer
{
/// <summary>
/// Initializes an entity to be managed by this appearance controller.
/// DO NOT assume this is your only entity. Visualizers are shared.
/// </summary>
public virtual void InitializeEntity(EntityUid entity)
{
}
/// <summary>
/// Called whenever appearance data for an entity changes.
/// Update its visuals here.
/// </summary>
/// <param name="component">The appearance component of the entity that might need updating.</param>
public virtual void OnChangeData(AppearanceComponent component)
{
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Robust.Client.GameObjects;
/// <summary>
/// This is the client instance of <see cref="AppearanceComponent"/>.
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(AppearanceComponent))]
public sealed class ClientAppearanceComponent : AppearanceComponent
{
[ViewVariables]
private bool _appearanceDirty;
[ViewVariables]
[DataField("visuals")]
internal List<AppearanceVisualizer> Visualizers = new();
protected override void MarkDirty()
{
if (_appearanceDirty)
return;
EntitySystem.Get<AppearanceSystem>().EnqueueUpdate(this);
_appearanceDirty = true;
}
protected override void Initialize()
{
base.Initialize();
foreach (var visual in Visualizers)
{
visual.InitializeEntity(Owner);
}
MarkDirty();
}
internal void UnmarkDirty()
{
_appearanceDirty = false;
}
}

View File

@@ -3,8 +3,6 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -14,6 +12,7 @@ namespace Robust.Client.GameObjects
public class EyeComponent : SharedEyeComponent
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
/// <inheritdoc />
public override string Name => "Eye";
@@ -118,7 +117,7 @@ namespace Robust.Client.GameObjects
_eye = new Eye
{
Position = Owner.Transform.MapPosition,
Position = _entityManager.GetComponent<TransformComponent>(Owner).MapPosition,
Zoom = _setZoomOnInitialize,
DrawFov = _setDrawFovOnInitialize
};
@@ -166,7 +165,7 @@ namespace Robust.Client.GameObjects
public void UpdateEyePosition()
{
if (_eye == null) return;
var mapPos = Owner.Transform.MapPosition;
var mapPos = _entityManager.GetComponent<TransformComponent>(Owner).MapPosition;
_eye.Position = new MapCoordinates(mapPos.Position, mapPos.MapId);
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
@@ -12,6 +11,7 @@ namespace Robust.Client.GameObjects
internal sealed class ClientOccluderComponent : OccluderComponent
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[ViewVariables] private (GridId, Vector2i) _lastPosition;
[ViewVariables] internal OccluderDir Occluding { get; private set; }
@@ -32,7 +32,7 @@ namespace Robust.Client.GameObjects
{
base.Startup();
if (Owner.Transform.Anchored)
if (_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
{
AnchorStateChanged();
}
@@ -42,11 +42,11 @@ namespace Robust.Client.GameObjects
{
SendDirty();
if(!Owner.Transform.Anchored)
if(!_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
return;
var grid = _mapManager.GetGrid(Owner.Transform.GridID);
_lastPosition = (Owner.Transform.GridID, grid.TileIndicesFor(Owner.Transform.Coordinates));
var grid = _mapManager.GetGrid(_entityManager.GetComponent<TransformComponent>(Owner).GridID);
_lastPosition = (_entityManager.GetComponent<TransformComponent>(Owner).GridID, grid.TileIndicesFor(_entityManager.GetComponent<TransformComponent>(Owner).Coordinates));
}
protected override void Shutdown()
@@ -58,9 +58,9 @@ namespace Robust.Client.GameObjects
private void SendDirty()
{
if (Owner.Transform.Anchored)
if (_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
{
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
_entityManager.EventBus.RaiseEvent(EventSource.Local,
new OccluderDirtyEvent(Owner, _lastPosition));
}
}
@@ -69,18 +69,18 @@ namespace Robust.Client.GameObjects
{
Occluding = OccluderDir.None;
if (Deleted || !Owner.Transform.Anchored)
if (Deleted || !_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
{
return;
}
var grid = _mapManager.GetGrid(_entityManager.GetComponent<TransformComponent>(Owner).GridID);
var position = _entityManager.GetComponent<TransformComponent>(Owner).Coordinates;
void CheckDir(Direction dir, OccluderDir oclDir)
{
var grid = _mapManager.GetGrid(Owner.Transform.GridID);
var position = Owner.Transform.Coordinates;
foreach (var neighbor in grid.GetInDir(position, dir))
{
if (Owner.EntityManager.TryGetComponent(neighbor, out ClientOccluderComponent? comp) && comp.Enabled)
if (_entityManager.TryGetComponent(neighbor, out ClientOccluderComponent? comp) && comp.Enabled)
{
Occluding |= oclDir;
break;
@@ -88,10 +88,20 @@ namespace Robust.Client.GameObjects
}
}
CheckDir(Direction.North, OccluderDir.North);
CheckDir(Direction.East, OccluderDir.East);
CheckDir(Direction.South, OccluderDir.South);
CheckDir(Direction.West, OccluderDir.West);
var angle = _entityManager.GetComponent<TransformComponent>(Owner).LocalRotation;
var dirRolling = angle.GetCardinalDir();
// dirRolling starts at effective south
CheckDir(dirRolling, OccluderDir.South);
dirRolling = dirRolling.GetClockwise90Degrees();
CheckDir(dirRolling, OccluderDir.West);
dirRolling = dirRolling.GetClockwise90Degrees();
CheckDir(dirRolling, OccluderDir.North);
dirRolling = dirRolling.GetClockwise90Degrees();
CheckDir(dirRolling, OccluderDir.East);
}
[Flags]

View File

@@ -1,6 +1,7 @@
using Robust.Client.Graphics;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -12,6 +13,8 @@ namespace Robust.Client.GameObjects
[ComponentReference(typeof(SharedPointLightComponent))]
public class PointLightComponent : SharedPointLightComponent, ISerializationHooks
{
[Dependency] private readonly IEntityManager _entityManager = default!;
internal bool TreeUpdateQueued { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
@@ -31,7 +34,7 @@ namespace Robust.Client.GameObjects
{
if (_enabled == value) return;
base.Enabled = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
_entityManager.EventBus.RaiseLocalEvent(Owner, new PointLightUpdateEvent());
}
}
@@ -44,7 +47,7 @@ namespace Robust.Client.GameObjects
if (_containerOccluded == value) return;
_containerOccluded = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
_entityManager.EventBus.RaiseLocalEvent(Owner, new PointLightUpdateEvent());
}
}
@@ -151,7 +154,7 @@ namespace Robust.Client.GameObjects
if (MathHelper.CloseToPercent(value, _radius)) return;
base.Radius = value;
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PointLightRadiusChangedEvent(this));
_entityManager.EventBus.RaiseEvent(EventSource.Local, new PointLightRadiusChangedEvent(this));
}
}

View File

@@ -23,6 +23,11 @@ namespace Robust.Client.GameObjects
public sealed class SpriteBoundsSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eye = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly RenderingTreeSystem _renderingTree = default!;
private SpriteBoundsOverlay? _overlay;
public bool Enabled
@@ -37,15 +42,13 @@ namespace Robust.Client.GameObjects
if (_enabled)
{
DebugTools.AssertNull(_overlay);
var overlayManager = IoCManager.Resolve<IOverlayManager>();
_overlay = new SpriteBoundsOverlay(EntitySystem.Get<RenderingTreeSystem>(), IoCManager.Resolve<IEyeManager>());
overlayManager.AddOverlay(_overlay);
_overlay = new SpriteBoundsOverlay(_renderingTree, _eye, _entityManager);
_overlayManager.AddOverlay(_overlay);
}
else
{
if (_overlay == null) return;
var overlayManager = IoCManager.Resolve<IOverlayManager>();
overlayManager.RemoveOverlay(_overlay);
_overlayManager.RemoveOverlay(_overlay);
_overlay = null;
}
}
@@ -58,13 +61,15 @@ namespace Robust.Client.GameObjects
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly IEyeManager _eyeManager = default!;
private readonly IEyeManager _eyeManager;
private readonly IEntityManager _entityManager;
private RenderingTreeSystem _renderTree;
public SpriteBoundsOverlay(RenderingTreeSystem renderTree, IEyeManager eyeManager)
public SpriteBoundsOverlay(RenderingTreeSystem renderTree, IEyeManager eyeManager, IEntityManager entityManager)
{
_renderTree = renderTree;
_eyeManager = eyeManager;
_entityManager = entityManager;
}
protected internal override void Draw(in OverlayDrawArgs args)
@@ -75,11 +80,11 @@ namespace Robust.Client.GameObjects
foreach (var comp in _renderTree.GetRenderTrees(currentMap, viewport))
{
var localAABB = comp.Owner.Transform.InvWorldMatrix.TransformBox(viewport);
var localAABB = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(viewport);
foreach (var sprite in comp.SpriteTree.QueryAabb(localAABB))
{
var worldPos = sprite.Owner.Transform.WorldPosition;
var worldPos = _entityManager.GetComponent<TransformComponent>(sprite.Owner).WorldPosition;
var bounds = sprite.CalculateBoundingBox(worldPos);
handle.DrawRect(bounds, Color.Red.WithAlpha(0.2f));
handle.DrawRect(bounds.Scale(0.2f).Translated(-new Vector2(0f, bounds.Extents.Y)), Color.Blue.WithAlpha(0.5f));

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Text;
@@ -13,14 +12,11 @@ using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
@@ -34,6 +30,7 @@ namespace Robust.Client.GameObjects
{
[Dependency] private readonly IResourceCache resourceCache = default!;
[Dependency] private readonly IPrototypeManager prototypes = default!;
[Dependency] private readonly IEntityManager entities = default!;
[DataField("visible")]
private bool _visible = true;
@@ -47,7 +44,7 @@ namespace Robust.Client.GameObjects
if (_visible == value) return;
_visible = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(OwnerUid, new SpriteUpdateEvent());
entities.EventBus.RaiseLocalEvent(Owner, new SpriteUpdateEvent());
}
}
@@ -213,6 +210,7 @@ namespace Robust.Client.GameObjects
layer.Color = layerDatum.Color;
layer.Rotation = layerDatum.Rotation;
layer._offset = layerDatum.Offset;
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
layer.Visible = anyTextureAttempted && layerDatum.Visible;
layer.Scale = layerDatum.Scale;
@@ -302,7 +300,7 @@ namespace Robust.Client.GameObjects
{
if (_containerOccluded == value) return;
_containerOccluded = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(OwnerUid, new SpriteUpdateEvent());
entities.EventBus.RaiseLocalEvent(Owner, new SpriteUpdateEvent());
}
}
@@ -1259,7 +1257,7 @@ namespace Robust.Client.GameObjects
if (worldRotation.Theta < 0)
worldRotation = new Angle(worldRotation.Theta + Math.Tau);
var localMatrix = GetLocalMatrix();
var spriteMatrix = GetLocalMatrix();
foreach (var layer in Layers)
{
@@ -1270,9 +1268,10 @@ namespace Robust.Client.GameObjects
var numDirs = GetLayerDirectionCount(layer);
var layerRotation = worldRotation + layer.Rotation;
var layerPosition = worldPosition + layerRotation.RotateVec(layer._offset);
CalcModelMatrix(numDirs, eyeRotation, layerRotation, worldPosition, out var modelMatrix);
Matrix3.Multiply(ref localMatrix, ref modelMatrix, out var transformMatrix);
CalcModelMatrix(numDirs, eyeRotation, layerRotation, layerPosition, out var modelMatrix);
Matrix3.Multiply(ref spriteMatrix, ref modelMatrix, out var transformMatrix);
drawingHandle.SetTransform(in transformMatrix);
RenderLayer(drawingHandle, layer, eyeRotation, layerRotation, overrideDirection);
@@ -1290,7 +1289,7 @@ namespace Robust.Client.GameObjects
var layerColor = color * layer.Color;
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter) + layer.Offset;
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter);
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
var quad = Box2.FromDimensions(position, textureSize);
@@ -1503,7 +1502,7 @@ namespace Robust.Client.GameObjects
{
// Look this was an easy way to get bounds checks for layer updates.
// If you really want it optimal you'll need to comb through all 2k lines of spritecomponent.
if (Owner?.EntityManager?.EventBus != null)
if ((Owner != default ? entities : null)?.EventBus != null)
UpdateBounds();
if (_inertUpdateQueued)
@@ -1512,7 +1511,7 @@ namespace Robust.Client.GameObjects
_inertUpdateQueued = true;
// Yes that null check is valid because of that stupid fucking dummy IEntity.
// Who thought that was a good idea.
Owner?.EntityManager?.EventBus?.RaiseEvent(EventSource.Local, new SpriteUpdateInertEvent {Sprite = this});
(Owner != default ? entities : null)?.EventBus?.RaiseEvent(EventSource.Local, new SpriteUpdateInertEvent {Sprite = this});
}
internal void DoUpdateIsInert()
@@ -1599,7 +1598,7 @@ namespace Robust.Client.GameObjects
builder.AppendFormat(
"vis/depth/scl/rot/ofs/col/norot/override/dir: {0}/{1}/{2}/{3}/{4}/{5}/{6}/{8}/{7}\n",
Visible, DrawDepth, Scale, Rotation, Offset,
Color, NoRotation, GetDir(RSI.State.DirectionType.Dir8, Owner.Transform.WorldRotation),
Color, NoRotation, GetDir(RSI.State.DirectionType.Dir8, entities.GetComponent<TransformComponent>(Owner).WorldRotation),
DirectionOverride
);
@@ -1653,7 +1652,7 @@ namespace Robust.Client.GameObjects
internal void UpdateBounds()
{
Owner.EntityManager.EventBus.RaiseLocalEvent(OwnerUid, new SpriteUpdateEvent());
entities.EventBus.RaiseLocalEvent(Owner, new SpriteUpdateEvent());
}
/// <summary>
@@ -1723,7 +1722,7 @@ namespace Robust.Client.GameObjects
}
}
private Vector2 _offset;
internal Vector2 _offset;
[ViewVariables]
public DirectionOffset DirOffset { get; set; }
@@ -2072,7 +2071,7 @@ namespace Robust.Client.GameObjects
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace).Uid;
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace);
var spriteComponent = entityManager.EnsureComponent<SpriteComponent>(dummy);
var anyTexture = false;
@@ -2111,7 +2110,7 @@ namespace Robust.Client.GameObjects
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace).Uid;
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace);
var spriteComponent = entityManager.EnsureComponent<SpriteComponent>(dummy);
var result = spriteComponent.Icon ?? GetFallbackState(resourceCache);
entityManager.DeleteEntity(dummy);

View File

@@ -1,5 +1,4 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Robust.Client.GameObjects
@@ -9,55 +8,7 @@ namespace Robust.Client.GameObjects
{
public override string Name => "RenderingTree";
internal DynamicTree<SpriteComponent> SpriteTree { get; private set; } = new(SpriteAabbFunc);
internal DynamicTree<PointLightComponent> LightTree { get; private set; } = new(LightAabbFunc);
private static Box2 SpriteAabbFunc(in SpriteComponent value)
{
var worldPos = value.Owner.Transform.WorldPosition;
var worldRot = value.Owner.Transform.WorldRotation;
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
var localAABB = tree?.Owner.Transform.InvWorldMatrix.TransformBox(bounds) ?? bounds.CalcBoundingBox();
return localAABB;
}
private static Box2 LightAabbFunc(in PointLightComponent value)
{
var worldPos = value.Owner.Transform.WorldPosition;
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
var boxSize = value.Radius * 2;
var localPos = tree?.Owner.Transform.InvWorldMatrix.Transform(worldPos) ?? worldPos;
return Box2.CenteredAround(localPos, (boxSize, boxSize));
}
internal static Box2 SpriteAabbFunc(SpriteComponent value, Vector2? worldPos = null, Angle? worldRot = null)
{
worldPos ??= value.Owner.Transform.WorldPosition;
worldRot ??= value.Owner.Transform.WorldRotation;
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos.Value), worldRot.Value, worldPos.Value);
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
var localAABB = tree?.Owner.Transform.InvWorldMatrix.TransformBox(bounds) ?? bounds.CalcBoundingBox();
return localAABB;
}
internal static Box2 LightAabbFunc(PointLightComponent value, Vector2? worldPos = null)
{
// Lights are circles so don't need entity's rotation
worldPos ??= value.Owner.Transform.WorldPosition;
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
var boxSize = value.Radius * 2;
var localPos = tree?.Owner.Transform.InvWorldMatrix.Transform(worldPos.Value) ?? worldPos.Value;
return Box2.CenteredAround(localPos, (boxSize, boxSize));
}
internal DynamicTree<SpriteComponent> SpriteTree { get; set; } = default!;
internal DynamicTree<PointLightComponent> LightTree { get; set; } = default!;
}
}

View File

@@ -15,6 +15,8 @@ namespace Robust.Client.GameObjects
{
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly Dictionary<object, BoundUserInterface> _openInterfaces =
new();
@@ -87,15 +89,15 @@ namespace Robust.Client.GameObjects
_openInterfaces.Remove(uiKey);
boundUserInterface.Dispose();
var playerSession = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.Session;
var playerSession = _playerManager.LocalPlayer?.Session;
if(playerSession != null)
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new BoundUIClosedEvent(uiKey, Owner.Uid, playerSession));
_entityManager.EventBus.RaiseLocalEvent(Owner, new BoundUIClosedEvent(uiKey, Owner, playerSession));
}
internal void SendMessage(BoundUserInterfaceMessage message, object uiKey)
{
EntitySystem.Get<UserInterfaceSystem>()
.Send(new BoundUIWrapMessage(Owner.Uid, message, uiKey));
.Send(new BoundUIWrapMessage(Owner, message, uiKey));
}
}

View File

@@ -46,7 +46,7 @@ namespace Robust.Client.GameObjects
foreach (var key in remie)
{
component.PlayingAnimations.Remove(key);
EntityManager.EventBus.RaiseLocalEvent(component.Owner.Uid, new AnimationCompletedEvent {Uid = component.Owner.Uid, Key = key});
EntityManager.EventBus.RaiseLocalEvent(component.Owner, new AnimationCompletedEvent {Uid = component.Owner, Key = key});
component.AnimationComplete(key);
}
@@ -58,7 +58,7 @@ namespace Robust.Client.GameObjects
/// </summary>
public void Play(EntityUid uid, Animation animation, string key)
{
var component = EntityManager.EnsureComponent<AnimationPlayerComponent>(EntityManager.GetEntity(uid));
var component = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
Play(component, animation, key);
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
@@ -7,9 +7,9 @@ namespace Robust.Client.GameObjects
[UsedImplicitly]
internal sealed class AppearanceSystem : EntitySystem
{
private readonly Queue<AppearanceComponent> _queuedUpdates = new();
private readonly Queue<ClientAppearanceComponent> _queuedUpdates = new();
public void EnqueueUpdate(AppearanceComponent component)
public void EnqueueUpdate(ClientAppearanceComponent component)
{
_queuedUpdates.Enqueue(component);
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Graphics;
@@ -82,8 +81,8 @@ namespace Robust.Client.GameObjects
private void PlayAudioEntityHandler(PlayAudioEntityMessage ev)
{
var stream = EntityManager.TryGetEntity(ev.EntityUid, out var entity) ?
(PlayingStream?) Play(ev.FileName, entity, ev.FallbackCoordinates, ev.AudioParams)
var stream = EntityManager.EntityExists(ev.EntityUid) ?
(PlayingStream?) Play(ev.FileName, ev.EntityUid, ev.FallbackCoordinates, ev.AudioParams)
: (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams);
if (stream != null)
@@ -123,15 +122,15 @@ namespace Robust.Client.GameObjects
continue;
}
}
else if (stream.TrackingEntity != null)
else if (stream.TrackingEntity != default)
{
if (stream.TrackingEntity.Deleted)
if (EntityManager.Deleted(stream.TrackingEntity))
{
StreamDone(stream);
continue;
}
mapPos = stream.TrackingEntity.Transform.MapPosition;
mapPos = EntityManager.GetComponent<TransformComponent>(stream.TrackingEntity).MapPosition;
}
// TODO Remove when coordinates can't be NaN
@@ -211,7 +210,7 @@ namespace Robust.Client.GameObjects
}
}
if (stream.TrackingEntity != null)
if (stream.TrackingEntity != default)
{
stream.Source.SetVelocity(stream.TrackingEntity.GlobalLinearVelocity());
}
@@ -281,7 +280,7 @@ namespace Robust.Client.GameObjects
/// <param name="entity">The entity "emitting" the audio.</param>
/// <param name="fallbackCoordinates">The map or grid coordinates at which to play the audio when entity is invalid.</param>
/// <param name="audioParams"></param>
private IPlayingAudioStream? Play(string filename, IEntity entity, EntityCoordinates fallbackCoordinates,
private IPlayingAudioStream? Play(string filename, EntityUid entity, EntityCoordinates fallbackCoordinates,
AudioParams? audioParams = null)
{
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
@@ -300,11 +299,11 @@ namespace Robust.Client.GameObjects
/// <param name="entity">The entity "emitting" the audio.</param>
/// <param name="fallbackCoordinates">The map or grid coordinates at which to play the audio when entity is invalid.</param>
/// <param name="audioParams"></param>
private IPlayingAudioStream? Play(AudioStream stream, IEntity entity, EntityCoordinates fallbackCoordinates,
private IPlayingAudioStream? Play(AudioStream stream, EntityUid entity, EntityCoordinates fallbackCoordinates,
AudioParams? audioParams = null)
{
var source = _clyde.CreateAudioSource(stream);
if (!source.SetPosition(entity.Transform.WorldPosition))
if (!source.SetPosition(EntityManager.GetComponent<TransformComponent>(entity).WorldPosition))
{
return Play(stream, fallbackCoordinates, fallbackCoordinates, audioParams);
}
@@ -407,7 +406,7 @@ namespace Robust.Client.GameObjects
{
public uint? NetIdentifier;
public IClydeAudioSource Source = default!;
public IEntity TrackingEntity = default!;
public EntityUid TrackingEntity = default!;
public EntityCoordinates? TrackingCoordinates;
public EntityCoordinates? TrackingFallbackCoordinates;
public bool Done;
@@ -440,9 +439,6 @@ namespace Robust.Client.GameObjects
}
}
/// <inheritdoc />
public int DefaultSoundRange => 25;
/// <inheritdoc />
public int OcclusionCollisionMask { get; set; }
@@ -453,15 +449,9 @@ namespace Robust.Client.GameObjects
}
/// <inheritdoc />
public IPlayingAudioStream? Play(Filter playerFilter, string filename, IEntity entity, AudioParams? audioParams = null)
public IPlayingAudioStream? Play(Filter playerFilter, string filename, EntityUid entity, AudioParams? audioParams = null)
{
return Play(filename, entity, GetFallbackCoordinates(entity.Transform.MapPosition), audioParams);
}
public IPlayingAudioStream? Play(Filter playerFilter, string filename, EntityUid uid, AudioParams? audioParams = null)
{
return EntityManager.TryGetEntity(uid, out var entity)
? Play(filename, entity, GetFallbackCoordinates(entity.Transform.MapPosition), audioParams) : null;
return Play(filename, entity, GetFallbackCoordinates(EntityManager.GetComponent<TransformComponent>(entity).MapPosition), audioParams);
}
/// <inheritdoc />

View File

@@ -70,14 +70,14 @@ namespace Robust.Client.GameObjects
private void HandleDirtyEvent(OccluderDirtyEvent ev)
{
var sender = ev.Sender;
if (sender.IsValid() &&
sender.TryGetComponent(out ClientOccluderComponent? iconSmooth)
if (EntityManager.EntityExists(sender) &&
EntityManager.TryGetComponent(sender, out ClientOccluderComponent? iconSmooth)
&& iconSmooth.Running)
{
var grid1 = _mapManager.GetGrid(sender.Transform.GridID);
var coords = sender.Transform.Coordinates;
var grid1 = _mapManager.GetGrid(EntityManager.GetComponent<TransformComponent>(sender).GridID);
var coords = EntityManager.GetComponent<TransformComponent>(sender).Coordinates;
_dirtyEntities.Enqueue(sender.Uid);
_dirtyEntities.Enqueue(sender);
AddValidEntities(grid1.GetInDir(coords, Direction.North));
AddValidEntities(grid1.GetInDir(coords, Direction.South));
AddValidEntities(grid1.GetInDir(coords, Direction.East));
@@ -113,13 +113,13 @@ namespace Robust.Client.GameObjects
/// </summary>
internal sealed class OccluderDirtyEvent : EntityEventArgs
{
public OccluderDirtyEvent(IEntity sender, (GridId grid, Vector2i pos)? lastPosition)
public OccluderDirtyEvent(EntityUid sender, (GridId grid, Vector2i pos)? lastPosition)
{
LastPosition = lastPosition;
Sender = sender;
}
public (GridId grid, Vector2i pos)? LastPosition { get; }
public IEntity Sender { get; }
public EntityUid Sender { get; }
}
}

View File

@@ -15,7 +15,7 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly IRobustSerializer _serializer = default!;
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
private readonly HashSet<IEntity> _updateQueue = new();
private readonly HashSet<EntityUid> _updateQueue = new();
public readonly Dictionary<EntityUid, IContainer> ExpectedEntities = new();
@@ -37,10 +37,10 @@ namespace Robust.Client.GameObjects
private void HandleEntityInitialized(EntityInitializedMessage ev)
{
if (!ExpectedEntities.TryGetValue(ev.Entity.Uid, out var container))
if (!ExpectedEntities.TryGetValue(ev.Entity, out var container))
return;
RemoveExpectedEntity(ev.Entity.Uid);
RemoveExpectedEntity(ev.Entity);
if (container.Deleted)
return;
@@ -89,12 +89,12 @@ namespace Robust.Client.GameObjects
container.OccludesLight = occludesLight;
// Remove gone entities.
List<IEntity>? toRemove = null;
List<EntityUid>? toRemove = null;
foreach (var entity in container.ContainedEntities)
{
if (!entityUids.Contains(entity.Uid))
if (!entityUids.Contains(entity))
{
toRemove ??= new List<IEntity>();
toRemove ??= new List<EntityUid>();
toRemove.Add(entity);
}
}
@@ -123,11 +123,11 @@ namespace Robust.Client.GameObjects
}
// Add new entities.
foreach (var entityUid in entityUids)
foreach (var entity in entityUids)
{
if (!EntityManager.TryGetEntity(entityUid, out var entity))
if (!EntityManager.EntityExists(entity))
{
AddExpectedEntity(entityUid, container);
AddExpectedEntity(entity, container);
continue;
}
@@ -172,7 +172,7 @@ namespace Robust.Client.GameObjects
foreach (var toUpdate in _updateQueue)
{
if (toUpdate.Deleted)
if (EntityManager.Deleted(toUpdate))
{
continue;
}
@@ -183,22 +183,22 @@ namespace Robust.Client.GameObjects
_updateQueue.Clear();
}
private static void UpdateEntityRecursively(IEntity entity)
private void UpdateEntityRecursively(EntityUid entity)
{
// TODO: Since we are recursing down,
// we could cache ShowContents data here to speed it up for children.
// Am lazy though.
UpdateEntity(entity);
foreach (var child in entity.Transform.Children)
foreach (var child in EntityManager.GetComponent<TransformComponent>(entity).Children)
{
UpdateEntityRecursively(child.Owner);
}
}
private static void UpdateEntity(IEntity entity)
private void UpdateEntity(EntityUid entity)
{
if (entity.TryGetComponent(out SpriteComponent? sprite))
if (EntityManager.TryGetComponent(entity, out SpriteComponent? sprite))
{
sprite.ContainerOccluded = false;
@@ -216,7 +216,7 @@ namespace Robust.Client.GameObjects
}
}
if (entity.TryGetComponent(out PointLightComponent? light))
if (EntityManager.TryGetComponent(entity, out PointLightComponent? light))
{
light.ContainerOccluded = false;

View File

@@ -77,11 +77,11 @@ namespace Robust.Client.GameObjects
for (var i = 0; i < message.Entities.Count; i++)
{
var uid = message.Entities[i];
var entity = message.Entities[i];
if (!EntityManager.TryGetEntity(uid, out var entity)) continue;
if (!EntityManager.EntityExists(entity)) continue;
text.AppendLine(entity.ToString());
text.AppendLine((string) EntityManager.ToPrettyString(entity));
}
_label.Text = text.ToString();

View File

@@ -23,7 +23,6 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly IOverlayManager overlayManager = default!;
[Dependency] private readonly IPrototypeManager prototypeManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
private readonly List<Effect> _Effects = new();
@@ -35,7 +34,7 @@ namespace Robust.Client.GameObjects
SubscribeNetworkEvent<EffectSystemMessage>(CreateEffect);
SubscribeLocalEvent<EffectSystemMessage>(CreateEffect);
var overlay = new EffectOverlay(this, prototypeManager, _mapManager, _playerManager, _entityManager);
var overlay = new EffectOverlay(this, prototypeManager, _playerManager, EntityManager);
overlayManager.AddOverlay(overlay);
}
@@ -65,13 +64,8 @@ namespace Robust.Client.GameObjects
}
//Create effect from creation message
var effect = new Effect(message, resourceCache, _mapManager, _entityManager);
var effect = new Effect(message, resourceCache, _mapManager, EntityManager);
effect.Deathtime = gameTiming.CurTime + message.LifeTime;
if (effect.AttachedEntityUid != null
&& _entityManager.TryGetEntity(effect.AttachedEntityUid.Value, out var attachedEntity))
{
effect.AttachedEntity = attachedEntity;
}
_Effects.Add(effect);
}
@@ -119,8 +113,6 @@ namespace Robust.Client.GameObjects
/// <summary>
/// Entity that the effect is attached to
/// </summary>
public IEntity? AttachedEntity { get; set; }
public EntityUid? AttachedEntityUid { get; }
/// <summary>
@@ -335,14 +327,12 @@ namespace Robust.Client.GameObjects
private readonly ShaderInstance _unshadedShader;
private readonly EffectSystem _owner;
private readonly IMapManager _mapManager;
private readonly IEntityManager _entityManager;
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IMapManager mapMan, IPlayerManager playerMan, IEntityManager entityManager)
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IPlayerManager playerMan, IEntityManager entityManager)
{
_owner = owner;
_unshadedShader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
_mapManager = mapMan;
_playerManager = playerMan;
_entityManager = entityManager;
}
@@ -353,12 +343,15 @@ namespace Robust.Client.GameObjects
var worldHandle = args.WorldHandle;
ShaderInstance? currentShader = null;
var player = _playerManager.LocalPlayer?.ControlledEntity;
if (_playerManager.LocalPlayer?.ControlledEntity is not {} playerEnt)
return;
foreach (var effect in _owner._Effects)
{
if (effect.AttachedEntity?.Transform.MapID != player?.Transform.MapID &&
effect.Coordinates.GetMapId(_entityManager) != map)
if(effect.AttachedEntityUid is {} attached
&& _entityManager.GetComponent<TransformComponent>(attached).MapID != _entityManager.GetComponent<TransformComponent>(playerEnt).MapID
&& effect.Coordinates.GetMapId(_entityManager) != map)
{
continue;
}
@@ -371,13 +364,19 @@ namespace Robust.Client.GameObjects
currentShader = newShader;
}
// TODO: Should be doing matrix transformations
var effectSprite = effect.EffectSprite;
var effectOrigin = effect.AttachedEntity?.Transform.MapPosition.Position + effect.AttachedOffset ??
effect.Coordinates.ToMapPos(_entityManager);
var tempQualifier1 = effect.AttachedEntityUid;
var coordinates =
(tempQualifier1 != null ? _entityManager.GetComponent<TransformComponent>(tempQualifier1.Value).Coordinates : effect.Coordinates)
.Offset(effect.AttachedOffset);
var rotation = _entityManager.GetComponent<TransformComponent>(coordinates.EntityId).WorldRotation;
var effectOrigin = coordinates.ToMapPos(_entityManager);
var effectArea = Box2.CenteredAround(effectOrigin, effect.Size);
var rotatedBox = new Box2Rotated(effectArea, effect.Rotation, effectOrigin);
var rotatedBox = new Box2Rotated(effectArea, effect.Rotation - rotation, effectOrigin);
worldHandle.DrawTextureRect(effectSprite, rotatedBox, ToColor(effect.Color));
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Physics;
@@ -9,7 +9,6 @@ using Robust.Shared.Input.Binding;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
#nullable enable
@@ -70,15 +69,23 @@ namespace Robust.Client.GameObjects
// TODO: Content should have its own way of handling this. We should have a default behavior that they can overwrite.
var playerTransform = _playerManager.LocalPlayer?.ControlledEntity?.Transform;
EntityUid tempQualifier = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
var playerTransform = (tempQualifier != EntityUid.Invalid ? EntityManager.GetComponent<TransformComponent>(tempQualifier) : null);
if (playerTransform == null) return;
var gridId = playerTransform.GridID;
var parent = gridId != GridId.Invalid && EntityManager.TryGetEntity(_mapManager.GetGrid(gridId).GridEntityId, out var gridEnt) ?
gridEnt.Transform
: _mapManager.GetMapEntity(playerTransform.MapID).Transform;
TransformComponent parent;
if (gridId != GridId.Invalid &&
_mapManager.GetGrid(gridId).GridEntityId is var gridEnt &&
EntityManager.EntityExists(gridEnt))
parent = EntityManager.GetComponent<TransformComponent>(gridEnt);
else
{
parent = EntityManager.GetComponent<TransformComponent>(
_mapManager.GetMapEntityId(playerTransform.MapID));
}
// Make sure that we don't fire the vomit carousel when we spawn in
if (_lastParent is null)

View File

@@ -1,9 +1,12 @@
using System;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
@@ -25,7 +28,7 @@ namespace Robust.Client.GameObjects
{
DebugTools.Assert(_overlay == null);
_overlay = new GridChunkBoundsOverlay(
IoCManager.Resolve<IEntityManager>(),
EntityManager,
IoCManager.Resolve<IEyeManager>(),
IoCManager.Resolve<IMapManager>());
@@ -61,23 +64,38 @@ namespace Robust.Client.GameObjects
{
var currentMap = _eyeManager.CurrentMap;
var viewport = _eyeManager.GetWorldViewport();
var worldHandle = args.WorldHandle;
foreach (var grid in _mapManager.FindGridsIntersecting(currentMap, viewport))
{
var gridEnt = _entityManager.GetEntity(grid.GridEntityId);
var gridInternal = (IMapGridInternal)grid;
var worldMatrix = _entityManager.GetComponent<TransformComponent>(grid.GridEntityId).WorldMatrix;
worldHandle.SetTransform(worldMatrix);
var transform = new Transform(Vector2.Zero, Angle.Zero);
if (!_entityManager.TryGetComponent<PhysicsComponent>(gridEnt.Uid, out var body)) continue;
gridInternal.GetMapChunks(viewport, out var chunkEnumerator);
var transform = body.GetTransform();
foreach (var fixture in body.Fixtures)
while (chunkEnumerator.MoveNext(out var chunk))
{
for (var i = 0; i < fixture.Shape.ChildCount; i++)
foreach (var fixture in chunk.Fixtures)
{
var aabb = fixture.Shape.ComputeAABB(transform, i);
var poly = (PolygonShape) fixture.Shape;
args.WorldHandle.DrawRect(aabb, Color.Green.WithAlpha(0.2f));
args.WorldHandle.DrawRect(aabb, Color.Red.WithAlpha(0.5f), false);
var verts = new Vector2[poly.Vertices.Length];
for (var i = 0; i < poly.Vertices.Length; i++)
{
verts[i] = Transform.Mul(transform, poly.Vertices[i]);
}
worldHandle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, Color.Green.WithAlpha(0.2f));
for (var i = 0; i < fixture.Shape.ChildCount; i++)
{
var aabb = fixture.Shape.ComputeAABB(transform, i);
args.WorldHandle.DrawRect(aabb, Color.Red.WithAlpha(0.5f), false);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Robust.Client.GameStates;
using Robust.Client.Input;
using Robust.Client.Player;
@@ -107,7 +107,7 @@ namespace Robust.Client.GameObjects
private void OnAttachedEntityChanged(PlayerAttachSysMessage message)
{
if (message.AttachedEntity != null) // attach
if (message.AttachedEntity != default) // attach
{
SetEntityContextActive(_inputManager, message.AttachedEntity);
}
@@ -117,14 +117,14 @@ namespace Robust.Client.GameObjects
}
}
private static void SetEntityContextActive(IInputManager inputMan, IEntity entity)
private void SetEntityContextActive(IInputManager inputMan, EntityUid entity)
{
if(entity == null || !entity.IsValid())
if(entity == default || !EntityManager.EntityExists(entity))
throw new ArgumentNullException(nameof(entity));
if (!entity.TryGetComponent(out InputComponent? inputComp))
if (!EntityManager.TryGetComponent(entity, out InputComponent? inputComp))
{
Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity.Uid}, entProto={entity.Prototype}. Setting default \"{InputContextContainer.DefaultContextName}\" context...");
Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity}, entProto={EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype}. Setting default \"{InputContextContainer.DefaultContextName}\" context...");
inputMan.Contexts.SetActiveContext(InputContextContainer.DefaultContextName);
return;
}
@@ -135,7 +135,7 @@ namespace Robust.Client.GameObjects
}
else
{
Logger.ErrorS("input.context", $"Unknown context: entId={entity.Uid}, entProto={entity.Prototype}, context={inputComp.ContextName}. . Setting default \"{InputContextContainer.DefaultContextName}\" context...");
Logger.ErrorS("input.context", $"Unknown context: entId={entity}, entProto={EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype}, context={inputComp.ContextName}. . Setting default \"{InputContextContainer.DefaultContextName}\" context...");
inputMan.Contexts.SetActiveContext(InputContextContainer.DefaultContextName);
}
}
@@ -145,12 +145,13 @@ namespace Robust.Client.GameObjects
/// </summary>
public void SetEntityContextActive()
{
if (_playerManager.LocalPlayer?.ControlledEntity == null)
var controlled = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
if (controlled == EntityUid.Invalid)
{
return;
}
SetEntityContextActive(_inputManager, _playerManager.LocalPlayer.ControlledEntity);
SetEntityContextActive(_inputManager, controlled);
}
}
@@ -162,13 +163,13 @@ namespace Robust.Client.GameObjects
/// <summary>
/// New entity the player is attached to.
/// </summary>
public IEntity? AttachedEntity { get; }
public EntityUid AttachedEntity { get; }
/// <summary>
/// Creates a new instance of <see cref="PlayerAttachSysMessage"/>.
/// </summary>
/// <param name="attachedEntity">New entity the player is attached to.</param>
public PlayerAttachSysMessage(IEntity? attachedEntity)
public PlayerAttachSysMessage(EntityUid attachedEntity)
{
AttachedEntity = attachedEntity;
}
@@ -176,21 +177,21 @@ namespace Robust.Client.GameObjects
public class PlayerAttachedEvent : EntityEventArgs
{
public PlayerAttachedEvent(IEntity entity)
public PlayerAttachedEvent(EntityUid entity)
{
Entity = entity;
}
public IEntity Entity { get; }
public EntityUid Entity { get; }
}
public class PlayerDetachedEvent : EntityEventArgs
{
public PlayerDetachedEvent(IEntity entity)
public PlayerDetachedEvent(EntityUid entity)
{
Entity = entity;
}
public IEntity Entity { get; }
public EntityUid Entity { get; }
}
}

View File

@@ -23,8 +23,7 @@ namespace Robust.Client.GameObjects
private void HandleRemove(EntityUid uid, PointLightComponent component, ComponentRemove args)
{
var ent = EntityManager.GetEntity(uid);
var map = ent.Transform.MapID;
var map = EntityManager.GetComponent<TransformComponent>(uid).MapID;
// TODO: Just make this update the tree directly and not allocate
if (map != MapId.Nullspace)
{

View File

@@ -1,6 +1,4 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using JetBrains.Annotations;
using Robust.Client.Physics;
using Robust.Shared;
@@ -11,7 +9,6 @@ using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
{
@@ -30,7 +27,7 @@ namespace Robust.Client.GameObjects
private readonly List<SpriteComponent> _spriteQueue = new();
private readonly List<PointLightComponent> _lightQueue = new();
private HashSet<EntityUid> _checkedChildren = new();
private readonly HashSet<EntityUid> _checkedChildren = new();
/// <summary>
/// <see cref="CVars.MaxLightRadius"/>
@@ -43,10 +40,12 @@ namespace Robust.Client.GameObjects
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
{
yield return EntityManager.GetEntity(grid.GridEntityId).GetComponent<RenderingTreeComponent>();
var tempQualifier = grid.GridEntityId;
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier);
}
yield return _mapManager.GetMapEntity(mapId).GetComponent<RenderingTreeComponent>();
var tempQualifier1 = _mapManager.GetMapEntityId(mapId);
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier1);
}
internal IEnumerable<RenderingTreeComponent> GetRenderTrees(MapId mapId, Box2 worldAABB)
@@ -55,10 +54,12 @@ namespace Robust.Client.GameObjects
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB))
{
yield return EntityManager.GetEntity(grid.GridEntityId).GetComponent<RenderingTreeComponent>();
var tempQualifier = grid.GridEntityId;
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier);
}
yield return _mapManager.GetMapEntity(mapId).GetComponent<RenderingTreeComponent>();
var tempQualifier1 = _mapManager.GetMapEntityId(mapId);
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier1);
}
public override void Initialize()
@@ -86,12 +87,19 @@ namespace Robust.Client.GameObjects
SubscribeLocalEvent<PointLightComponent, RenderTreeRemoveLightEvent>(RemoveLight);
SubscribeLocalEvent<PointLightComponent, PointLightUpdateEvent>(HandleLightUpdate);
SubscribeLocalEvent<RenderingTreeComponent, ComponentRemove>(HandleTreeRemove);
SubscribeLocalEvent<RenderingTreeComponent, ComponentInit>(OnTreeInit);
SubscribeLocalEvent<RenderingTreeComponent, ComponentRemove>(OnTreeRemove);
var configManager = IoCManager.Resolve<IConfigurationManager>();
configManager.OnValueChanged(CVars.MaxLightRadius, value => MaxLightRadius = value, true);
}
private void OnTreeInit(EntityUid uid, RenderingTreeComponent component, ComponentInit args)
{
component.LightTree = new DynamicTree<PointLightComponent>(LightAabbFunc);
component.SpriteTree = new DynamicTree<SpriteComponent>(SpriteAabbFunc);
}
private void HandleLightUpdate(EntityUid uid, PointLightComponent component, PointLightUpdateEvent args)
{
if (component.TreeUpdateQueued) return;
@@ -106,23 +114,23 @@ namespace Robust.Client.GameObjects
private void AnythingMoved(ref MoveEvent args)
{
AnythingMovedSubHandler(args.Sender.Transform);
AnythingMovedSubHandler(EntityManager.GetComponent<TransformComponent>(args.Sender));
}
private void AnythingMovedSubHandler(TransformComponent sender)
{
// To avoid doing redundant updates (and we don't need to update a grid's children ever)
if (!_checkedChildren.Add(sender.Owner.Uid) ||
sender.Owner.HasComponent<RenderingTreeComponent>()) return;
if (!_checkedChildren.Add(sender.Owner) ||
EntityManager.HasComponent<RenderingTreeComponent>(sender.Owner)) return;
// This recursive search is needed, as MoveEvent is defined to not care about indirect events like children.
// WHATEVER YOU DO, DON'T REPLACE THIS WITH SPAMMING EVENTS UNLESS YOU HAVE A GUARANTEE IT WON'T LAG THE GC.
// (Struct-based events ok though)
// Ironically this was lagging the GC lolz
if (sender.Owner.TryGetComponent(out SpriteComponent? sprite))
if (EntityManager.TryGetComponent(sender.Owner, out SpriteComponent? sprite))
QueueSpriteUpdate(sprite);
if (sender.Owner.TryGetComponent(out PointLightComponent? light))
if (EntityManager.TryGetComponent(sender.Owner, out PointLightComponent? light))
QueueLightUpdate(light);
foreach (TransformComponent child in sender.Children)
@@ -214,7 +222,7 @@ namespace Robust.Client.GameObjects
_mapManager.OnGridCreated -= MapManagerOnGridCreated;
}
private void HandleTreeRemove(EntityUid uid, RenderingTreeComponent component, ComponentRemove args)
private void OnTreeRemove(EntityUid uid, RenderingTreeComponent component, ComponentRemove args)
{
foreach (var sprite in component.SpriteTree)
{
@@ -237,27 +245,31 @@ namespace Robust.Client.GameObjects
return;
}
_mapManager.GetMapEntity(e.Map).EnsureComponent<RenderingTreeComponent>();
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetMapEntityId(e.Map));
}
private void MapManagerOnGridCreated(MapId mapId, GridId gridId)
{
EntityManager.GetEntity(_mapManager.GetGrid(gridId).GridEntityId).EnsureComponent<RenderingTreeComponent>();
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetGrid(gridId).GridEntityId);
}
internal static RenderingTreeComponent? GetRenderTree(IEntity entity)
// TODO: Pass in TransformComponent directly: mainly interested in getting this shit working atm.
private RenderingTreeComponent? GetRenderTree(EntityUid entity)
{
if (entity.Transform.MapID == MapId.Nullspace ||
entity.HasComponent<RenderingTreeComponent>()) return null;
if (!EntityManager.EntityExists(entity) ||
!EntityManager.TryGetComponent(entity, out TransformComponent? xform) ||
xform.MapID == MapId.Nullspace ||
EntityManager.HasComponent<RenderingTreeComponent>(entity)) return null;
var parent = entity.Transform.Parent?.Owner;
var parent = xform.ParentUid;
while (true)
{
if (parent == null) break;
if (!parent.IsValid())
break;
if (parent.TryGetComponent(out RenderingTreeComponent? comp)) return comp;
parent = parent.Transform.Parent?.Owner;
if (EntityManager.TryGetComponent(parent, out RenderingTreeComponent? comp)) return comp;
parent = EntityManager.GetComponent<TransformComponent>(parent).ParentUid;
}
return null;
@@ -265,7 +277,7 @@ namespace Robust.Client.GameObjects
private bool IsVisible(SpriteComponent component)
{
return component.Visible && !component.ContainerOccluded;
return component.Visible && !component.ContainerOccluded && !component.Deleted;
}
public override void FrameUpdate(float frameTime)
@@ -284,7 +296,8 @@ namespace Robust.Client.GameObjects
var oldMapTree = sprite.RenderTree;
var newMapTree = GetRenderTree(sprite.Owner);
// TODO: Temp PVS guard
var worldPos = sprite.Owner.Transform.WorldPosition;
var xform = EntityManager.GetComponent<TransformComponent>(sprite.Owner);
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
{
@@ -292,7 +305,7 @@ namespace Robust.Client.GameObjects
continue;
}
var aabb = RenderingTreeComponent.SpriteAabbFunc(sprite, worldPos);
var aabb = SpriteAabbFunc(sprite, worldPos, worldRot);
// If we're on a new map then clear the old one.
if (oldMapTree != newMapTree)
@@ -312,7 +325,7 @@ namespace Robust.Client.GameObjects
{
light.TreeUpdateQueued = false;
if (!light.Enabled || light.ContainerOccluded)
if (light.Deleted || !light.Enabled || light.ContainerOccluded)
{
ClearLight(light);
continue;
@@ -321,7 +334,7 @@ namespace Robust.Client.GameObjects
var oldMapTree = light.RenderTree;
var newMapTree = GetRenderTree(light.Owner);
// TODO: Temp PVS guard
var worldPos = light.Owner.Transform.WorldPosition;
var worldPos = EntityManager.GetComponent<TransformComponent>(light.Owner).WorldPosition;
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
{
@@ -336,7 +349,7 @@ namespace Robust.Client.GameObjects
Logger.WarningS(LoggerSawmill, $"Light radius for {light.Owner} set above max radius of {MaxLightRadius}. This may lead to pop-in.");
}
var aabb = RenderingTreeComponent.LightAabbFunc(light, worldPos);
var aabb = LightAabbFunc(light, worldPos);
// If we're on a new map then clear the old one.
if (oldMapTree != newMapTree)
@@ -355,6 +368,74 @@ namespace Robust.Client.GameObjects
_spriteQueue.Clear();
_lightQueue.Clear();
}
private Box2 SpriteAabbFunc(in SpriteComponent value)
{
var xform = EntityManager.GetComponent<TransformComponent>(value.Owner);
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
var tree = GetRenderTree(value.Owner);
if (tree == null)
{
return bounds.CalcBoundingBox();
}
else
{
return EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.TransformBox(bounds);
}
}
private Box2 LightAabbFunc(in PointLightComponent value)
{
var worldPos = EntityManager.GetComponent<TransformComponent>(value.Owner).WorldPosition;
var tree = GetRenderTree(value.Owner);
var boxSize = value.Radius * 2;
Vector2 localPos;
if (tree == null)
{
localPos = worldPos;
}
else
{
// TODO: Need a way to just cache this InvWorldMatrix
localPos = EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.Transform(worldPos);
}
return Box2.CenteredAround(localPos, (boxSize, boxSize));
}
private Box2 SpriteAabbFunc(SpriteComponent value, Vector2 worldPos, Angle worldRot)
{
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
var tree = GetRenderTree(value.Owner);
if (tree == null)
{
return bounds.CalcBoundingBox();
}
else
{
return EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.TransformBox(bounds);
}
}
private Box2 LightAabbFunc(PointLightComponent value, Vector2 worldPos)
{
// Lights are circles so don't need entity's rotation
var tree = GetRenderTree(value.Owner);
var boxSize = value.Radius * 2;
Vector2 localPos;
if (tree == null)
{
localPos = worldPos;
} else
{
localPos = EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.Transform(worldPos);
}
return Box2.CenteredAround(localPos, (boxSize, boxSize));
}
}
internal class RenderTreeRemoveLightEvent : EntityEventArgs

View File

@@ -55,7 +55,7 @@ namespace Robust.Client.GameObjects
foreach (var comp in _treeSystem.GetRenderTrees(currentMap, pvsBounds))
{
var bounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(pvsBounds);
var bounds = EntityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(pvsBounds);
comp.SpriteTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
{

View File

@@ -35,13 +35,13 @@ namespace Robust.Client.GameObjects
var player = _playerManager.LocalPlayer?.ControlledEntity;
if (player == null || !player.TryGetComponent(out PhysicsComponent? body))
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
{
_label.Visible = false;
return;
}
var screenPos = _eyeManager.WorldToScreen(player.Transform.WorldPosition);
var screenPos = _eyeManager.WorldToScreen(EntityManager.GetComponent<TransformComponent>(player.Value).WorldPosition);
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
_label.Visible = true;

View File

@@ -6,10 +6,10 @@ namespace Robust.Client.GameObjects
{
// These methods are used by the Game State Manager.
IEntity CreateEntity(string? prototypeName, EntityUid? uid = null);
EntityUid CreateEntity(string? prototypeName, EntityUid uid = default);
void InitializeEntity(IEntity entity);
void InitializeEntity(EntityUid entity);
void StartEntity(IEntity entity);
void StartEntity(EntityUid entity);
}
}

View File

@@ -18,6 +18,7 @@ using Robust.Shared.GameStates;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
@@ -39,6 +40,8 @@ namespace Robust.Client.GameStates
_pendingSystemMessages
= new();
private readonly Dictionary<EntityUid, MapId> _hiddenEntities = new();
private uint _metaCompNetId;
[Dependency] private readonly IComponentFactory _compFactory = default!;
@@ -140,8 +143,7 @@ namespace Robust.Client.GameStates
message.InputSequence = _nextInputCmdSeq;
_pendingInputs.Enqueue(message);
var inputMan = IoCManager.Resolve<IInputManager>();
inputMan.NetworkBindMap.TryGetKeyFunction(message.InputFunctionId, out var boundFunc);
_inputManager.NetworkBindMap.TryGetKeyFunction(message.InputFunctionId, out var boundFunc);
Logger.DebugS(CVars.NetPredict.Name,
$"CL> SENT tick={_timing.CurTick}, sub={_timing.TickFraction}, seq={_nextInputCmdSeq}, func={boundFunc.FunctionName}, state={message.State}");
_nextInputCmdSeq++;
@@ -338,21 +340,21 @@ namespace Robust.Client.GameStates
foreach (var entity in _entities.GetEntities())
{
// TODO: 99% there's an off-by-one here.
if (entity.Uid.IsClientSide() || entity.LastModifiedTick < curTick)
if (entity.IsClientSide() || _entityManager.GetComponent<MetaDataComponent>(entity).EntityLastModifiedTick < curTick)
{
continue;
}
Logger.DebugS(CVars.NetPredict.Name, $"Entity {entity.Uid} was made dirty.");
Logger.DebugS(CVars.NetPredict.Name, $"Entity {entity} was made dirty.");
if (!_processor.TryGetLastServerStates(entity.Uid, out var last))
if (!_processor.TryGetLastServerStates(entity, out var last))
{
// Entity was probably deleted on the server so do nothing.
continue;
}
// TODO: handle component deletions/creations.
foreach (var (netId, comp) in _entityManager.GetNetComponents(entity.Uid))
foreach (var (netId, comp) in _entityManager.GetNetComponents(entity))
{
DebugTools.AssertNotNull(netId);
@@ -379,7 +381,6 @@ namespace Robust.Client.GameStates
var outputData = new Dictionary<EntityUid, Dictionary<uint, ComponentState>>();
Debug.Assert(_players.LocalPlayer != null, "_players.LocalPlayer != null");
var player = _players.LocalPlayer.Session;
var bus = _entityManager.EventBus;
@@ -390,7 +391,7 @@ namespace Robust.Client.GameStates
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
{
var state = _entityManager.GetComponentState(bus, component, player);
var state = _entityManager.GetComponentState(bus, component);
if(state.GetType() == typeof(ComponentState))
continue;
@@ -425,44 +426,47 @@ namespace Robust.Client.GameStates
private List<EntityUid> ApplyEntityStates(ReadOnlySpan<EntityState> curEntStates, ReadOnlySpan<EntityUid> deletions,
ReadOnlySpan<EntityState> nextEntStates)
{
var toApply = new Dictionary<IEntity, (EntityState?, EntityState?)>();
var toInitialize = new List<Entity>();
var toApply = new Dictionary<EntityUid, (EntityState?, EntityState?)>();
var toInitialize = new List<EntityUid>();
var created = new List<EntityUid>();
foreach (var es in curEntStates)
{
var uid = es.Uid;
//Known entities
if (_entities.TryGetEntity(es.Uid, out var entity))
if (_entities.EntityExists(uid))
{
// Logger.Debug($"[{IGameTiming.TickStampStatic}] MOD {es.Uid}");
toApply.Add(entity, (es, null));
toApply.Add(uid, (es, null));
}
else //Unknown entities
{
var metaState = (MetaDataComponentState?) es.ComponentChanges.Value?.FirstOrDefault(c => c.NetID == _metaCompNetId).State;
if (metaState == null)
{
throw new InvalidOperationException($"Server sent new entity state for {es.Uid} without metadata component!");
throw new InvalidOperationException($"Server sent new entity state for {uid} without metadata component!");
}
// Logger.Debug($"[{IGameTiming.TickStampStatic}] CREATE {es.Uid} {metaState.PrototypeId}");
var newEntity = (Entity)_entities.CreateEntity(metaState.PrototypeId, es.Uid);
var newEntity = _entities.CreateEntity(metaState.PrototypeId, uid);
toApply.Add(newEntity, (es, null));
toInitialize.Add(newEntity);
created.Add(newEntity.Uid);
created.Add(newEntity);
}
}
foreach (var es in nextEntStates)
{
if (_entities.TryGetEntity(es.Uid, out var entity))
var uid = es.Uid;
if (_entities.EntityExists(uid))
{
if (toApply.TryGetValue(entity, out var state))
if (toApply.TryGetValue(uid, out var state))
{
toApply[entity] = (state.Item1, es);
toApply[uid] = (state.Item1, es);
}
else
{
toApply[entity] = (null, es);
toApply[uid] = (null, es);
}
}
}
@@ -471,7 +475,7 @@ namespace Robust.Client.GameStates
foreach (var kvStates in toApply)
{
var ent = kvStates.Key;
var entity = (Entity) ent;
var entity = ent;
HandleEntityState(entity, _entities.EventBus, kvStates.Value.Item1,
kvStates.Value.Item2);
}
@@ -483,7 +487,7 @@ namespace Robust.Client.GameStates
}
#if EXCEPTION_TOLERANCE
HashSet<Entity> brokenEnts = new HashSet<Entity>();
HashSet<EntityUid> brokenEnts = new HashSet<EntityUid>();
#endif
foreach (var entity in toInitialize)
@@ -497,7 +501,7 @@ namespace Robust.Client.GameStates
}
catch (Exception e)
{
Logger.ErrorS("state", $"Server entity threw in Init: uid={entity.Uid}, proto={entity.Prototype}\n{e}");
Logger.ErrorS("state", $"Server entity threw in Init: ent={_entityManager.ToPrettyString(entity)}\n{e}");
brokenEnts.Add(entity);
}
#endif
@@ -517,34 +521,27 @@ namespace Robust.Client.GameStates
}
catch (Exception e)
{
Logger.ErrorS("state", $"Server entity threw in Start: uid={entity.Uid}, proto={entity.Prototype}\n{e}");
Logger.ErrorS("state", $"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}\n{e}");
brokenEnts.Add(entity);
}
#endif
}
foreach (var entity in toInitialize)
{
#if EXCEPTION_TOLERANCE
if (brokenEnts.Contains(entity))
continue;
#endif
}
#if EXCEPTION_TOLERANCE
foreach (var entity in brokenEnts)
{
entity.Delete();
_entityManager.DeleteEntity(entity);
}
#endif
return created;
}
private void HandleEntityState(IEntity entity, IEventBus bus, EntityState? curState,
private void HandleEntityState(EntityUid entity, IEventBus bus, EntityState? curState,
EntityState? nextState)
{
var compStateWork = new Dictionary<ushort, (ComponentState? curState, ComponentState? nextState)>();
var entityUid = entity.Uid;
var entityUid = entity;
if (curState != null)
{
@@ -595,7 +592,7 @@ namespace Robust.Client.GameStates
foreach (var (netId, (cur, next)) in compStateWork)
{
if (_entityManager.TryGetComponent(entityUid, (ushort) netId, out var component))
if (_entityManager.TryGetComponent(entityUid, netId, out var component))
{
try
{

View File

@@ -9,6 +9,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -109,7 +110,8 @@ namespace Robust.Client.GameStates
if(_entityManager.EntityExists(netEnt.Id))
{
//TODO: Whoever is working on PVS remake, change the InPVS detection.
var position = _entityManager.GetEntity(netEnt.Id).Transform.MapPosition;
var uid = netEnt.Id;
var position = _entityManager.GetComponent<TransformComponent>(uid).MapPosition;
netEnt.InPVS = !pvsEnabled || (pvsBox.Contains(position.Position) && position.MapId == pvsCenter.MapId);
_netEnts[i] = netEnt; // copy struct back
continue;
@@ -166,8 +168,9 @@ namespace Robust.Client.GameStates
for (int i = 0; i < _netEnts.Count; i++)
{
var netEnt = _netEnts[i];
var uid = netEnt.Id;
if (!_entityManager.TryGetEntity(netEnt.Id, out var ent))
if (!_entityManager.EntityExists(uid))
{
_netEnts.RemoveSwap(i);
i--;
@@ -176,7 +179,7 @@ namespace Robust.Client.GameStates
var xPos = 100;
var yPos = 10 + _lineHeight * i;
var name = $"({netEnt.Id}) {ent.Prototype?.ID}";
var name = $"({netEnt.Id}) {_entityManager.GetComponent<MetaDataComponent>(uid).EntityPrototype?.ID}";
var color = CalcTextColor(ref netEnt);
screenHandle.DrawString(_font, new Vector2(xPos + (TrafficHistorySize + 4), yPos), name, color);
DrawTrafficBox(screenHandle, ref netEnt, xPos, yPos);

View File

@@ -35,7 +35,7 @@ namespace Robust.Client.GameStates
foreach (var boundingBox in _entityManager.EntityQuery<IPhysBody>(true))
{
// all entities have a TransformComponent
var transform = boundingBox.Owner.Transform;
var transform = _entityManager.GetComponent<TransformComponent>(boundingBox.Owner);
// if not on the same map, continue
if (transform.MapID != _eyeManager.CurrentMap || boundingBox.Owner.IsInContainer())

View File

@@ -0,0 +1,59 @@
using System.Collections.Concurrent;
using OpenToolkit.Audio.OpenAL;
namespace Robust.Client.Graphics.Audio
{
internal partial class ClydeAudio
{
// Used to track audio sources that were disposed in the finalizer thread,
// so we need to properly send them off in the main thread.
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _sourceDisposeQueue = new();
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _bufferedSourceDisposeQueue = new();
private readonly ConcurrentQueue<int> _bufferDisposeQueue = new();
private void _flushALDisposeQueues()
{
// Clear out finalized audio sources.
while (_sourceDisposeQueue.TryDequeue(out var handles))
{
_openALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
if (IsEfxSupported) RemoveEfx(handles);
AL.DeleteSource(handles.sourceHandle);
_checkAlError();
_audioSources.Remove(handles.sourceHandle);
}
// Clear out finalized buffered audio sources.
while (_bufferedSourceDisposeQueue.TryDequeue(out var handles))
{
_openALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
if (IsEfxSupported) RemoveEfx(handles);
AL.DeleteSource(handles.sourceHandle);
_checkAlError();
_bufferedAudioSources.Remove(handles.sourceHandle);
}
// Clear out finalized audio buffers.
while (_bufferDisposeQueue.TryDequeue(out var handle))
{
AL.DeleteBuffer(handle);
_checkAlError();
}
}
private void DeleteSourceOnMainThread(int sourceHandle, int filterHandle)
{
_sourceDisposeQueue.Enqueue((sourceHandle, filterHandle));
}
private void DeleteBufferedSourceOnMainThread(int bufferedSourceHandle, int filterHandle)
{
_bufferedSourceDisposeQueue.Enqueue((bufferedSourceHandle, filterHandle));
}
private void DeleteAudioBufferOnMainThread(int bufferHandle)
{
_bufferDisposeQueue.Enqueue(bufferHandle);
}
}
}

View File

@@ -10,436 +10,20 @@ using OpenToolkit.Audio.OpenAL.Extensions.Creative.EFX;
using OpenToolkit.Mathematics;
using Robust.Client.Audio;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Audio;
using Robust.Shared.Log;
using Vector2 = Robust.Shared.Maths.Vector2;
namespace Robust.Client.Graphics.Clyde
namespace Robust.Client.Graphics.Audio
{
internal partial class Clyde
internal partial class ClydeAudio
{
private ALDevice _openALDevice;
private ALContext _openALContext;
private readonly List<LoadedAudioSample> _audioSampleBuffers = new();
private readonly Dictionary<int, WeakReference<AudioSource>> _audioSources =
new();
private readonly Dictionary<int, WeakReference<BufferedAudioSource>> _bufferedAudioSources =
new();
private readonly HashSet<string> _alcDeviceExtensions = new();
private readonly HashSet<string> _alContextExtensions = new();
// Used to track audio sources that were disposed in the finalizer thread,
// so we need to properly send them off in the main thread.
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _sourceDisposeQueue = new();
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _bufferedSourceDisposeQueue = new();
private readonly ConcurrentQueue<int> _bufferDisposeQueue = new();
// The base gain value for a listener, used to boost the default volume.
private const float _baseGain = 2f;
public bool HasAlDeviceExtension(string extension) => _alcDeviceExtensions.Contains(extension);
public bool HasAlContextExtension(string extension) => _alContextExtensions.Contains(extension);
internal bool IsEfxSupported;
private ISawmill _openALSawmill = default!;
private void _initializeAudio()
{
_openALSawmill = Logger.GetSawmill("clyde.oal");
_audioOpenDevice();
// Create OpenAL context.
_audioCreateContext();
IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX");
_cfg.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true);
_cfg.OnValueChanged(CVars.AudioAttenuation, SetAudioAttenuation, true);
}
private void _audioCreateContext()
{
unsafe
{
_openALContext = ALC.CreateContext(_openALDevice, (int*) 0);
}
ALC.MakeContextCurrent(_openALContext);
_checkAlcError(_openALDevice);
_checkAlError();
// Load up AL context extensions.
var s = ALC.GetString(ALDevice.Null, AlcGetString.Extensions) ?? "";
foreach (var extension in s.Split(' '))
{
_alContextExtensions.Add(extension);
}
_openALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
_openALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
_openALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version));
}
private void _audioOpenDevice()
{
var preferredDevice = _cfg.GetCVar(CVars.AudioDevice);
// Open device.
if (!string.IsNullOrEmpty(preferredDevice))
{
_openALDevice = ALC.OpenDevice(preferredDevice);
if (_openALDevice == IntPtr.Zero)
{
_openALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
preferredDevice, ALC.GetError(ALDevice.Null));
_openALDevice = ALC.OpenDevice(null);
}
}
else
{
_openALDevice = ALC.OpenDevice(null);
}
_checkAlcError(_openALDevice);
if (_openALDevice == IntPtr.Zero)
{
throw new InvalidOperationException($"Unable to open OpenAL device! {ALC.GetError(ALDevice.Null)}");
}
// Load up ALC extensions.
var s = ALC.GetString(_openALDevice, AlcGetString.Extensions) ?? "";
foreach (var extension in s.Split(' '))
{
_alcDeviceExtensions.Add(extension);
}
}
private void _shutdownAudio()
{
foreach (var source in _audioSources.Values.ToArray())
{
if (source.TryGetTarget(out var target))
{
target.Dispose();
}
}
foreach (var source in _bufferedAudioSources.Values.ToArray())
{
if (source.TryGetTarget(out var target))
{
target.Dispose();
}
}
if (_openALContext != ALContext.Null)
{
ALC.DestroyContext(_openALContext);
}
if (_openALDevice != IntPtr.Zero)
{
ALC.CloseDevice(_openALDevice);
}
}
private void _updateAudio()
{
var eye = _eyeManager.CurrentEye;
var (x, y) = eye.Position.Position;
AL.Listener(ALListener3f.Position, x, y, -5);
var rot2d = eye.Rotation.ToVec();
AL.Listener(ALListenerfv.Orientation, new []{0, 0, -1, rot2d.X, rot2d.Y, 0});
// Default orientation: at: (0, 0, -1) up: (0, 1, 0)
var (rotX, rotY) = eye.Rotation.ToVec();
var at = new Vector3(0f, 0f, -1f);
var up = new Vector3(rotY, rotX, 0f);
AL.Listener(ALListenerfv.Orientation, ref at, ref up);
// Clear out finalized audio sources.
while (_sourceDisposeQueue.TryDequeue(out var handles))
{
_openALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
if (IsEfxSupported) RemoveEfx(handles);
AL.DeleteSource(handles.sourceHandle);
_checkAlError();
_audioSources.Remove(handles.sourceHandle);
}
// Clear out finalized buffered audio sources.
while (_bufferedSourceDisposeQueue.TryDequeue(out var handles))
{
_openALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
if (IsEfxSupported) RemoveEfx(handles);
AL.DeleteSource(handles.sourceHandle);
_checkAlError();
_bufferedAudioSources.Remove(handles.sourceHandle);
}
// Clear out finalized audio buffers.
while (_bufferDisposeQueue.TryDequeue(out var handle))
{
AL.DeleteBuffer((int) handle);
_checkAlError();
}
}
private static void RemoveEfx((int sourceHandle, int filterHandle) handles)
{
if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle);
}
public void SetMasterVolume(float newVolume)
{
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
}
public void SetAudioAttenuation(int value)
{
var attenuation = (Attenuation) value;
switch (attenuation)
{
case Attenuation.NoAttenuation:
AL.DistanceModel(ALDistanceModel.None);
break;
case Attenuation.InverseDistance:
AL.DistanceModel(ALDistanceModel.InverseDistance);
break;
case Attenuation.Default:
case Attenuation.InverseDistanceClamped:
AL.DistanceModel(ALDistanceModel.InverseDistanceClamped);
break;
case Attenuation.LinearDistance:
AL.DistanceModel(ALDistanceModel.LinearDistance);
break;
case Attenuation.LinearDistanceClamped:
AL.DistanceModel(ALDistanceModel.LinearDistanceClamped);
break;
case Attenuation.ExponentDistance:
AL.DistanceModel(ALDistanceModel.ExponentDistance);
break;
case Attenuation.ExponentDistanceClamped:
AL.DistanceModel(ALDistanceModel.ExponentDistanceClamped);
break;
default:
throw new ArgumentOutOfRangeException($"No implementation to set {attenuation.ToString()} for DistanceModel!");
}
var attToString = attenuation == Attenuation.Default ? Attenuation.InverseDistanceClamped : attenuation;
_openALSawmill.Info($"Set audio attenuation to {attToString.ToString()}");
}
public IClydeAudioSource CreateAudioSource(AudioStream stream)
{
var source = AL.GenSource();
// ReSharper disable once PossibleInvalidOperationException
// TODO: This really shouldn't be indexing based on the ClydeHandle...
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value.Value].BufferHandle);
var audioSource = new AudioSource(this, source, stream);
_audioSources.Add(source, new WeakReference<AudioSource>(audioSource));
return audioSource;
}
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false)
{
var source = AL.GenSource();
// ReSharper disable once PossibleInvalidOperationException
var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio);
_bufferedAudioSources.Add(source, new WeakReference<BufferedAudioSource>(audioSource));
return audioSource;
}
private void _checkAlcError(ALDevice device,
[CallerMemberName] string callerMember = "",
[CallerLineNumber] int callerLineNumber = -1)
{
var error = ALC.GetError(device);
if (error != AlcError.NoError)
{
_openALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
}
}
private void _checkAlError([CallerMemberName] string callerMember = "",
[CallerLineNumber] int callerLineNumber = -1)
{
var error = AL.GetError();
if (error != ALError.NoError)
{
_openALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
}
}
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
{
var vorbis = _readOggVorbis(stream);
var buffer = AL.GenBuffer();
ALFormat format;
// NVorbis only supports loading into floats.
// If this becomes a problem due to missing extension support (doubt it but ok),
// check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM.
if (vorbis.Channels == 1)
{
format = ALFormat.MonoFloat32Ext;
}
else if (vorbis.Channels == 2)
{
format = ALFormat.StereoFloat32Ext;
}
else
{
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
}
unsafe
{
fixed (float* ptr = vorbis.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float),
(int) vorbis.SampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
return new AudioStream(handle, length, (int) vorbis.Channels, name);
}
public AudioStream LoadAudioWav(Stream stream, string? name = null)
{
var wav = _readWav(stream);
var buffer = AL.GenBuffer();
ALFormat format;
if (wav.BitsPerSample == 16)
{
if (wav.NumChannels == 1)
{
format = ALFormat.Mono16;
}
else if (wav.NumChannels == 2)
{
format = ALFormat.Stereo16;
}
else
{
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
}
}
else if (wav.BitsPerSample == 8)
{
if (wav.NumChannels == 1)
{
format = ALFormat.Mono8;
}
else if (wav.NumChannels == 2)
{
format = ALFormat.Stereo8;
}
else
{
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
}
}
else
{
throw new InvalidOperationException("Unable to load wav with bits per sample different from 8 or 16");
}
unsafe
{
fixed (byte* ptr = wav.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, wav.Data.Length, wav.SampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate);
return new AudioStream(handle, length, wav.NumChannels, name);
}
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
{
var fmt = channels switch
{
1 => ALFormat.Mono16,
2 => ALFormat.Stereo16,
_ => throw new ArgumentOutOfRangeException(
nameof(channels), "Only stereo and mono is currently supported")
};
var buffer = AL.GenBuffer();
_checkAlError();
unsafe
{
fixed (short* ptr = samples)
{
AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
return new AudioStream(handle, length, channels, name);
}
private sealed class LoadedAudioSample
{
public readonly int BufferHandle;
public LoadedAudioSample(int bufferHandle)
{
BufferHandle = bufferHandle;
}
}
private void DeleteSourceOnMainThread(int sourceHandle, int filterHandle)
{
_sourceDisposeQueue.Enqueue((sourceHandle, filterHandle));
}
private void DeleteBufferedSourceOnMainThread(int bufferedSourceHandle, int filterHandle)
{
_bufferedSourceDisposeQueue.Enqueue((bufferedSourceHandle, filterHandle));
}
private void DeleteAudioBufferOnMainThread(int bufferHandle)
{
_bufferDisposeQueue.Enqueue(bufferHandle);
}
private sealed class AudioSource : IClydeAudioSource
{
private int SourceHandle;
private readonly Clyde _master;
private readonly ClydeAudio _master;
private readonly AudioStream _sourceStream;
private int FilterHandle;
#if DEBUG
@@ -450,7 +34,7 @@ namespace Robust.Client.Graphics.Clyde
private bool IsEfxSupported => _master.IsEfxSupported;
public AudioSource(Clyde master, int sourceHandle, AudioStream sourceStream)
public AudioSource(ClydeAudio master, int sourceHandle, AudioStream sourceStream)
{
_master = master;
SourceHandle = sourceHandle;
@@ -700,7 +284,7 @@ namespace Robust.Client.Graphics.Clyde
private int? SourceHandle = null;
private int[] BufferHandles;
private Dictionary<int, int> BufferMap = new();
private readonly Clyde _master;
private readonly ClydeAudio _master;
private bool _mono = true;
private bool _float = false;
private int FilterHandle;
@@ -711,7 +295,7 @@ namespace Robust.Client.Graphics.Clyde
private bool IsEfxSupported => _master.IsEfxSupported;
public BufferedAudioSource(Clyde master, int sourceHandle, int[] bufferHandles, bool floatAudio = false)
public BufferedAudioSource(ClydeAudio master, int sourceHandle, int[] bufferHandles, bool floatAudio = false)
{
_master = master;
SourceHandle = sourceHandle;

View File

@@ -1,9 +1,9 @@
using System;
using System.IO;
namespace Robust.Client.Graphics.Clyde
namespace Robust.Client.Graphics.Audio
{
internal partial class Clyde
internal partial class ClydeAudio
{
private OggVorbisData _readOggVorbis(Stream stream)
{

View File

@@ -3,9 +3,9 @@ using System.IO;
using JetBrains.Annotations;
using Robust.Shared.Utility;
namespace Robust.Client.Graphics.Clyde
namespace Robust.Client.Graphics.Audio
{
internal partial class Clyde
internal partial class ClydeAudio
{
/// <summary>
/// Load up a WAVE file.

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using OpenToolkit.Audio.OpenAL;
using OpenToolkit.Audio.OpenAL.Extensions.Creative.EFX;
using OpenToolkit.Mathematics;
using Robust.Client.Audio;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Audio;
using Robust.Shared.Log;
using Robust.Shared.Timing;
using Vector2 = Robust.Shared.Maths.Vector2;
namespace Robust.Client.Graphics.Audio
{
internal partial class ClydeAudio
{
[Robust.Shared.IoC.Dependency] private readonly IConfigurationManager _cfg = default!;
[Robust.Shared.IoC.Dependency] private readonly IEyeManager _eyeManager = default!;
private Thread? _gameThread;
public bool InitializePostWindowing()
{
_gameThread = Thread.CurrentThread;
return _initializeAudio();
}
public void FrameProcess(FrameEventArgs eventArgs)
{
_updateAudio();
}
public void Shutdown()
{
_shutdownAudio();
}
private bool IsMainThread()
{
return Thread.CurrentThread == _gameThread;
}
}
}

View File

@@ -0,0 +1,399 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using OpenToolkit.Audio.OpenAL;
using OpenToolkit.Audio.OpenAL.Extensions.Creative.EFX;
using OpenToolkit.Mathematics;
using Robust.Client.Audio;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Audio;
using Robust.Shared.Log;
using Vector2 = Robust.Shared.Maths.Vector2;
namespace Robust.Client.Graphics.Audio
{
internal partial class ClydeAudio : IClydeAudio, IClydeAudioInternal
{
private ALDevice _openALDevice;
private ALContext _openALContext;
private readonly List<LoadedAudioSample> _audioSampleBuffers = new();
private readonly Dictionary<int, WeakReference<AudioSource>> _audioSources =
new();
private readonly Dictionary<int, WeakReference<BufferedAudioSource>> _bufferedAudioSources =
new();
private readonly HashSet<string> _alcDeviceExtensions = new();
private readonly HashSet<string> _alContextExtensions = new();
// The base gain value for a listener, used to boost the default volume.
private const float _baseGain = 2f;
public bool HasAlDeviceExtension(string extension) => _alcDeviceExtensions.Contains(extension);
public bool HasAlContextExtension(string extension) => _alContextExtensions.Contains(extension);
internal bool IsEfxSupported;
private ISawmill _openALSawmill = default!;
private bool _initializeAudio()
{
_openALSawmill = Logger.GetSawmill("clyde.oal");
if (!_audioOpenDevice())
return false;
// Create OpenAL context.
_audioCreateContext();
IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX");
_cfg.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true);
_cfg.OnValueChanged(CVars.AudioAttenuation, SetAudioAttenuation, true);
return true;
}
private void _audioCreateContext()
{
unsafe
{
_openALContext = ALC.CreateContext(_openALDevice, (int*) 0);
}
ALC.MakeContextCurrent(_openALContext);
_checkAlcError(_openALDevice);
_checkAlError();
// Load up AL context extensions.
var s = ALC.GetString(ALDevice.Null, AlcGetString.Extensions) ?? "";
foreach (var extension in s.Split(' '))
{
_alContextExtensions.Add(extension);
}
_openALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
_openALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
_openALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version));
}
private bool _audioOpenDevice()
{
var preferredDevice = _cfg.GetCVar(CVars.AudioDevice);
// Open device.
if (!string.IsNullOrEmpty(preferredDevice))
{
_openALDevice = ALC.OpenDevice(preferredDevice);
if (_openALDevice == IntPtr.Zero)
{
_openALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
preferredDevice, ALC.GetError(ALDevice.Null));
_openALDevice = ALC.OpenDevice(null);
}
}
else
{
_openALDevice = ALC.OpenDevice(null);
}
_checkAlcError(_openALDevice);
if (_openALDevice == IntPtr.Zero)
{
_openALSawmill.Error("Unable to open OpenAL device! {1}", ALC.GetError(ALDevice.Null));
return false;
}
// Load up ALC extensions.
var s = ALC.GetString(_openALDevice, AlcGetString.Extensions) ?? "";
foreach (var extension in s.Split(' '))
{
_alcDeviceExtensions.Add(extension);
}
return true;
}
private void _shutdownAudio()
{
foreach (var source in _audioSources.Values.ToArray())
{
if (source.TryGetTarget(out var target))
{
target.Dispose();
}
}
foreach (var source in _bufferedAudioSources.Values.ToArray())
{
if (source.TryGetTarget(out var target))
{
target.Dispose();
}
}
if (_openALContext != ALContext.Null)
{
ALC.DestroyContext(_openALContext);
}
if (_openALDevice != IntPtr.Zero)
{
ALC.CloseDevice(_openALDevice);
}
}
private void _updateAudio()
{
var eye = _eyeManager.CurrentEye;
var (x, y) = eye.Position.Position;
AL.Listener(ALListener3f.Position, x, y, -5);
var rot2d = eye.Rotation.ToVec();
AL.Listener(ALListenerfv.Orientation, new []{0, 0, -1, rot2d.X, rot2d.Y, 0});
// Default orientation: at: (0, 0, -1) up: (0, 1, 0)
var (rotX, rotY) = eye.Rotation.ToVec();
var at = new Vector3(0f, 0f, -1f);
var up = new Vector3(rotY, rotX, 0f);
AL.Listener(ALListenerfv.Orientation, ref at, ref up);
_flushALDisposeQueues();
}
private static void RemoveEfx((int sourceHandle, int filterHandle) handles)
{
if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle);
}
public void SetMasterVolume(float newVolume)
{
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
}
public void SetAudioAttenuation(int value)
{
var attenuation = (Attenuation) value;
switch (attenuation)
{
case Attenuation.NoAttenuation:
AL.DistanceModel(ALDistanceModel.None);
break;
case Attenuation.InverseDistance:
AL.DistanceModel(ALDistanceModel.InverseDistance);
break;
case Attenuation.Default:
case Attenuation.InverseDistanceClamped:
AL.DistanceModel(ALDistanceModel.InverseDistanceClamped);
break;
case Attenuation.LinearDistance:
AL.DistanceModel(ALDistanceModel.LinearDistance);
break;
case Attenuation.LinearDistanceClamped:
AL.DistanceModel(ALDistanceModel.LinearDistanceClamped);
break;
case Attenuation.ExponentDistance:
AL.DistanceModel(ALDistanceModel.ExponentDistance);
break;
case Attenuation.ExponentDistanceClamped:
AL.DistanceModel(ALDistanceModel.ExponentDistanceClamped);
break;
default:
throw new ArgumentOutOfRangeException($"No implementation to set {attenuation.ToString()} for DistanceModel!");
}
var attToString = attenuation == Attenuation.Default ? Attenuation.InverseDistanceClamped : attenuation;
_openALSawmill.Info($"Set audio attenuation to {attToString.ToString()}");
}
public IClydeAudioSource CreateAudioSource(AudioStream stream)
{
var source = AL.GenSource();
// ReSharper disable once PossibleInvalidOperationException
// TODO: This really shouldn't be indexing based on the ClydeHandle...
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value.Value].BufferHandle);
var audioSource = new AudioSource(this, source, stream);
_audioSources.Add(source, new WeakReference<AudioSource>(audioSource));
return audioSource;
}
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false)
{
var source = AL.GenSource();
// ReSharper disable once PossibleInvalidOperationException
var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio);
_bufferedAudioSources.Add(source, new WeakReference<BufferedAudioSource>(audioSource));
return audioSource;
}
private void _checkAlcError(ALDevice device,
[CallerMemberName] string callerMember = "",
[CallerLineNumber] int callerLineNumber = -1)
{
var error = ALC.GetError(device);
if (error != AlcError.NoError)
{
_openALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
}
}
private void _checkAlError([CallerMemberName] string callerMember = "",
[CallerLineNumber] int callerLineNumber = -1)
{
var error = AL.GetError();
if (error != ALError.NoError)
{
_openALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
}
}
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
{
var vorbis = _readOggVorbis(stream);
var buffer = AL.GenBuffer();
ALFormat format;
// NVorbis only supports loading into floats.
// If this becomes a problem due to missing extension support (doubt it but ok),
// check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM.
if (vorbis.Channels == 1)
{
format = ALFormat.MonoFloat32Ext;
}
else if (vorbis.Channels == 2)
{
format = ALFormat.StereoFloat32Ext;
}
else
{
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
}
unsafe
{
fixed (float* ptr = vorbis.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float),
(int) vorbis.SampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
return new AudioStream(handle, length, (int) vorbis.Channels, name);
}
public AudioStream LoadAudioWav(Stream stream, string? name = null)
{
var wav = _readWav(stream);
var buffer = AL.GenBuffer();
ALFormat format;
if (wav.BitsPerSample == 16)
{
if (wav.NumChannels == 1)
{
format = ALFormat.Mono16;
}
else if (wav.NumChannels == 2)
{
format = ALFormat.Stereo16;
}
else
{
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
}
}
else if (wav.BitsPerSample == 8)
{
if (wav.NumChannels == 1)
{
format = ALFormat.Mono8;
}
else if (wav.NumChannels == 2)
{
format = ALFormat.Stereo8;
}
else
{
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
}
}
else
{
throw new InvalidOperationException("Unable to load wav with bits per sample different from 8 or 16");
}
unsafe
{
fixed (byte* ptr = wav.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, wav.Data.Length, wav.SampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate);
return new AudioStream(handle, length, wav.NumChannels, name);
}
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
{
var fmt = channels switch
{
1 => ALFormat.Mono16,
2 => ALFormat.Stereo16,
_ => throw new ArgumentOutOfRangeException(
nameof(channels), "Only stereo and mono is currently supported")
};
var buffer = AL.GenBuffer();
_checkAlError();
unsafe
{
fixed (short* ptr = samples)
{
AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
return new AudioStream(handle, length, channels, name);
}
private sealed class LoadedAudioSample
{
public readonly int BufferHandle;
public LoadedAudioSample(int bufferHandle)
{
BufferHandle = bufferHandle;
}
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Input;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Color = Robust.Shared.Maths.Color;
namespace Robust.Client.Graphics.Audio
{
/// <summary>
/// Hey look, it's ClydeAudio's evil twin brother!
/// </summary>
[UsedImplicitly]
internal sealed class ClydeAudioHeadless : IClydeAudio, IClydeAudioInternal
{
public bool InitializePostWindowing()
{
return true;
}
public void FrameProcess(FrameEventArgs eventArgs)
{
}
public void Shutdown()
{
}
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
{
// TODO: Might wanna actually load this so the length gets reported correctly.
return new(default, default, 1, name);
}
public AudioStream LoadAudioWav(Stream stream, string? name = null)
{
// TODO: Might wanna actually load this so the length gets reported correctly.
return new(default, default, 1, name);
}
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
{
// TODO: Might wanna actually load this so the length gets reported correctly.
return new(default, default, channels, name);
}
public IClydeAudioSource CreateAudioSource(AudioStream stream)
{
return DummyAudioSource.Instance;
}
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false)
{
return DummyBufferedAudioSource.Instance;
}
public void SetMasterVolume(float newVolume)
{
// Nada.
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Input;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Color = Robust.Shared.Maths.Color;
namespace Robust.Client.Graphics.Audio
{
/// <summary>
/// Hey look, it's ClydeAudio.AudioSource's evil twin brother!
/// </summary>
internal class DummyAudioSource : IClydeAudioSource
{
public static DummyAudioSource Instance { get; } = new();
public bool IsPlaying => default;
public bool IsLooping { get; set; }
public void Dispose()
{
// Nada.
}
public void StartPlaying()
{
// Nada.
}
public void StopPlaying()
{
// Nada.
}
public bool SetPosition(Vector2 position)
{
return true;
}
public void SetPitch(float pitch)
{
// Nada.
}
public void SetGlobal()
{
// Nada.
}
public void SetVolume(float decibels)
{
// Nada.
}
public void SetVolumeDirect(float scale)
{
// Nada.
}
public void SetMaxDistance(float maxDistance)
{
// Nada.
}
public void SetRolloffFactor(float rolloffFactor)
{
// Nada.
}
public void SetReferenceDistance(float refDistance)
{
// Nada.
}
public void SetOcclusion(float blocks)
{
// Nada.
}
public void SetPlaybackPosition(float seconds)
{
// Nada.
}
public void SetVelocity(Vector2 velocity)
{
// Nada.
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Input;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Color = Robust.Shared.Maths.Color;
namespace Robust.Client.Graphics.Audio
{
/// <summary>
/// Hey look, it's ClydeAudio.BufferedAudioSource's evil twin brother!
/// </summary>
internal sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource
{
public new static DummyBufferedAudioSource Instance { get; } = new();
public int SampleRate { get; set; } = 0;
public void WriteBuffer(int handle, ReadOnlySpan<ushort> data)
{
// Nada.
}
public void WriteBuffer(int handle, ReadOnlySpan<float> data)
{
// Nada.
}
public void QueueBuffers(ReadOnlySpan<int> handles)
{
// Nada.
}
public void EmptyBuffers()
{
// Nada.
}
public void GetBuffersProcessed(Span<int> handles)
{
// Nada.
}
public int GetNumberOfBuffersProcessed()
{
return 0;
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Input;
using Robust.Shared.Timing;
using Robust.Shared.IoC;
namespace Robust.Client.Graphics.Audio
{
/// <summary>
/// For "start ss14 with no audio devices" Smugleaf
/// </summary>
[UsedImplicitly]
internal sealed class FallbackProxyClydeAudio : ProxyClydeAudio
{
public override bool InitializePostWindowing()
{
// Deliberate lack of base call here (see base implementation for comments as to why there even is a base)
ActualImplementation = new ClydeAudio();
IoCManager.InjectDependencies(ActualImplementation);
if (ActualImplementation.InitializePostWindowing())
return true;
// If we get here, that failed, so use the fallback
ActualImplementation = new ClydeAudioHeadless();
IoCManager.InjectDependencies(ActualImplementation);
return ActualImplementation.InitializePostWindowing();
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Input;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Color = Robust.Shared.Maths.Color;
namespace Robust.Client.Graphics.Audio
{
/// <summary>
/// For "start ss14 with no audio devices" Smugleaf
/// </summary>
[UsedImplicitly]
internal abstract class ProxyClydeAudio : IClydeAudio, IClydeAudioInternal
{
protected IClydeAudioInternal ActualImplementation = default!;
public virtual bool InitializePostWindowing()
{
// This particular implementation exists to be overridden because removing this method causes C# to complain
return ActualImplementation.InitializePostWindowing();
}
public void FrameProcess(FrameEventArgs eventArgs)
{
ActualImplementation.FrameProcess(eventArgs);
}
public void Shutdown()
{
ActualImplementation.Shutdown();
}
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
{
return ActualImplementation.LoadAudioOggVorbis(stream, name);
}
public AudioStream LoadAudioWav(Stream stream, string? name = null)
{
return ActualImplementation.LoadAudioWav(stream, name);
}
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
{
return ActualImplementation.LoadAudioRaw(samples, channels, sampleRate, name);
}
public IClydeAudioSource CreateAudioSource(AudioStream stream)
{
return ActualImplementation.CreateAudioSource(stream);
}
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false)
{
return ActualImplementation.CreateBufferedAudioSource(buffers, floatAudio);
}
public void SetMasterVolume(float newVolume)
{
ActualImplementation.SetMasterVolume(newVolume);
}
}
}

View File

@@ -15,17 +15,11 @@ namespace Robust.Client.Graphics.Clyde
RuntimeInformation.ProcessArchitecture == Architecture.X64 &&
Environment.GetEnvironmentVariable("ROBUST_INTEGRATED_GPU") != "1")
{
try
{
// We force load nvapi64.dll so nvidia gives us the dedicated GPU on optimus laptops.
// This is 100x easier than nvidia's documented approach of NvOptimusEnablement,
// and works while developing.
NativeLibrary.Load("nvapi64.dll");
}
catch (Exception)
{
// If this fails whatever.
}
// We force load nvapi64.dll so nvidia gives us the dedicated GPU on optimus laptops.
// This is 100x easier than nvidia's documented approach of NvOptimusEnablement,
// and works while developing.
NativeLibrary.TryLoad("nvapi64.dll", out _);
// If this fails whatever.
}
if (OperatingSystem.IsWindows())

View File

@@ -74,7 +74,8 @@ namespace Robust.Client.Graphics.Clyde
case DEventWindowFocus(var args):
OnWindowFocused?.Invoke(args);
break;
case DEventWindowResized(var args):
case DEventWindowResized(var reg, var args):
reg.Resized?.Invoke(args);
OnWindowResized?.Invoke(args);
break;
}
@@ -112,7 +113,7 @@ namespace Robust.Client.Graphics.Clyde
reg.FramebufferSize,
reg.Handle);
_eventDispatchQueue.Enqueue(new DEventWindowResized(eventArgs));
_eventDispatchQueue.Enqueue(new DEventWindowResized(reg, eventArgs));
}
private void SendWindowContentScaleChanged(WindowContentScaleEventArgs ev)
@@ -151,7 +152,7 @@ namespace Robust.Client.Graphics.Clyde
private sealed record DEventWindowClosed(WindowReg Reg, WindowRequestClosedEventArgs Args) : DEventBase;
private sealed record DEventWindowResized(WindowResizedEventArgs Args) : DEventBase;
private sealed record DEventWindowResized(WindowReg Reg, WindowResizedEventArgs Args) : DEventBase;
private sealed record DEventWindowContentScaleChanged(WindowContentScaleEventArgs Args) : DEventBase;

View File

@@ -16,7 +16,6 @@ namespace Robust.Client.Graphics.Clyde
// Advanced GL contexts currently disabled due to lack of testing etc.
if (OperatingSystem.IsWindows() && _cfg.GetCVar(CVars.DisplayAngle))
{
/*
if (_cfg.GetCVar(CVars.DisplayAngleCustomSwapChain))
{
_sawmillOgl.Debug("Trying custom swap chain ANGLE.");
@@ -31,7 +30,6 @@ namespace Robust.Client.Graphics.Clyde
return;
}
}
*/
/*
if (_cfg.GetCVar(CVars.DisplayEgl))

View File

@@ -12,6 +12,8 @@ using OpenToolkit.Graphics.OpenGL4;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Client.Graphics.Clyde
{
@@ -372,14 +374,14 @@ namespace Robust.Client.Graphics.Clyde
{
foreach (var comp in _entitySystemManager.GetEntitySystem<RenderingTreeSystem>().GetRenderTrees(map, worldBounds))
{
var bounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(worldBounds);
var bounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
comp.SpriteTree.QueryAabb(ref list, (
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
in SpriteComponent value) =>
{
var entity = value.Owner;
var transform = entity.Transform;
var transform = _entityManager.GetComponent<TransformComponent>(entity);
ref var entry = ref state.AllocAdd();
entry.sprite = value;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Buffers;
using OpenToolkit.Graphics.OpenGL4;
@@ -6,6 +6,7 @@ using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -329,7 +330,8 @@ namespace Robust.Client.Graphics.Clyde
var mapId = eye.Position.MapId;
// If this map has lighting disabled, return
if (!_mapManager.GetMapEntity(mapId).GetComponent<IMapComponent>().LightingEnabled)
var mapUid = _mapManager.GetMapEntityId(mapId);
if (!_entityManager.GetComponent<IMapComponent>(mapUid).LightingEnabled)
{
return;
}
@@ -413,7 +415,7 @@ namespace Robust.Client.Graphics.Clyde
{
var (component, lightPos, _) = lights[i];
var transform = component.Owner.Transform;
var transform = _entityManager.GetComponent<TransformComponent>(component.Owner);
Texture? mask = null;
var rotation = Angle.Zero;
@@ -516,7 +518,7 @@ namespace Robust.Client.Graphics.Clyde
foreach (var comp in renderingTreeSystem.GetRenderTrees(map, enlargedBounds))
{
var bounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(worldBounds);
var bounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
comp.LightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldAABB, int count) state, in PointLightComponent light) =>
{
@@ -526,7 +528,7 @@ namespace Robust.Client.Graphics.Clyde
return false;
}
var transform = light.Owner.Transform;
var transform = _entityManager.GetComponent<TransformComponent>(light.Owner);
if (float.IsNaN(transform.LocalPosition.X) || float.IsNaN(transform.LocalPosition.Y)) return true;
@@ -871,12 +873,12 @@ namespace Robust.Client.Graphics.Clyde
foreach (var comp in occluderSystem.GetOccluderTrees(map, expandedBounds))
{
var treeBounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(expandedBounds);
var treeBounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(expandedBounds);
comp.Tree.QueryAabb((in OccluderComponent sOccluder) =>
{
var occluder = (ClientOccluderComponent)sOccluder;
var transform = occluder.Owner.Transform;
var transform = _entityManager.GetComponent<TransformComponent>(occluder.Owner);
if (!occluder.Enabled)
{
return true;
@@ -927,10 +929,10 @@ namespace Robust.Client.Graphics.Clyde
//
// Calculate delta positions from camera.
var (dTlX, dTlY) = eyeTransform.Transform(tl);
var (dTrX, dTrY) = eyeTransform.Transform(tr);
var (dBlX, dBlY) = eyeTransform.Transform(bl);
var (dBrX, dBrY) = eyeTransform.Transform(br);
var dTl = eyeTransform.Transform(tl);
var dTr = eyeTransform.Transform(tr);
var dBl = eyeTransform.Transform(bl);
var dBr = eyeTransform.Transform(br);
// Get which neighbors are occluding.
var no = (occluder.Occluding & OccluderDir.North) != 0;
@@ -939,10 +941,26 @@ namespace Robust.Client.Graphics.Clyde
var wo = (occluder.Occluding & OccluderDir.West) != 0;
// Do visibility tests for occluders (described above).
var tlV = dTlX > 0 && !wo || dTlY < 0 && !no;
var trV = dTrX < 0 && !eo || dTrY < 0 && !no;
var blV = dBlX > 0 && !wo || dBlY > 0 && !so;
var brV = dBrX < 0 && !eo || dBrY > 0 && !so;
bool CheckFaceEyeVis(Vector2 a, Vector2 b)
{
// get normal
var alongNormal = b - a;
var normal = alongNormal.Rotated90DegreesAnticlockwiseWorld.Normalized;
// determine which side of the plane the face is on
// the plane is at the origin of this coordinate system, which is also the eye
// the normal of the plane is that of the face
// therefore, if the dot <= 0, the face is facing the camera
// I don't like this, but rotated occluders started happening
return Vector2.Dot(normal, a) <= 0;
}
var nV = ((!no) && CheckFaceEyeVis(dTl, dTr));
var sV = ((!so) && CheckFaceEyeVis(dBr, dBl));
var eV = ((!eo) && CheckFaceEyeVis(dTr, dBr));
var wV = ((!wo) && CheckFaceEyeVis(dBl, dTl));
var tlV = nV || wV;
var trV = nV || eV;
var blV = sV || wV;
var brV = sV || eV;
// Handle faces, rules described above.
// Note that "from above" it should be clockwise.

View File

@@ -2,6 +2,7 @@ using System;
using System.Runtime.InteropServices;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Robust.Client.Graphics.Clyde
@@ -122,14 +123,16 @@ namespace Robust.Client.Graphics.Clyde
_clyde.DrawSetScissor(scissorBox);
}
public void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
public void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
{
if (entity.Deleted)
var entMan = IoCManager.Resolve<IEntityManager>();
if (entMan.Deleted(entity))
{
throw new ArgumentException("Tried to draw an entity has been deleted.", nameof(entity));
}
var sprite = entity.GetComponent<SpriteComponent>();
var sprite = entMan.GetComponent<SpriteComponent>(entity);
var oldProj = _clyde._currentMatrixProj;
var oldView = _clyde._currentMatrixView;
@@ -162,7 +165,7 @@ namespace Robust.Client.Graphics.Clyde
DrawingHandleWorld,
Angle.Zero,
overrideDirection == null
? entity.Transform.WorldRotation
? entMan.GetComponent<TransformComponent>(entity).WorldRotation
: Angle.Zero,
overrideDirection);
@@ -346,7 +349,7 @@ namespace Robust.Client.Graphics.Clyde
rect.BottomLeft, rect.BottomRight, color, subRegion);
}
public override void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
public override void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
{
_renderHandle.DrawEntity(entity, position, scale, overrideDirection);
}

View File

@@ -8,7 +8,6 @@ using Robust.Client.GameObjects;
using Robust.Client.Utility;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Color = Robust.Shared.Maths.Color;
using TKStencilOp = OpenToolkit.Graphics.OpenGL4.StencilOp;
namespace Robust.Client.Graphics.Clyde
@@ -448,7 +447,7 @@ namespace Robust.Client.Graphics.Clyde
//function! If passing in textures as uniforms ever stops working it might be since someone made it use all the way up to Texture6 too.
//Might change this in the future?
TextureUnit cTarget = TextureUnit.Texture6 + textureUnitVal;
SetTexture(cTarget, ((ClydeTexture) clydeTexture).TextureId);
SetTexture(cTarget, clydeTexture.TextureId);
program.SetUniformTexture(name, cTarget);
textureUnitVal++;
break;
@@ -1027,7 +1026,7 @@ namespace Robust.Client.Graphics.Clyde
return cmp;
}
return a.Owner.Uid.CompareTo(b.Owner.Uid);
return a.Owner.CompareTo(b.Owner);
}
}

View File

@@ -155,10 +155,6 @@ namespace Robust.Client.Graphics.Clyde
{
isActuallySrgb = loadParams.Srgb;
}
else if (pixelType == typeof(Bgra32))
{
isActuallySrgb = loadParams.Srgb;
}
else if (pixelType == typeof(A8))
{
DebugTools.Assert(_hasGLTextureSwizzle);
@@ -264,7 +260,6 @@ namespace Robust.Client.Graphics.Clyde
// Note that if _hasGLSrgb is off, we import an sRGB texture as non-sRGB.
// Shaders are expected to compensate for this
Rgba32 => (srgb && _hasGLSrgb ? PIF.Srgb8Alpha8 : PIF.Rgba8, PF.Rgba, PT.UnsignedByte),
Bgra32 => (srgb && _hasGLSrgb ? PIF.Srgb8Alpha8 : PIF.Rgba8, PF.Bgra, PT.UnsignedByte),
A8 or L8 => (PIF.R8, PF.Red, PT.UnsignedByte),
_ => throw new NotSupportedException("Unsupported pixel type."),
};
@@ -445,7 +440,7 @@ namespace Robust.Client.Graphics.Clyde
{
return default(T) switch
{
Rgba32 or Bgra32 => TexturePixelType.Rgba32,
Rgba32 => TexturePixelType.Rgba32,
L8 => TexturePixelType.L8,
A8 => TexturePixelType.A8,
_ => throw new NotSupportedException("Unsupported pixel type."),

View File

@@ -218,7 +218,8 @@ namespace Robust.Client.Graphics.Clyde
return false;
}
InitOpenGL();
if (!_earlyGLInit)
InitOpenGL();
_sawmillOgl.Debug("Setting viewport and rendering splash...");
@@ -458,6 +459,7 @@ namespace Robust.Client.Graphics.Clyde
public RenderWindow RenderTarget = default!;
public Action<WindowRequestClosedEventArgs>? RequestClosed;
public Action<WindowDestroyedEventArgs>? Closed;
public Action<WindowResizedEventArgs>? Resized;
}
private sealed class WindowHandle : IClydeWindowInternal
@@ -523,6 +525,12 @@ namespace Robust.Client.Graphics.Clyde
remove => Reg.Closed -= value;
}
public event Action<WindowResizedEventArgs>? Resized
{
add => Reg.Resized += value;
remove => Reg.Resized -= value;
}
public nint? WindowsHWnd => _clyde._windowing!.WindowGetWin32Window(Reg);
}

View File

@@ -22,7 +22,7 @@ namespace Robust.Client.Graphics.Clyde
/// <summary>
/// Responsible for most things rendering on OpenGL mode.
/// </summary>
internal sealed partial class Clyde : IClydeInternal, IClydeAudio, IPostInjectInit
internal sealed partial class Clyde : IClydeInternal, IPostInjectInit
{
[Dependency] private readonly IClydeTileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
@@ -66,6 +66,7 @@ namespace Robust.Client.Graphics.Clyde
private ISawmill _sawmillOgl = default!;
private IBindingsContext _glBindingsContext = default!;
private bool _earlyGLInit;
public Clyde()
{
@@ -97,7 +98,6 @@ namespace Robust.Client.Graphics.Clyde
if (!InitMainWindowAndRenderer())
return false;
_initializeAudio();
return true;
}
@@ -115,8 +115,6 @@ namespace Robust.Client.Graphics.Clyde
public void FrameProcess(FrameEventArgs eventArgs)
{
_updateAudio();
_windowing?.FlushDispose();
FlushShaderInstanceDispose();
FlushRenderTargetDispose();
@@ -509,7 +507,6 @@ namespace Robust.Client.Graphics.Clyde
{
_glContext?.Shutdown();
ShutdownWindowing();
_shutdownAudio();
}
private bool IsMainThread()

View File

@@ -19,7 +19,7 @@ namespace Robust.Client.Graphics.Clyde
/// Hey look, it's Clyde's evil twin brother!
/// </summary>
[UsedImplicitly]
internal sealed class ClydeHeadless : IClydeInternal, IClydeAudio
internal sealed class ClydeHeadless : IClydeInternal
{
// Would it make sense to report a fake resolution like 720p here so code doesn't break? idk.
public IClydeWindow MainWindow { get; }
@@ -236,34 +236,6 @@ namespace Robust.Client.Graphics.Clyde
// Nada.
}
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
{
// TODO: Might wanna actually load this so the length gets reported correctly.
return new(default, default, 1, name);
}
public AudioStream LoadAudioWav(Stream stream, string? name = null)
{
// TODO: Might wanna actually load this so the length gets reported correctly.
return new(default, default, 1, name);
}
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
{
// TODO: Might wanna actually load this so the length gets reported correctly.
return new(default, default, channels, name);
}
public IClydeAudioSource CreateAudioSource(AudioStream stream)
{
return DummyAudioSource.Instance;
}
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false)
{
return DummyBufferedAudioSource.Instance;
}
public Task<string> GetText()
{
return Task.FromResult(string.Empty);
@@ -274,11 +246,6 @@ namespace Robust.Client.Graphics.Clyde
// Nada.
}
public void SetMasterVolume(float newVolume)
{
// Nada.
}
private class DummyCursor : ICursor
{
public void Dispose()
@@ -627,6 +594,7 @@ namespace Robust.Client.Graphics.Clyde
public bool DisposeOnClose { get; set; }
public event Action<WindowRequestClosedEventArgs>? RequestClosed { add { } remove { } }
public event Action<WindowDestroyedEventArgs>? Destroyed;
public event Action<WindowResizedEventArgs>? Resized { add { } remove { } }
public void MaximizeOnMonitor(IClydeMonitor monitor)
{

View File

@@ -1,6 +1,4 @@
// Commented out because I can't be bothered to figure out trimming for TerraFX.
/*
using System;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
@@ -10,13 +8,18 @@ using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using TerraFX.Interop;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static Robust.Client.Graphics.Clyde.Egl;
using static TerraFX.Interop.D3D_DRIVER_TYPE;
using static TerraFX.Interop.D3D_FEATURE_LEVEL;
using static TerraFX.Interop.DXGI_FORMAT;
using static TerraFX.Interop.DXGI_SWAP_EFFECT;
using static TerraFX.Interop.Windows;
using static TerraFX.Interop.DirectX.D3D_DRIVER_TYPE;
using static TerraFX.Interop.DirectX.D3D_FEATURE_LEVEL;
using static TerraFX.Interop.DirectX.DXGI_FORMAT;
using static TerraFX.Interop.DirectX.DXGI_SWAP_EFFECT;
using static TerraFX.Interop.Windows.Windows;
using static TerraFX.Interop.DirectX.DirectX;
using static TerraFX.Interop.DirectX.D3D11;
using static TerraFX.Interop.DirectX.DXGI;
using GL = OpenToolkit.Graphics.OpenGL4.GL;
namespace Robust.Client.Graphics.Clyde
@@ -77,7 +80,7 @@ namespace Robust.Client.Graphics.Clyde
};
_windowData[reg.Id] = data;
var hWnd = Clyde._windowing!.WindowGetWin32Window(reg)!.Value;
var hWnd = (HWND) Clyde._windowing!.WindowGetWin32Window(reg)!.Value;
// todo: exception management.
CreateSwapChain1(hWnd, data);
@@ -98,7 +101,8 @@ namespace Robust.Client.Graphics.Clyde
{
if (data.EglBackbuffer != null)
{
eglMakeCurrent(_eglDisplay, null, null, null);
if (data.Reg.IsMainWindow)
eglMakeCurrent(_eglDisplay, null, null, null);
eglDestroySurface(_eglDisplay, data.EglBackbuffer);
data.EglBackbuffer = null;
@@ -115,8 +119,7 @@ namespace Robust.Client.Graphics.Clyde
fixed (ID3D11Texture2D** texPtr = &data.Backbuffer)
{
var iid = IID_ID3D11Texture2D;
ThrowIfFailed("GetBuffer", data.SwapChain->GetBuffer(0, &iid, (void**) texPtr));
ThrowIfFailed("GetBuffer", data.SwapChain->GetBuffer(0, __uuidof<ID3D11Texture2D>(), (void**) texPtr));
}
var attributes = stackalloc int[]
@@ -134,7 +137,7 @@ namespace Robust.Client.Graphics.Clyde
attributes);
}
private void CreateSwapChain1(nint hWnd, WindowData data)
private void CreateSwapChain1(HWND hWnd, WindowData data)
{
var desc = new DXGI_SWAP_CHAIN_DESC
{
@@ -199,6 +202,7 @@ namespace Robust.Client.Graphics.Clyde
// and so that we can know _hasGLSrgb in window creation.
eglMakeCurrent(_eglDisplay, null, null, _eglContext);
Clyde.InitOpenGL();
Clyde._earlyGLInit = true;
}
private void TryInitializeCore()
@@ -294,11 +298,9 @@ namespace Robust.Client.Graphics.Clyde
try
{
var iid = IID_IDXGIFactory1;
fixed (IDXGIFactory1** ptr = &_factory)
{
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(&iid, (void**) ptr));
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(__uuidof<IDXGIFactory1>(), (void**) ptr));
}
// Try to find the correct adapter if specified.
@@ -317,6 +319,38 @@ namespace Robust.Client.Graphics.Clyde
Logger.DebugS("clyde.ogl.angle", $"Found display adapter with name: {adapterName}");
}
#pragma warning disable CA1416
IDXGIFactory6* factory6;
if (_adapter == null && _factory->QueryInterface(__uuidof<IDXGIFactory6>(), (void**) &factory6) == 0)
{
var gpuPref = (DXGI_GPU_PREFERENCE) Clyde._cfg.GetCVar(CVars.DisplayGpuPreference);
IDXGIAdapter1* adapter;
for (var adapterIndex = 0u;
factory6->EnumAdapterByGpuPreference(
adapterIndex,
gpuPref,
__uuidof<IDXGIAdapter1>(),
(void**)&adapter) != DXGI_ERROR_NOT_FOUND;
adapterIndex++)
{
/*
DXGI_ADAPTER_DESC1 aDesc;
ThrowIfFailed("GetDesc1", adapter->GetDesc1(&aDesc));
var aDescName = new ReadOnlySpan<char>(aDesc.Description, 128);
Logger.DebugS("clyde.ogl.angle", aDescName.ToString());
adapter->Release();
*/
_adapter = adapter;
break;
}
factory6->Release();
}
#pragma warning restore CA1416
Span<D3D_FEATURE_LEVEL> featureLevels = stackalloc D3D_FEATURE_LEVEL[]
{
// 11_0 can do GLES3
@@ -335,7 +369,7 @@ namespace Robust.Client.Graphics.Clyde
ThrowIfFailed("D3D11CreateDevice", D3D11CreateDevice(
(IDXGIAdapter*) _adapter,
_adapter == null ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_UNKNOWN,
IntPtr.Zero,
HMODULE.NULL,
0,
fl,
(uint) featureLevels.Length,
@@ -348,13 +382,11 @@ namespace Robust.Client.Graphics.Clyde
// Get adapter from the device.
iid = IID_IDXGIDevice1;
ThrowIfFailed("QueryInterface", _device->QueryInterface(&iid, (void**) &dxgiDevice));
ThrowIfFailed("QueryInterface", _device->QueryInterface(__uuidof<IDXGIDevice1>(), (void**) &dxgiDevice));
fixed (IDXGIAdapter1** ptrAdapter = &_adapter)
{
iid = IID_IDXGIAdapter1;
ThrowIfFailed("GetParent", dxgiDevice->GetParent(&iid, (void**) ptrAdapter));
ThrowIfFailed("GetParent", dxgiDevice->GetParent(__uuidof<IDXGIAdapter1>(), (void**) ptrAdapter));
}
_deviceFl = _device->GetFeatureLevel();
@@ -527,4 +559,3 @@ namespace Robust.Client.Graphics.Clyde
}
}
}
*/

View File

@@ -1,28 +1,32 @@
/*
using System;
using System;
using System.Runtime.InteropServices;
using Robust.Shared.Console;
using Robust.Shared.Utility;
using TerraFX.Interop;
using static TerraFX.Interop.D3D_DRIVER_TYPE;
using static TerraFX.Interop.D3D_FEATURE_LEVEL;
using static TerraFX.Interop.DXGI_MEMORY_SEGMENT_GROUP;
using static TerraFX.Interop.DXGI_SWAP_EFFECT;
using static TerraFX.Interop.Windows;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static TerraFX.Interop.DirectX.DXGI_MEMORY_SEGMENT_GROUP;
using static TerraFX.Interop.Windows.Windows;
using static TerraFX.Interop.DirectX.DirectX;
using static TerraFX.Interop.DirectX.DXGI;
namespace Robust.Client.Graphics.Clyde
{
public sealed class VramCommand : IConsoleCommand
{
public string Command => "vram";
public string Description => "Checks vram";
public string Description => "Displays video memory usage statics by the game.";
public string Help => "Usage: vram";
public unsafe void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (!OperatingSystem.IsWindows())
{
shell.WriteError("This command is only supported on Windows.");
return;
}
IDXGIFactory1* dxgiFactory;
var iid = IID_IDXGIFactory1;
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(&iid, (void**) &dxgiFactory));
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(__uuidof<IDXGIFactory1>(), (void**) &dxgiFactory));
uint idx = 0;
IDXGIAdapter* adapter;
@@ -30,8 +34,8 @@ namespace Robust.Client.Graphics.Clyde
{
DXGI_ADAPTER_DESC2 desc;
IDXGIAdapter3* adapter3;
iid = IID_IDXGIAdapter3;
adapter->QueryInterface(&iid, (void**) &adapter3);
adapter->QueryInterface(__uuidof<IDXGIAdapter3>(), (void**) &adapter3);
adapter->Release();
ThrowIfFailed("GetDesc", adapter3->GetDesc2(&desc));
var descString = new ReadOnlySpan<char>(desc.Description, 128).TrimEnd('\0');
@@ -46,6 +50,8 @@ namespace Robust.Client.Graphics.Clyde
shell.WriteLine($"Usage (non local): {ByteHelpers.FormatBytes((long) memInfo.CurrentUsage)}");
idx += 1;
adapter3->Release();
}
}
@@ -58,4 +64,3 @@ namespace Robust.Client.Graphics.Clyde
}
}
}
*/

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Robust.Shared;
using System.Threading;
using OpenToolkit.GraphicsLibraryFramework;
using Robust.Client.Input;
@@ -8,6 +9,8 @@ using GlfwKey = OpenToolkit.GraphicsLibraryFramework.Keys;
using GlfwButton = OpenToolkit.GraphicsLibraryFramework.MouseButton;
using static Robust.Client.Input.Mouse;
using static Robust.Client.Input.Keyboard;
using Robust.Shared.IoC;
using Robust.Shared.Configuration;
namespace Robust.Client.Graphics.Clyde
{
@@ -22,6 +25,7 @@ namespace Robust.Client.Graphics.Clyde
private void InitKeyMap()
{
_printableKeyNameMap.Clear();
// From GLFW's source code: this is the actual list of "printable" keys
// that GetKeyName returns something for.
CacheKey(Keys.KeyPadEqual);
@@ -41,8 +45,18 @@ namespace Robust.Client.Graphics.Clyde
if (rKey == Key.Unknown)
return;
var name = GLFW.GetKeyName(key, 0);
if (name != null)
string name;
if (!_clyde._cfg.GetCVar(CVars.DisplayUSQWERTYHotkeys))
{
name = GLFW.GetKeyName(key, 0);
}
else
{
name = key.ToString();
}
if (!string.IsNullOrEmpty(name))
_printableKeyNameMap.Add(rKey, name);
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
@@ -268,7 +268,7 @@ namespace Robust.Client.Graphics.Clyde
}
var (desc, errCode) = errorResult!.Value;
return (null, $"[{errCode}]: {desc}");
return (null, (string)$"[{errCode}]: {desc}");
}
public void WindowDestroy(WindowReg window)

View File

@@ -1,6 +1,7 @@
using System;
using System.Runtime.Serialization;
using OpenToolkit.GraphicsLibraryFramework;
using Robust.Client.Input;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
@@ -16,6 +17,7 @@ namespace Robust.Client.Graphics.Clyde
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private readonly Clyde _clyde;
@@ -39,6 +41,7 @@ namespace Robust.Client.Graphics.Clyde
#if DEBUG
_cfg.OnValueChanged(CVars.DisplayWin32Experience, b => _win32Experience = b, true);
#endif
_cfg.OnValueChanged(CVars.DisplayUSQWERTYHotkeys, ReInitKeyMap);
InitChannels();
@@ -60,6 +63,7 @@ namespace Robust.Client.Graphics.Clyde
if (_glfwInitialized)
{
_sawmill.Debug("Terminating GLFW.");
_cfg.UnsubValueChanged(CVars.DisplayUSQWERTYHotkeys, ReInitKeyMap);
GLFW.Terminate();
}
}
@@ -69,6 +73,12 @@ namespace Robust.Client.Graphics.Clyde
// Not currently used
}
private void ReInitKeyMap(bool onValueChanged)
{
InitKeyMap();
_inputManager.InputModeChanged();
}
private bool InitGlfw()
{
StoreCallbacks();

View File

@@ -94,6 +94,6 @@ namespace Robust.Client.Graphics
public Vector2 DrawString(Font font, Vector2 pos, string str)
=> DrawString(font, pos, str, Color.White);
public abstract void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
public abstract void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
}
}

View File

@@ -52,6 +52,11 @@ namespace Robust.Client.Graphics
public abstract void DrawTextureRectRegion(Texture texture, in Box2Rotated quad,
Color? modulate = null, UIBox2? subRegion = null);
private Box2 GetQuad(Texture texture, Vector2 position)
{
return Box2.FromDimensions(position, texture.Size / (float)Ppm);
}
/// <summary>
/// Draws a full texture sprite to the world. The coordinate system is right handed.
/// Make sure to set <see cref="DrawingHandleBase.SetTransform"/>
@@ -67,7 +72,28 @@ namespace Robust.Client.Graphics
{
CheckDisposed();
DrawTextureRect(texture, Box2.FromDimensions(position, texture.Size / (float) Ppm), modulate);
DrawTextureRect(texture, GetQuad(texture, position), modulate);
}
/// <summary>
/// Draws a full texture sprite to the world. The coordinate system is right handed.
/// Make sure to set <see cref="DrawingHandleBase.SetTransform"/>
/// to set the model matrix if needed.
/// </summary>
/// <param name="texture">Texture to draw.</param>
/// <param name="position">The coordinates of the quad in object space (or world if the transform is identity.).</param>
/// <param name="angle">The angle of the quad in object space.</param>
/// <param name="modulate">A color to multiply the texture by when shading.</param>
/// <remarks>
/// The sprite will have it's local dimensions calculated so that it has <see cref="EyeManager.PixelsPerMeter"/> texels per meter in the world.
/// </remarks>
public void DrawTexture(Texture texture, Vector2 position, Angle angle, Color? modulate = null)
{
CheckDisposed();
var quad = GetQuad(texture, position);
DrawTextureRect(texture, new Box2Rotated(quad, angle, quad.Center), modulate);
}
/// <summary>

View File

@@ -0,0 +1,16 @@
using System;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
namespace Robust.Client.Graphics
{
internal interface IClydeAudioInternal : IClydeAudio
{
bool InitializePostWindowing();
void FrameProcess(FrameEventArgs eventArgs);
void Shutdown();
}
}

View File

@@ -35,6 +35,12 @@ namespace Robust.Client.Graphics
/// This means the window must not be used anymore (it is disposed).
/// </summary>
event Action<WindowDestroyedEventArgs> Destroyed;
/// <summary>
/// Raised when the window has been definitively closed.
/// This means the window must not be used anymore (it is disposed).
/// </summary>
event Action<WindowResizedEventArgs> Resized;
}
public interface IClydeWindowInternal : IClydeWindow

View File

@@ -12,6 +12,6 @@ namespace Robust.Client.Graphics
void RenderInRenderTarget(IRenderTarget target, Action a, Color clearColor=default);
void SetScissor(UIBox2i? scissorBox);
void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
}
}

View File

@@ -111,6 +111,7 @@ namespace Robust.Client.Input
event Action<IKeyBinding> OnKeyBindingAdded;
event Action<IKeyBinding> OnKeyBindingRemoved;
event Action OnInputModeChanged;
/// <summary>
/// Gets all the keybinds bound to a specific function.
@@ -131,5 +132,7 @@ namespace Robust.Client.Input
bool IsKeyFunctionModified(BoundKeyFunction function);
bool IsKeyDown(Keyboard.Key key);
void InputModeChanged();
}
}

View File

@@ -6,6 +6,7 @@ namespace Robust.Client.Input
{
BoundKeyState State { get; }
BoundKeyFunction Function { get; }
string FunctionCommand { get; }
KeyBindingType BindingType { get; }
Keyboard.Key BaseKey { get; }

View File

@@ -43,6 +43,7 @@ namespace Robust.Client.Input
[Dependency] private readonly IResourceManager _resourceMan = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IUserInterfaceManagerInternal _uiMgr = default!;
[Dependency] private readonly IConsoleHost _console = default!;
private bool _currentlyFindingViewport;
@@ -98,6 +99,7 @@ namespace Robust.Client.Input
public event KeyEventAction? FirstChanceOnKeyEvent;
public event Action<IKeyBinding>? OnKeyBindingAdded;
public event Action<IKeyBinding>? OnKeyBindingRemoved;
public event Action? OnInputModeChanged;
/// <inheritdoc />
public void Initialize()
@@ -131,7 +133,7 @@ namespace Robust.Client.Input
.SelectMany(p => p)
.Select(p => new KeyBindingRegistration
{
Function = p.Function,
Function = p.Function.FunctionName,
BaseKey = p.BaseKey,
Mod1 = p.Mod1,
Mod2 = p.Mod2,
@@ -213,7 +215,7 @@ namespace Robust.Client.Input
foreach (var binding in _bindings)
{
// check if our binding is even in the active context
if (!Contexts.ActiveContext.FunctionExistsHierarchy(binding.Function))
if (binding.BindingType != KeyBindingType.Command && !Contexts.ActiveContext.FunctionExistsHierarchy(binding.Function))
continue;
if (PackedMatchesPressedState(binding.PackedKeyCombo))
@@ -359,6 +361,12 @@ namespace Robust.Client.Input
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false)
{
if (binding.BindingType == KeyBindingType.Command && state == BoundKeyState.Down)
{
_console.ExecuteCommand(binding.FunctionCommand);
return true;
}
// christ this crap *is* re-entrant thanks to PlacementManager and
// I honestly have no idea what the best solution here is.
// note from the future: context switches won't cause re-entrancy anymore because InputContextContainer defers context switches
@@ -492,10 +500,10 @@ namespace Robust.Client.Input
foreach (var reg in baseKeyRegs)
{
if (!NetworkBindMap.FunctionExists(reg.Function.FunctionName))
if (reg.Type != KeyBindingType.Command && !NetworkBindMap.FunctionExists(reg.Function.FunctionName))
{
Logger.ErrorS("input", "Key function in {0} does not exist: '{1}'", file,
reg.Function.FunctionName);
reg.Function);
continue;
}
@@ -531,6 +539,17 @@ namespace Robust.Client.Input
/// <inheritdoc />
public IKeyBinding RegisterBinding(BoundKeyFunction function, KeyBindingType bindingType,
Key baseKey, Key? mod1, Key? mod2, Key? mod3)
{
var binding = new KeyBinding(this, function.FunctionName, bindingType, baseKey, false, false, false,
0, mod1 ?? Key.Unknown, mod2 ?? Key.Unknown, mod3 ?? Key.Unknown);
RegisterBinding(binding);
return binding;
}
public IKeyBinding RegisterBinding(string function, KeyBindingType bindingType,
Key baseKey, Key? mod1, Key? mod2, Key? mod3)
{
var binding = new KeyBinding(this, function, bindingType, baseKey, false, false, false,
0, mod1 ?? Key.Unknown, mod2 ?? Key.Unknown, mod3 ?? Key.Unknown);
@@ -542,7 +561,7 @@ namespace Robust.Client.Input
public IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified = true)
{
var binding = new KeyBinding(this, reg.Function, reg.Type, reg.BaseKey, reg.CanFocus, reg.CanRepeat,
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);
RegisterBinding(binding, markModified);
@@ -569,6 +588,8 @@ namespace Robust.Client.Input
OnKeyBindingRemoved?.Invoke(binding);
}
public void InputModeChanged() => OnInputModeChanged?.Invoke();
private void RegisterBinding(KeyBinding binding, bool markModified = true)
{
// we sort larger combos first so they take priority over smaller (single key) combos,
@@ -684,6 +705,7 @@ namespace Robust.Client.Input
[ViewVariables] public BoundKeyState State { get; set; }
public PackedKeyCombo PackedKeyCombo { get; }
[ViewVariables] public BoundKeyFunction Function { get; }
[ViewVariables] public string FunctionCommand => Function.FunctionName;
[ViewVariables] public KeyBindingType BindingType { get; }
[ViewVariables] public Key BaseKey => PackedKeyCombo.BaseKey;
@@ -711,7 +733,9 @@ namespace Robust.Client.Input
[ViewVariables] public int Priority { get; internal set; }
public KeyBinding(InputManager inputManager, BoundKeyFunction function,
public KeyBinding(
InputManager inputManager,
string function,
KeyBindingType bindingType,
Key baseKey,
bool canFocus, bool canRepeat, bool allowSubCombs, int priority, Key mod1 = Key.Unknown,
@@ -771,7 +795,7 @@ namespace Robust.Client.Input
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendFormat("{0}: {1}", Function.FunctionName, BaseKey);
sb.AppendFormat("{0}: {1}", Function, BaseKey);
if (Mod1 != Key.Unknown)
{
sb.AppendFormat("+{0}", Mod1);
@@ -868,6 +892,10 @@ namespace Robust.Client.Input
Unknown = 0,
State,
Toggle,
/// <summary>
/// This keybind does not execute a real key function but instead causes a console command to be executed.
/// </summary>
Command,
}
public enum CommandState : byte
@@ -922,7 +950,7 @@ namespace Robust.Client.Input
var registration = new KeyBindingRegistration
{
Function = new BoundKeyFunction(inputCommand),
Function = inputCommand,
BaseKey = keyId,
Type = keyMode
};

View File

@@ -1,4 +1,4 @@
using System;
using System;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -35,7 +35,8 @@ namespace Robust.Client.Physics
protected override void HandleMapCreated(object? sender, MapEventArgs eventArgs)
{
if (eventArgs.Map == MapId.Nullspace) return;
MapManager.GetMapEntity(eventArgs.Map).AddComponent<PhysicsMapComponent>();
var mapUid = MapManager.GetMapEntityId(eventArgs.Map);
EntityManager.AddComponent<PhysicsMapComponent>(mapUid);
}
}
}

View File

@@ -4,6 +4,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
namespace Robust.Client.Placement.Modes
{
@@ -31,8 +32,8 @@ namespace Robust.Client.Placement.Modes
var mapId = MouseCoords.GetMapId(pManager.EntityManager);
var snapToEntities = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(MouseCoords, SnapToRange)
.Where(entity => entity.Prototype == pManager.CurrentPrototype && entity.Transform.MapID == mapId)
.OrderBy(entity => (entity.Transform.WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager)).LengthSquared)
.Where(entity => pManager.EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype == pManager.CurrentPrototype && pManager.EntityManager.GetComponent<TransformComponent>(entity).MapID == mapId)
.OrderBy(entity => (pManager.EntityManager.GetComponent<TransformComponent>(entity).WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager)).LengthSquared)
.ToList();
if (snapToEntities.Count == 0)
@@ -41,7 +42,8 @@ namespace Robust.Client.Placement.Modes
}
var closestEntity = snapToEntities[0];
if (!closestEntity.TryGetComponent<ISpriteComponent>(out var component) || component.BaseRSI == null)
var closestTransform = pManager.EntityManager.GetComponent<TransformComponent>(closestEntity);
if (!pManager.EntityManager.TryGetComponent<ISpriteComponent?>(closestEntity, out var component) || component.BaseRSI == null)
{
return;
}
@@ -50,8 +52,8 @@ namespace Robust.Client.Placement.Modes
var closestRect =
Box2.FromDimensions(
closestEntity.Transform.WorldPosition.X - closestBounds.X / 2f,
closestEntity.Transform.WorldPosition.Y - closestBounds.Y / 2f,
closestTransform.WorldPosition.X - closestBounds.X / 2f,
closestTransform.WorldPosition.Y - closestBounds.Y / 2f,
closestBounds.X, closestBounds.Y);
var sides = new[]

View File

@@ -1,5 +1,4 @@
using System;
using Robust.Client.Graphics;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -34,8 +33,8 @@ namespace Robust.Client.Placement.Modes
GridDistancing = SnapSize;
var mouselocal = new Vector2( //Round local coordinates onto the snap grid
(float) MathF.Round(MouseCoords.X / SnapSize, MidpointRounding.AwayFromZero) * SnapSize,
(float) MathF.Round(MouseCoords.Y / SnapSize, MidpointRounding.AwayFromZero) * SnapSize);
MathF.Round(MouseCoords.X / SnapSize, MidpointRounding.AwayFromZero) * SnapSize,
MathF.Round(MouseCoords.Y / SnapSize, MidpointRounding.AwayFromZero) * SnapSize);
//Convert back to original world and screen coordinates after applying offset
MouseCoords =

View File

@@ -18,7 +18,7 @@ namespace Robust.Client.Placement
return false;
}
public virtual bool HijackDeletion(IEntity entity)
public virtual bool HijackDeletion(EntityUid entity)
{
return false;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Client.GameObjects;
@@ -15,7 +15,6 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Physics;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Timing;
@@ -99,7 +98,7 @@ namespace Robust.Client.Placement
/// Colour of this gets swapped around in PlacementMode.
/// This entity needs to stay in nullspace.
/// </summary>
public IEntity? CurrentPlacementOverlayEntity { get; set; }
public EntityUid? CurrentPlacementOverlayEntity { get; set; }
/// <summary>
/// A BAD way to explicitly control the icons used!!!
@@ -242,7 +241,7 @@ namespace Robust.Client.Placement
return false;
}
HandleDeletion(EntityManager.GetEntity(uid));
HandleDeletion(uid);
}
else
{
@@ -414,14 +413,14 @@ namespace Robust.Client.Placement
return false;
}
public void HandleDeletion(IEntity entity)
public void HandleDeletion(EntityUid entity)
{
if (!IsActive || !Eraser) return;
if (Hijack != null && Hijack.HijackDeletion(entity)) return;
var msg = NetworkManager.CreateNetMessage<MsgPlacement>();
msg.PlaceType = PlacementManagerMessage.RequestEntRemove;
msg.EntityUid = entity.Uid;
msg.EntityUid = entity;
NetworkManager.ClientSendMessage(msg);
}
@@ -493,10 +492,9 @@ namespace Robust.Client.Placement
{
// Try to get current map.
var map = MapId.Nullspace;
var ent = PlayerManager.LocalPlayer!.ControlledEntity;
if (ent != null)
if (PlayerManager.LocalPlayer!.ControlledEntity is {Valid: true} ent)
{
map = ent.Transform.MapID;
map = EntityManager.GetComponent<TransformComponent>(ent).MapID;
}
if (map == MapId.Nullspace || CurrentPermission == null || CurrentMode == null)
@@ -511,15 +509,15 @@ namespace Robust.Client.Placement
private bool CurrentEraserMouseCoordinates(out EntityCoordinates coordinates)
{
var ent = PlayerManager.LocalPlayer?.ControlledEntity;
if (ent == null)
var ent = PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
if (ent == EntityUid.Invalid)
{
coordinates = new EntityCoordinates();
return false;
}
else
{
var map = ent.Transform.MapID;
var map = EntityManager.GetComponent<TransformComponent>(ent).MapID;
if (map == MapId.Nullspace || !Eraser)
{
coordinates = new EntityCoordinates();
@@ -634,11 +632,12 @@ namespace Robust.Client.Placement
CurrentMode.Render(handle);
if (CurrentPermission == null || CurrentPermission.Range <= 0 || !CurrentMode.RangeRequired
|| PlayerManager.LocalPlayer?.ControlledEntity == null)
if (CurrentPermission is not {Range: > 0} ||
!CurrentMode.RangeRequired ||
PlayerManager.LocalPlayer?.ControlledEntity is not {Valid: true} controlled)
return;
var worldPos = PlayerManager.LocalPlayer.ControlledEntity.Transform.WorldPosition;
var worldPos = EntityManager.GetComponent<TransformComponent>(controlled).WorldPosition;
handle.DrawCircle(worldPos, CurrentPermission.Range, new Color(1, 1, 1, 0.25f));
}
@@ -661,8 +660,8 @@ namespace Robust.Client.Placement
{
if (CurrentPlacementOverlayEntity != null)
{
if (!CurrentPlacementOverlayEntity.Deleted)
CurrentPlacementOverlayEntity.Delete();
if (!EntityManager.Deleted(CurrentPlacementOverlayEntity))
EntityManager.DeleteEntity(CurrentPlacementOverlayEntity.Value);
CurrentPlacementOverlayEntity = null;
}
}
@@ -671,7 +670,7 @@ namespace Robust.Client.Placement
{
EnsureNoPlacementOverlayEntity();
CurrentPlacementOverlayEntity = EntityManager.SpawnEntity(null, MapCoordinates.Nullspace);
return CurrentPlacementOverlayEntity.EnsureComponent<SpriteComponent>();
return EntityManager.EnsureComponent<SpriteComponent>(CurrentPlacementOverlayEntity.Value);
}
private void PreparePlacement(string templateName)

View File

@@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Client.Graphics;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Utility;
namespace Robust.Client.Placement
@@ -87,9 +85,9 @@ namespace Robust.Client.Placement
public virtual void Render(DrawingHandleWorld handle)
{
var sce = pManager.CurrentPlacementOverlayEntity;
if (sce == null || sce.Deleted)
if (sce is not {} scent || pManager.EntityManager.Deleted(scent))
return;
var sc = sce.GetComponent<SpriteComponent>();
var sc = pManager.EntityManager.GetComponent<SpriteComponent>(sce!.Value);
IEnumerable<EntityCoordinates> locationcollection;
switch (pManager.PlacementType)
@@ -113,9 +111,8 @@ namespace Robust.Client.Placement
{
if (!coordinate.IsValid(pManager.EntityManager))
return; // Just some paranoia just in case
var entity = coordinate.GetEntity(pManager.EntityManager);
var worldPos = coordinate.ToMapPos(pManager.EntityManager);
var worldRot = entity.Transform.WorldRotation + dirAng;
var worldRot = pManager.EntityManager.GetComponent<TransformComponent>(coordinate.EntityId).WorldRotation + dirAng;
sc.Color = IsValidPosition(coordinate) ? ValidPlaceColor : InvalidPlaceColor;
sc.Render(handle, pManager.eyeManager.CurrentEye.Rotation, worldRot, worldPos);
@@ -129,7 +126,7 @@ namespace Robust.Client.Placement
public IEnumerable<EntityCoordinates> LineCoordinates()
{
var (x, y) = MouseCoords.ToMapPos(pManager.EntityManager) - pManager.StartPoint.ToMapPos(pManager.EntityManager);
var (_, (x, y)) = MouseCoords.WithEntityId(pManager.StartPoint.EntityId) - pManager.StartPoint;
float iterations;
Vector2 distance;
if (Math.Abs(x) > Math.Abs(y))
@@ -152,7 +149,7 @@ namespace Robust.Client.Placement
// This name is a nice reminder of our origins. Never forget.
public IEnumerable<EntityCoordinates> GridCoordinates()
{
var placementdiff = MouseCoords.ToMapPos(pManager.EntityManager) - pManager.StartPoint.ToMapPos(pManager.EntityManager);
var placementdiff = MouseCoords.WithEntityId(pManager.StartPoint.EntityId) - pManager.StartPoint;
var distanceX = new Vector2(placementdiff.X > 0 ? 1 : -1, 0) * GridDistancing;
var distanceY = new Vector2(0, placementdiff.Y > 0 ? 1 : -1) * GridDistancing;
@@ -198,14 +195,14 @@ namespace Robust.Client.Placement
{
if (!RangeRequired)
return true;
if (pManager.PlayerManager.LocalPlayer?.ControlledEntity == null)
var controlled = pManager.PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
if (controlled == EntityUid.Invalid)
{
return false;
}
var range = pManager.CurrentPermission!.Range;
if (range > 0 && !pManager.PlayerManager.LocalPlayer.ControlledEntity.Transform.Coordinates.InRange(pManager.EntityManager, coordinates, range))
if (range > 0 && !pManager.EntityManager.GetComponent<TransformComponent>(controlled).Coordinates.InRange(pManager.EntityManager, coordinates, range))
return false;
return true;
}

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