Compare commits

..

231 Commits

Author SHA1 Message Date
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
378 changed files with 10415 additions and 6138 deletions

View File

@@ -0,0 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<PropertyGroup><Version>0.8.2</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,55 @@ 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>();
var toHide = new List<EntityUid>();
var toShow = 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));
if(_hiddenEntities.ContainsKey(uid))
toShow.Add(uid);
uid = es.Uid;
}
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);
uid = newEntity;
}
if(es.Hide)
toHide.Add(uid);
}
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 +483,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 +495,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 +509,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 +529,42 @@ 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
foreach (var entityUid in toHide)
{
if(_entityManager.HasComponent<MapGridComponent>(entityUid)) continue;
var xform = _entityManager.GetComponent<TransformComponent>(entityUid);
_hiddenEntities.Add(entityUid, xform.MapID);
xform.ChangeMapId(MapId.Nullspace);
}
foreach (var entityUid in toShow)
{
_entityManager.GetComponent<TransformComponent>(entityUid).ChangeMapId(_hiddenEntities[entityUid]);
_hiddenEntities.Remove(entityUid);
}
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 +615,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,12 @@
using System;
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.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Utility;
namespace Robust.Client.Placement
@@ -87,9 +84,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 +110,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);
@@ -198,14 +194,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