Compare commits

...

161 Commits

Author SHA1 Message Date
Acruid
e0cdcd228e Fixed Timer Namespace in unit tests. 2021-02-18 20:35:34 -08:00
Acruid
fdb5e014b5 PauseManager moved to Shared (#1553)
* Moved IPauseManager from server to shared.

* Moved ITimerManager from Timers to Timing.

* Added missing IConsoleHost to server/client RegisterIoC. Tests work again.
2021-02-18 20:12:26 -08:00
DrSmugleaf
cefcad775b Make addcomp and rmcomp give better feedback and case insensitive (#1570)
* Make addcomp and rmcomp case insensitive

* Fix up names

* Make addcomp and rmcomp give better feedback

* Make addcomp and rmcomp less fail happy
2021-02-18 20:01:14 -08:00
Vera Aguilera Puerto
e40feac1f1 Adds VV autorefresh when right-clicking the refresh button. (#1558)
* Adds VV autorefresh when right-clicking the refresh button.

* cancel token on close

* button tooltip
2021-02-18 00:14:11 -08:00
DrSmugleaf
3ef4ac7452 Make component states dependant on the player getting them (#1569) 2021-02-17 23:48:17 -08:00
Pieter-Jan Briers
93bf1b09e7 Fix disconnecting while connecting causes you to be locked out of the server. 2021-02-17 23:22:11 +01:00
DrSmugleaf
a1e557e870 Add IPrototypeManager method to load a string (#1567) 2021-02-17 13:20:39 -08:00
Pieter-Jan Briers
864adb7445 Add DateTimeStyles to sandbox. 2021-02-17 11:52:36 +01:00
mirrorcult
9e3f3f0c1c vec2i serializer (#1563)
Co-authored-by: cyclowns <cyclowns@protonmail.ch>
2021-02-16 12:19:45 -08:00
DrSmugleaf
a40c4a435c Fix file not found exceptions when starting up the game with a debugger (#1562)
* Fix exceptions when starting up the game

* Remove try catches
2021-02-16 20:05:22 +01:00
DrSmugleaf
17182dd0e8 Engine PR for enabling nullability in Content.Client (#1565) 2021-02-16 20:05:06 +01:00
DrSmugleaf
d8b50044a2 Add (de)serialization for immutable lists (#1549) 2021-02-16 20:04:28 +01:00
Pieter-Jan Briers
4dc396e73d Fixes warning in TypePropertySerialization_Test.cs 2021-02-16 09:20:06 +01:00
Pieter-Jan Briers
6ae0b0e892 Fix [GenerateTypedNameReferences] with sealed types.
Fixes #1546
2021-02-16 09:19:57 +01:00
Pieter-Jan Briers
7162ca3456 Probably fix the bug where people get locked out of the server due to duplicate connction attempts. 2021-02-16 09:02:14 +01:00
Pieter-Jan Briers
1b44c1a1b8 Allow NotImplementedException in sandbox. 2021-02-15 17:57:38 +01:00
Clyybber
5b80b33e00 Change GetFileSystemInfos to EnumerateFileSystemInfos for iteration (#1561) 2021-02-15 16:26:16 +01:00
DrSmugleaf
f05c1b2395 Add Attribute generic constraint to IReflectionManager.FindTypesWithAttribute (#1548) 2021-02-14 02:14:00 +01:00
Pieter-Jan Briers
d9b2c73440 XamlUI improvements.
1. Add XAML namespaces https://spacestation14.io with Avalonia hacks.
2. Make markup extensions work with Avalonia hacks.
3. Add LocExtension for localized strings.
4. Add Vector2 parsing type converter to XAML compilation.
5. Make SS14Window better thanks to these improvements.
2021-02-14 02:09:37 +01:00
Pieter-Jan Briers
29a39f8e0a Adds LocExtension markup extension for XAML.
Does Loc.GetString().
2021-02-13 15:13:05 +01:00
Pieter-Jan Briers
2d72a2bdb5 Server timing improvements.
Add helpers.
Fix desynchronized NetTime.
2021-02-13 11:42:12 +01:00
Pieter-Jan Briers
91da635631 Fix Robust.Shared.Interfaces namespace in ScriptInstanceShared 2021-02-13 11:42:12 +01:00
chairbender
68ab3d504a Various fixes to support new chatbox (#1547)
* #272 no arrow, actually change id on channel changer

* #272 ability to apply style class to child

* #272 try methods for selecting items in OptionButton.cs

* #272 allow escaping right angle bracket in formatted message

* #272 ability to detect when local player is set / unset

* #272 make RemoveModal public since PushModal is as well, so modals can be removed on-demand if needed rather than relying on a click elsewhere

* #272 revert
2021-02-12 18:20:29 -08:00
Pieter-Jan Briers
5187040a64 Remove unsafe code from GrowableStack 2021-02-13 00:47:05 +01:00
DrSmugleaf
e0c63e7ce6 Add SerializedType attribute to specify the id used in !type tags (#1545)
* Add SerializedType attribute to serialize types without the !type tag

* Fix nulls in tests

* Fix null in tests maybe

* Return to type tags

* Fix imports
2021-02-11 13:50:55 -08:00
Acruid
0ba2272cab Remove all dependencies from LocalizationManager.
Remove IoCManager calls from EntityPrototype.
Add missing using statements preventing EXCEPTION_TOLERANCE from building.
2021-02-11 00:39:12 -08:00
Acruid
2183cd7ca1 Massive Namespace Cleanup (#1544)
* Removed the Interfaces folder.
* All objects inside the GameObjects subfolders are now in the GameObjects namespace.
* Added a Resharper DotSettings file to mark the GameObjects subfolders as not providing namespaces.
* Simplified Robust.client.Graphics namespace.
* Automated remove redundant using statements.
2021-02-10 23:27:19 -08:00
DrSmugleaf
cbf01f0aa5 Fix ComponentChangedSerialized test (#1543) 2021-02-09 21:24:28 +01:00
Pieter-Jan Briers
6973d43006 Network timing synchronization.
Lidgren NetTime.Now is now synchronized to IGameTiming.RealTime.

NetChannel is now a nested class of NetManager.

Add IGameTiming.ServerTime, INetChannel.RemoteTimeOffset, INetChannel.RemoteTime
2021-02-09 14:28:20 +01:00
Pieter-Jan Briers
c5a961d9a0 Even better: use argStr. 2021-02-09 14:28:20 +01:00
Acruid
b4f7d71ee7 Server console can forward commands to a client. 2021-02-09 04:16:15 -08:00
metalgearsloth
cd3684e575 Guard against client appearancecomp changes (#1540)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-09 13:13:05 +01:00
Pieter-Jan Briers
317b8ce965 Fix sudo command mangling quotes. 2021-02-09 12:29:23 +01:00
Pieter-Jan Briers
ff96140afa Fix messed up \n. 2021-02-09 03:17:27 +01:00
Pieter-Jan Briers
bade95f6b7 Fix unit tests 2021-02-09 02:37:27 +01:00
Pieter-Jan Briers
69daaca399 Remove CVar.SECURE and replace auth CVars with env variables. 2021-02-09 01:50:42 +01:00
DrSmugleaf
5af7e60043 Add KeyValuePair YAML serialization (#1538) 2021-02-08 21:49:08 +01:00
DrSmugleaf
ab823dcd12 Add component registration lookup with case insensitive names (#1535) 2021-02-08 18:07:23 +01:00
Paul Ritter
b3976eb8d7 Analyzer Bongaloo 2: The Return (#1512)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
2021-02-08 18:05:27 +01:00
Pieter-Jan Briers
91759cdd3c Harder DynamicTree against NaN.
B2DynamicTree<T> has asserts, DynamicTree<T> handles them gracefully.

NaNs in DynamicTree were causing the internal state to corrupt resulting in the sprite flickering issues.

Fixes https://github.com/space-wizards/space-station-14/issues/2896
2021-02-08 05:03:56 +01:00
Pieter-Jan Briers
b000c3178b FileLogHandler is now internal.
Sandboxing thing.
2021-02-07 23:07:57 +01:00
Vera Aguilera Puerto
63b3ecdd13 Fix SpawnRepeating timer extensions 2021-02-07 17:04:43 +01:00
DrSmugleaf
a183a98f75 Add support for deserializing nullable enum types (#1537) 2021-02-07 14:31:38 +01:00
Acruid
bded7ad228 You can now provide a factory delegate to IoC register calls. 2021-02-06 23:00:08 -08:00
metalgearsloth
75c4f48496 Fix NPCs getting stuck (#1533)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-05 20:38:01 +01:00
Acruid
49fe2d59cf Removes hard-coded color support for writing console shells. Strings with formatting codes can replace this feature.
Added a separate WriteError function to console shell, to mimic writing to stderr.
2021-02-05 01:35:06 -08:00
DrSmugleaf
8a5b7f5146 Add AudioSystem extension methods for the client and server (#1532) 2021-02-05 17:23:36 +11:00
Pieter-Jan Briers
ae4f470e1f Fix duplicate .xaml files. 2021-02-04 15:22:18 +01:00
metalgearsloth
18fcab6f71 Fix loadbp on paused maps (#1531)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-04 23:29:37 +11:00
Acruid
1ae6c32c62 Adds the net_entityreport console command, that displays a network history of entity updates. 2021-02-03 23:19:47 -08:00
Pieter-Jan Briers
cf6925f19b Makes sudo command show output to user. (#1530) 2021-02-03 19:31:32 -08:00
Pieter-Jan Briers
b4c7ffe38a Adds an exec command.
Just like Quake. Man that Carmack guy was onto something.
2021-02-04 03:40:03 +01:00
Pieter-Jan Briers
d5c2f45f14 Fix accidental usage of OpenGL DSA in screenshot code. 2021-02-03 21:10:48 +01:00
Pieter-Jan Briers
dcc88d2d36 Fix KHR_debug detection on OpenGL ES. 2021-02-03 21:10:48 +01:00
metalgearsloth
a274b8dfc2 EntityQuery no longer yields paused by default (#1521)
* Change EntityQuery to not get paused by default

* GetAllComponents

* Fix shell commands

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-04 00:20:31 +11:00
Paul Ritter
f42c1379e0 Revert "Viewport improvements. (#1510)" (#1527)
This reverts commit fc5e3ab69d.
2021-02-03 12:25:12 +01:00
Pieter-Jan Briers
c4fa7e98d4 Fix clients not disconnecting correctly when client is closed. 2021-02-03 00:04:41 +01:00
Vera Aguilera Puerto
8b9dadffb1 MIDI improvements (#1526) 2021-02-02 18:46:24 +01:00
Vera Aguilera Puerto
5e99c6d04d ViewVariables server-side "Add Component" button isn't hidden anymore when searching for components. 2021-02-02 13:28:56 +01:00
Vera Aguilera Puerto
606d232dbb Update ViewVariables to use the new commands API 2021-02-02 13:26:41 +01:00
Vera Aguilera Puerto
df877582a6 You can now add or remove components from the comfort of ViewVariables (#1524) 2021-02-02 12:01:54 +01:00
Acruid
8ffdb090e6 Adds a property on ITransformComponent that prevents the entity from being rotated. Resolves #1479. 2021-02-01 20:27:17 -08:00
Acruid
033d6ffb22 Fixes bug where effects were dying in transit from lag. Resolves #1256. 2021-02-01 19:17:34 -08:00
Acruid
3eb6e067f9 Console Unify (#1513)
* Renamed shared ICommand to IConsoleCommand.

* Lots of refactoring into a shared context.

* Removed ICommonSession from server concmd Execute.

* Added argStr parameter to concmd execute.

* The execute function of client concmds now returns void, use the new shell.RemoteExecuteCommand function to forward commands.

# Conflicts:
#	Robust.Client/Console/Commands/Debug.cs

* Finally move shells and commands into shared.

* Console commands can now be registered directly without a class in a shared context.

* Pulled up ConsoleHost and Console shell into a shared context.

* Pulled up half the functions of ConsoleHost into a shared context.

* Repair rebase damage.

* Make LoadConsoleCommands function not remove any previously registered commands.
2021-02-01 16:40:26 -08:00
Vera Aguilera Puerto
1f64f93ef4 Update nfluidsynth to latest version, 0.3.1 2021-02-01 21:33:22 +01:00
Pieter-Jan Briers
7dd617b0d6 Adds reflection helper methods to script globals. 2021-02-01 19:41:02 +01:00
metalgearsloth
0567b70704 Optimise showbb (#1519)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-01 20:15:50 +11:00
Pieter-Jan Briers
9963e89c14 Sanitize ResourcePath to disallow .. shenigans. 2021-01-31 22:38:40 +01:00
Remie Richards
29e0faed88 Can now animate ITransformComponent.WorldPosition (#1518) 2021-01-30 23:26:01 +01:00
Pieter-Jan Briers
0ce6ff9870 Fix tickrate sync if non-default tickrate saved to client config.
Fixes #1516

Thanks to blue on Discord for helping me diagnose this.
2021-01-30 01:27:42 +01:00
Vera Aguilera Puerto
dbe9a96dfa Reorganizes NumericsHelpers into different files using partial classes. (#1514) 2021-01-29 12:46:27 +01:00
Vera Aguilera Puerto
fc5e3ab69d Viewport improvements. (#1510)
* Remove the main viewport.

* Re-add main viewport in a different place

* Move screen/map translation into ViewportContainer

* Support for viewport resolution

* Fix postfx, add WorldToScreen to Viewport

* Remove useless cast

* Some cleanup.

* Fix incorrect worldBounds when rendering viewports

* nullability

Co-authored-by: 20kdc <asdd2808@gmail.com>
2021-01-29 12:45:04 +01:00
Julian Giebel
ed4974141c Add TryCastValue to CollectionExtensions.cs (#1515)
* Add TryCastValue to CollectionExtensions.cs

* Change TryCastValue to be more generic

* Add notnull constraing for TKey to TryCastValue

Co-authored-by: Julian Giebel <j.giebel@netrocks.info>
2021-01-28 20:45:11 +01:00
Pieter-Jan Briers
3e5efd5ed0 Add AnimatedTextureRect 2021-01-24 23:00:02 +01:00
Pieter-Jan Briers
e1110eadb4 Better unified handling of SpriteSpecifiers
Add more utilities, most notably IRsiStateLike, to make working with SpriteSpecifiers easier. Especially when animated.
2021-01-24 22:59:47 +01:00
Pieter-Jan Briers
06ace83a73 Allow audio playback without grids. 2021-01-24 16:04:23 +01:00
Pieter-Jan Briers
cd01ca924b Fix broken PanelContainer due to previous commit.
God I am tired.
2021-01-24 04:20:51 +01:00
Pieter-Jan Briers
38ad8ce132 Fix UI scaling weirdness with PanelContainer. 2021-01-23 21:51:35 +01:00
20kdc
ee440c2df9 Make the lighting manager much more configurable, including a console lock (#1506) 2021-01-23 21:17:49 +01:00
Pieter-Jan Briers
32f3c863fb Fix UI scaling bugs with ProgressBar 2021-01-23 20:15:45 +01:00
Pieter-Jan Briers
b00e0bef5a Fix UI scaling bug with RichTextLabel 2021-01-23 16:02:31 +01:00
Pieter-Jan Briers
0a09b27918 Fix UI scaling bug with GridContainer 2021-01-23 16:02:23 +01:00
Paul
c9f6a4e32a fixes enum VV 2021-01-21 15:33:42 +01:00
Pieter-Jan Briers
b205a14f69 Exception tolerance for NetManager.OnDisconnect 2021-01-20 21:07:02 +01:00
Pieter-Jan Briers
d5f3292e0a Unregister OnSessionOnPlayerStatusChanged on bound user interfaces.
I am frankly flabbergasted this is only a problem now.
2021-01-20 21:00:24 +01:00
Pieter-Jan Briers
561e4b330e Fix ALL components memory leaking.
:irrational:
2021-01-20 20:45:02 +01:00
Paul
36a5d102ff prevent one error from killing the entire namegenerator from running 2021-01-17 17:17:02 +01:00
Pieter-Jan Briers
b9c39e0953 Fix reconnecting. 2021-01-17 16:08:48 +01:00
Pieter-Jan Briers
ad4c8be132 Add system for preserving Map UIDs across edits. 2021-01-17 15:51:32 +01:00
kira-er
988cbf9a87 VV Enum (#1503) 2021-01-17 01:50:26 +01:00
Acruid
e26512001a Completely removed MsgSetTickRate, the NetConfigurationManager replaces the functionality. This builds on top of the previous commit.
Fixes bug where server was not sending the entire set of replicated cvars.
2021-01-16 15:33:44 -08:00
Pieter-Jan Briers
8e97982f1e Fix net.tickrate not being replicated correctly to clients. 2021-01-16 21:46:10 +01:00
Paul
3ca686298e Fixes ViewVariablesManager using the wrong VVPropEditor for Type in shared not annotated with [NetSerializable] 2021-01-16 20:05:15 +01:00
20kdc
5e914cb13a PointLightComponent: Don't dirty if enabled is being set to what it's already set to. (#1507) 2021-01-16 00:13:55 +11:00
Pieter-Jan Briers
a1bdfca8ba Fix SimplePredictReconcileTest. 2021-01-15 11:03:52 +01:00
20kdc
79deaca409 Polar Coordinates: simplify maths, any-angle occluders are no longer evil (#1504)
Someone please performance-test this, but I think the fragment shader code simplifications speak for themselves.

Radrark found the "line in polar coordinates" equation I needed and a diagram that explained the variables.

That equation was essentially the missing piece to the whole thing.
2021-01-14 13:44:25 +01:00
20kdc
2eeb21431b Fix FileDialogManager not doing DLL mapping properly in sandbox, and FileDialogManager hard crash on multiple dialogs (#1505) 2021-01-14 13:43:39 +01:00
chairbender
c4062bcae9 #1449 new ControlFocusExited override for allowing controls to know (#1467)
when they lost control focus, separate from keyboard focus
2021-01-13 23:18:45 +01:00
Acruid
cd3a85ea04 Replicated CVars (#1489) 2021-01-13 10:02:08 +01:00
Pieter-Jan Briers
d15b5c7f22 Fix compiler warnings. 2021-01-13 03:10:51 +01:00
Leo
18bbe2271d Adds extension method to read and write colors on NetMessages (#1500) 2021-01-13 01:12:55 +01:00
Pieter-Jan Briers
ee2b7a3a66 Adds gcm function to ScriptGlobalsShared 2021-01-12 21:17:35 +01:00
Pieter-Jan Briers
ca36671131 Fix nullable error in RadioOptions 2021-01-12 21:17:26 +01:00
Pieter-Jan Briers
604a1a6960 Disable CodeQL crap since it STILL doesn't do .NET 5 and I'm sick of the errors. 2021-01-12 17:20:11 +01:00
Pieter-Jan Briers
2898f5396f Account for windows time period latency in Lidgren.
1. Set timeBeginPeriod(3) on the server to reduce scheduler latency in the lidgren thread.
2. Add 16ms of guaranteed lag bias to client prediction calculations to account for scheduler latency.

Both of these changes are to account for how the windows scheduler seems to handle time periods in related to socket polls. See this Discord conversation for why, details down below as well: https://discord.com/channels/310555209753690112/770682801607278632/798309250291204107

Basically Windows has this thing called time periods which determines the precision of sleep operations and such. By default it's like 16ms so a sleep will only be accurate to within 16ms.

Problem: Lidgren polls the socket with a timeout of 1ms.

The way Windows seems to handle this is that:
1. if a message comes into the socket, the poll immediately ends and Lidgren can handle it.
2. If nothing comes in, it takes the whole 16ms time period to actually process stuff.

Oh yeah, and Lidgren's thread needs to keep pumping at a steady rate or else it *won't flush its send queue*. On Windows it seems to normally pump at 65/125 Hz. On Linux it goes like 950 Hz as intended.

Now, the worst part is that (1) causes Lidgren's latency calculation to always read 0 (over localhost) instead of the 30~ms it SHOULD BE (assuming client and server localhost).

That 30ms of unaccounted delay worst caseis enough to cause prediction undershoot and have messages arrive too late. Yikes.

So, to fix this...

On the server we just decrease the tick period and call it a day. Screw your battery life players don't have local servers running anyways.

On the client we bias the prediction calculations to account for this "unmeasurable" lag.

Of course, all this can be configured via CVars.
2021-01-12 02:43:15 +01:00
bgare89
39541639c5 Add RadioOptions.cs (#1484)
Co-authored-by: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com>
2021-01-12 01:45:15 +01:00
metalgearsloth
50981ad1a1 Layered PlacementManager (#1403)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-01-11 22:42:54 +11:00
Pieter-Jan Briers
0cbfbeffae Revert "Analyzer to check if interfacemethods are explicitly marked as such" (#1499)
This reverts commit e603153016.
2021-01-11 12:26:23 +01:00
Paul Ritter
e603153016 Analyzer to check if interfacemethods are explicitly marked as such (#1477)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
2021-01-11 12:14:26 +01:00
Pieter-Jan Briers
e7c417ca0c Remove a couple MiB of memory usage. 2021-01-11 10:58:42 +01:00
Pieter-Jan Briers
a3989f28eb Re-order exception handler in StatusHost to avoid blowing up in some cases ??? 2021-01-11 10:24:29 +01:00
Pieter-Jan Briers
38ace3c348 Fix exception with trying to place entities at... NaN?
This is still a bug but it should not cause an exception on the server
2021-01-11 10:24:29 +01:00
Ygg01
0e00170f45 Make RSI directions default to 1. (#1495)
Add some helper methods, made an `example.rsi` for testing.

Closes #1463
2021-01-11 18:12:34 +11:00
Pieter-Jan Briers
261ee96cad Make client connect 1 second faster.
Whoops.
2021-01-11 02:39:35 +01:00
Pieter-Jan Briers
2c851885db Fix ItemList not working correctly with scaling. 2021-01-11 02:06:25 +01:00
Pieter-Jan Briers
849be86455 Add launchauth command to bootstrap login tokens for connecting to live servers. 2021-01-10 23:55:01 +01:00
Pieter-Jan Briers
ffd5c120be Add PreserveBaseOverridesAttribute to sandbox whitelist.
Used by covariant returns.
2021-01-10 22:03:18 +01:00
Vera Aguilera Puerto
76b15dda70 Client-side addcompc/rmcompc (#1497) 2021-01-10 20:09:14 +01:00
Vera Aguilera Puerto
0bf7f519ad Adds client-side setinputcontext command (#1498) 2021-01-10 20:08:44 +01:00
Vera Aguilera Puerto
f97f325a36 Fixes sprite scale not being taken into account. (#1496)
* Fixes sprite scale not being taken into account.
- Only apply scale if length of scale vector not close or equal to 1.

* I blame Remie

* Fix vector maths
I should spend more time studying maths instead of contributing, to be fair.

* Remie no please nO
2021-01-10 19:53:04 +01:00
metalgearsloth
24315fa787 Avoid unnecessary EntMapIdChangedMessages (#1493)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-01-10 12:46:59 +11:00
Ygg01
792179657b Fix space in dotnet breaking windows builds (#1490)
Who would win?
Thirty developers
-OR-
One sneaky ` ` boi?

On windows default path to dotnet are `C:\Program Files`. Without quoting the
the command if it contains a space, it's going to break Windows builds that
install in `C:\Program Files`.
2021-01-08 13:53:42 +01:00
Paul
9b92bcf911 intermediate fix to garantuee people with dotnet in path to launch commpilerobustxaml 2021-01-05 12:15:03 +01:00
metalgearsloth
86e34ea27b Fix enum caching (#1485)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-01-05 16:17:20 +11:00
Clyybber
62cf778958 Fix build when dotnet is not in the PATH (#1483) 2021-01-04 20:25:43 +01:00
py01
5d667e44c3 Replaces AnchoredChanged C# event with a ComponentMessage (#1482)
Co-authored-by: py01 <pyronetics01@gmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2021-01-04 10:16:05 +01:00
metalgearsloth
6d84b8741c Cache enum references (#1480)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-01-03 03:45:02 +01:00
DrSmugleaf
d08ca59b75 Make RobustUnitTest register content cvars and set _isServer (#1481) 2021-01-03 03:44:12 +01:00
Pieter-Jan Briers
f06b046c1c Fix Content-Type not being set for status API. 2021-01-03 03:42:21 +01:00
Vera Aguilera Puerto
bb412a6906 Entity Timer SpawnTimer throws if entity is deleted. 2021-01-01 15:45:13 +01:00
Radrark
1baee3004c Adds new keybind property: AllowSubCombs (#1473)
Co-authored-by: Radrark <null>
2020-12-29 15:59:34 +01:00
Pieter-Jan Briers
f18068c13a Update NetSerializer submodule to improve build times 2020-12-29 15:59:13 +01:00
Pieter-Jan Briers
0ba00a1845 Gcf command to mess with LOH compaction. 2020-12-29 14:10:15 +01:00
Pieter-Jan Briers
28caf0d74c Reduce allocations 2020-12-29 03:58:16 +01:00
Pieter-Jan Briers
ecbb32b70b Clean up various cases of failing to dispose file streams. 2020-12-29 03:58:16 +01:00
Pieter-Jan Briers
8e2a9cc597 Font atlasses are now dynamic and can render all glyphs in the font file. 2020-12-29 03:58:16 +01:00
Vera Aguilera Puerto
a7eb8201c9 Fix MIDI soundfont load error on non-rooted paths. 2020-12-26 22:05:17 +01:00
Pieter-Jan Briers
1f95fe6782 Fix XamlIL problems when publishing 2020-12-25 18:34:19 +01:00
DrSmugleaf
07c1f9e1af Replace MaybeNullWhen(false) with NotNullWhen(true) in AppearanceComponent (#1461) 2020-12-25 15:17:54 +01:00
Vera Aguilera Puerto
826dce6659 Fix custom MIDI soundfont loading when mounting content from zip
Fixes #1466
2020-12-24 03:54:06 +01:00
Vera Aguilera Puerto
cdf714f3ba Fix stereo ogg audio not playing correctly.
Only half of the samples were being read.
2020-12-23 16:21:42 +01:00
Pieter-Jan Briers
671ca7959c Throw debug info if the RichTextEntry assert fails. 2020-12-23 15:12:47 +01:00
Pieter-Jan Briers
b7a1345d3a XAML compilation improvements.
Prevent double XAML-ifying causing corrupt dlls.

Use MSBuild dependencies to reduce unecessary xamlil builds.
2020-12-23 12:01:35 +01:00
Pieter-Jan Briers
835b6ebdba Probably fix build forgetting that nuget exists. 2020-12-21 12:14:28 +01:00
Pieter-Jan Briers
0ecabd6553 Fix XamlIL locking build. 2020-12-21 12:13:45 +01:00
Pieter-Jan Briers
feaa69f825 Fix build of injector. 2020-12-21 04:05:34 +01:00
Pieter-Jan Briers
857904a3d9 Update dependencies of name generator. 2020-12-21 04:05:25 +01:00
Pieter-Jan Briers
0b37418477 Fix injectors UsingTask. 2020-12-21 03:31:11 +01:00
Pieter-Jan Briers
f234ecb2c3 Make Robust.Client.Injectors NS2.0
So that it works out of the box with Framework MSBuild.
2020-12-21 03:16:13 +01:00
Pieter-Jan Briers
b449959865 Clean up bad project reference in Robust.Server 2020-12-21 03:15:45 +01:00
Pieter-Jan Briers
8f870403d2 Managed implementation of HttpListener. (#1460) 2020-12-21 02:51:04 +01:00
Paul Ritter
d94f702601 Xaml UI (#1446)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2020-12-20 23:52:36 +01:00
DrSmugleaf
e78ab8f922 Add YamlObjectSerializer.NodeToType with generic argument (#1456) 2020-12-20 20:46:56 +01:00
20kdc
6972000293 Make the "softness" of soft shadows adjustible per-light. (#1454)
Note: Thanks to the nature of YAML properties in RobustToolbox, this commit is only an API blocker if the Softness property is directly manipulated from code, which is unlikely.
2020-12-19 21:44:47 +01:00
DrSmugleaf
58560f589f Defer MoveEvent out of TransformComponent.HandleComponentState (#1453)
* Defer MoveEvent out of TransformComponent.HandleComponentState

* Imports

* Make the update loop more readable and call ToArray

* Fix tests

* Fix tests HALLELUJAH
2020-12-19 13:09:16 +01:00
Pieter-Jan Briers
6e931ac175 Fix some CVars not saving. 2020-12-19 02:31:46 +01:00
Pieter-Jan Briers
a7eb5e8115 Use nvidia GPU on optimus laptops.
With an undocumented crappy hack, of course.
2020-12-19 02:25:10 +01:00
metalgearsloth
712e4acc66 Cache TryLooseGetType (#1448)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2020-12-19 01:42:51 +01:00
Pieter-Jan Briers
fdcfdffc0b Provide fallback for /status API if content does not override it. 2020-12-19 00:43:46 +01:00
Pieter-Jan Briers
74eb8e3e8d Allow build.json contents to be overriden by --cvar. 2020-12-17 17:13:07 +01:00
Pieter-Jan Briers
ae4c764e4f Whitelist System.Guid for sandbox. 2020-12-17 16:37:20 +01:00
770 changed files with 12921 additions and 7323 deletions

View File

@@ -11,14 +11,14 @@
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '30 18 * * 6'
#on:
# push:
# branches: [ master ]
# pull_request:
# # The branches below must be a subset of the branches above
# branches: [ master ]
# schedule:
# - cron: '30 18 * * 6'
jobs:
analyze:
@@ -38,12 +38,12 @@ jobs:
uses: actions/checkout@v2
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.100
- name: Build
run: dotnet build

6
.gitmodules vendored
View File

@@ -4,6 +4,12 @@
[submodule "Lidgren.Network"]
path = Lidgren.Network/Lidgren.Network
url = https://github.com/space-wizards/lidgren-network-gen3.git
[submodule "XamlX"]
path = XamlX
url = https://github.com/space-wizards/XamlX
[submodule "Robust.LoaderApi"]
path = Robust.LoaderApi
url = https://github.com/space-wizards/Robust.LoaderApi.git
[submodule "ManagedHttpListener"]
path = ManagedHttpListener
url = https://github.com/space-wizards/ManagedHttpListener.git

View File

@@ -0,0 +1,5 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Analyzers\Robust.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>
</Project>

View File

@@ -1,2 +1,3 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<Import Project="Robust.Analyzers.targets" />
</Project>

65
MSBuild/XamlIL.targets Normal file
View File

@@ -0,0 +1,65 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<PropertyGroup>
<!-- Avoid MSBuild adding a None entry for XAML files because they'd show up TWICE in the project view. -->
<DefaultItemExcludes>**/*.xaml</DefaultItemExcludes>
<RobustUseExternalMSBuild>true</RobustUseExternalMSBuild>
<_RobustUseExternalMSBuild>$(RobustUseExternalMSBuild)</_RobustUseExternalMSBuild>
<_RobustUseExternalMSBuild Condition="'$(_RobustForceInternalMSBuild)' == 'true'">false</_RobustUseExternalMSBuild>
</PropertyGroup>
<ItemGroup>
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<EmbeddedResource Include="**\*.xaml"/>
<AdditionalFiles Include="**\*.xaml"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false"/>
</ItemGroup>
<UsingTask
Condition="'$(_RobustUseExternalMSBuild)' != 'true' And $(DesignTimeBuild) != true"
TaskName="CompileRobustXamlTask"
AssemblyFile="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(Configuration)\netstandard2.0\Robust.Client.Injectors.dll"/>
<Target
Name="CompileRobustXaml"
Condition="Exists('@(IntermediateAssembly)')"
AfterTargets="AfterCompile"
Inputs="@(IntermediateAssembly);@(ReferencePathWithRefAssemblies)"
Outputs="$(IntermediateOutputPath)XAML/doot">
<PropertyGroup>
<RobustXamlReferencesTemporaryFilePath Condition="'$(RobustXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)XAML/references</RobustXamlReferencesTemporaryFilePath>
<RobustXamlOriginalCopyFilePath Condition="'$(RobustXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)XAML/original.dll</RobustXamlOriginalCopyFilePath>
</PropertyGroup>
<WriteLinesToFile
Condition="'$(_RobustForceInternalMSBuild)' != 'true'"
File="$(RobustXamlReferencesTemporaryFilePath)"
Lines="@(ReferencePathWithRefAssemblies)"
Overwrite="true"/>
<!--
UpdateBuildIndicator is done so that we can use MSBuild Inputs and Outputs on the target
to avoid unecessary execution of this target
Saves compile time if e.g. ONLY Robust.Client changes (Content.Client doesn't have to re-xaml).
-->
<CompileRobustXamlTask
Condition="'$(_RobustUseExternalMSBuild)' != 'true'"
AssemblyFile="@(IntermediateAssembly)"
ReferencesFilePath="$(RobustXamlReferencesTemporaryFilePath)"
OriginalCopyPath="$(RobustXamlOriginalCopyFilePath)"
ProjectDirectory="$(MSBuildProjectDirectory)"
AssemblyOriginatorKeyFile="$(AssemblyOriginatorKeyFile)"
SignAssembly="$(SignAssembly)"
DelaySign="$(DelaySign)"
UpdateBuildIndicator="$(IntermediateOutputPath)XAML/doot"/>
<PropertyGroup>
<DOTNET_HOST_PATH Condition="'$(DOTNET_HOST_PATH)' == ''">dotnet</DOTNET_HOST_PATH>
</PropertyGroup>
<Exec
Condition="'$(_RobustUseExternalMSBuild)' == 'true'"
Command="&quot;$(DOTNET_HOST_PATH)&quot; msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileRobustXaml /p:_RobustForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
</Target>
</Project>

1
ManagedHttpListener Submodule

Submodule ManagedHttpListener added at f2aa590fec

View File

@@ -17,6 +17,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
// On net472, we rely on Mono's DllMap for this. See the .dll.config file.
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;

View File

@@ -13,7 +13,7 @@ highp float createOcclusion(highp vec2 diff)
// Change soft shadow size based on distance from primary occluder.
highp float distRatio = (ourDist - occlDist.x) / occlDist.x / 2.0;
perpendicular *= distRatio;
perpendicular *= distRatio * lightSoftness;
// Totally not hacky PCF on top of VSM.
highp float occlusion = smoothstep(0.1, 1.0, ChebyshevUpperBound(occlDist, ourDist));

View File

@@ -13,6 +13,7 @@ uniform highp vec4 lightColor;
uniform highp vec2 lightCenter;
uniform highp float lightRange;
uniform highp float lightPower;
uniform highp float lightSoftness;
uniform highp float lightIndex;
uniform sampler2D shadowMap;

View File

@@ -0,0 +1,92 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Document = Microsoft.CodeAnalysis.Document;
namespace Robust.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ExplicitInterfaceAnalyzer : DiagnosticAnalyzer
{
public readonly SyntaxKind[] ExcludedModifiers =
{
SyntaxKind.VirtualKeyword,
SyntaxKind.AbstractKeyword,
SyntaxKind.OverrideKeyword
};
public const string DiagnosticId = "RA0000";
private const string Title = "No explicit interface specified";
private const string MessageFormat = "No explicit interface specified";
private const string Description = "Make sure to specify the interface in your method-declaration.";
private const string Category = "Usage";
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
private const string RequiresExplicitImplementationAttributeMetadataName =
"Robust.Shared.Analyzers.RequiresExplicitImplementationAttribute";
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration);
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration);
}
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
ISymbol symbol;
Location location;
switch (context.Node)
{
//we already have a explicit interface specified, no need to check further
case MethodDeclarationSyntax methodDecl when methodDecl.ExplicitInterfaceSpecifier != null || methodDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
return;
case PropertyDeclarationSyntax propertyDecl when propertyDecl.ExplicitInterfaceSpecifier != null || propertyDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
return;
case MethodDeclarationSyntax methodDecl:
symbol = context.SemanticModel.GetDeclaredSymbol(methodDecl);
location = methodDecl.Identifier.GetLocation();
break;
case PropertyDeclarationSyntax propertyDecl:
symbol = context.SemanticModel.GetDeclaredSymbol(propertyDecl);
location = propertyDecl.Identifier.GetLocation();
break;
default:
return;
}
var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresExplicitImplementationAttributeMetadataName);
var isInterfaceMember = symbol?.ContainingType.AllInterfaces.Any(
i =>
i.GetMembers().Any(m => SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(m)))
&& i.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))
) ?? false;
if (isInterfaceMember)
{
//we do not have an explicit interface specified. bad!
var diagnostic = Diagnostic.Create(
Rule,
location);
context.ReportDiagnostic(diagnostic);
}
}
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.8.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,150 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Robust.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class SerializableAnalyzer : DiagnosticAnalyzer
{
// Metadata of the analyzer
public const string DiagnosticId = "RA0001";
// You could use LocalizedString but it's a little more complicated for this sample
private const string Title = "Class not marked as (Net)Serializable";
private const string MessageFormat = "Class not marked as (Net)Serializable";
private const string Description = "The class should be marked as (Net)Serializable.";
private const string Category = "Usage";
private const string RequiresSerializableAttributeMetadataName = "Robust.Shared.Analyzers.RequiresSerializableAttribute";
private const string SerializableAttributeMetadataName = "System.SerializableAttribute";
private const string NetSerializableAttributeMetadataName = "Robust.Shared.Serialization.NetSerializableAttribute";
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
}
private bool Marked(INamedTypeSymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
{
if (namedTypeSymbol == null) return false;
if (namedTypeSymbol.GetAttributes()
.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))) return true;
return Marked(namedTypeSymbol.BaseType, attrSymbol);
}
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresSerializableAttributeMetadataName);
var classDecl = (ClassDeclarationSyntax) context.Node;
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl);
if (classSymbol == null) return;
if (Marked(classSymbol, attrSymbol))
{
var attributes = classSymbol.GetAttributes();
var serAttr = context.Compilation.GetTypeByMetadataName(SerializableAttributeMetadataName);
var netSerAttr = context.Compilation.GetTypeByMetadataName(NetSerializableAttributeMetadataName);
var hasSerAttr = attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, serAttr));
var hasNetSerAttr =
attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, netSerAttr));
if (!hasSerAttr || !hasNetSerAttr)
{
var requiredAttributes = new List<string>();
if(!hasSerAttr) requiredAttributes.Add(SerializableAttributeMetadataName);
if(!hasNetSerAttr) requiredAttributes.Add(NetSerializableAttributeMetadataName);
context.ReportDiagnostic(
Diagnostic.Create(
Rule,
classDecl.Identifier.GetLocation(),
ImmutableDictionary.CreateRange(new Dictionary<string, string>()
{
{
"requiredAttributes", string.Join(",", requiredAttributes)
}
})));
}
}
}
}
[ExportCodeFixProvider(LanguageNames.CSharp)]
public class SerializableCodeFixProvider : CodeFixProvider
{
private const string Title = "Annotate class as (Net)Serializable.";
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
foreach (var diagnostic in context.Diagnostics)
{
var span = diagnostic.Location.SourceSpan;
var classDecl = root.FindToken(span.Start).Parent.AncestorsAndSelf().OfType<ClassDeclarationSyntax>().First();
if(!diagnostic.Properties.TryGetValue("requiredAttributes", out var requiredAttributes)) return;
context.RegisterCodeFix(
CodeAction.Create(
Title,
c => FixAsync(context.Document, classDecl, requiredAttributes, c),
Title),
diagnostic);
}
}
private async Task<Document> FixAsync(Document document, ClassDeclarationSyntax classDecl,
string requiredAttributes, CancellationToken cancellationToken)
{
var attributes = new List<AttributeSyntax>();
var namespaces = new List<string>();
foreach (var attribute in requiredAttributes.Split(','))
{
var tempSplit = attribute.Split('.');
namespaces.Add(string.Join(".",tempSplit.Take(tempSplit.Length-1)));
var @class = tempSplit.Last();
@class = @class.Substring(0, @class.Length - 9); //cut out "Attribute" at the end
attributes.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName(@class)));
}
var newClassDecl =
classDecl.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(attributes)));
var root = (CompilationUnitSyntax) await document.GetSyntaxRootAsync(cancellationToken);
root = root.ReplaceNode(classDecl, newClassDecl);
foreach (var ns in namespaces)
{
if(root.Usings.Any(u => u.Name.ToString() == ns)) continue;
root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ns)));
}
return document.WithSyntaxRoot(root);
}
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(SerializableAnalyzer.DiagnosticId);
public override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
namespace Robust.Build.Tasks
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
/// </summary>
public class CompileRobustXamlTask : ITask
{
public bool Execute()
{
//Debugger.Launch();
OutputPath = OutputPath ?? AssemblyFile;
var outputPdb = GetPdbPath(OutputPath);
var input = AssemblyFile;
var inputPdb = GetPdbPath(input);
// Make a copy and delete the original file to prevent MSBuild from thinking that everything is OK
if (OriginalCopyPath != null)
{
File.Copy(AssemblyFile, OriginalCopyPath, true);
input = OriginalCopyPath;
File.Delete(AssemblyFile);
if (File.Exists(inputPdb))
{
var copyPdb = GetPdbPath(OriginalCopyPath);
File.Copy(inputPdb, copyPdb, true);
File.Delete(inputPdb);
inputPdb = copyPdb;
}
}
var msg = $"CompileRobustXamlTask -> AssemblyFile:{AssemblyFile}, ProjectDirectory:{ProjectDirectory}, OutputPath:{OutputPath}";
BuildEngine.LogMessage(msg, MessageImportance.High);
var res = XamlCompiler.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath,
(SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null);
if (!res.success)
return false;
if (!res.writtentofile)
{
File.Copy(input, OutputPath, true);
if(File.Exists(inputPdb))
File.Copy(inputPdb, outputPdb, true);
}
if (!string.IsNullOrEmpty(UpdateBuildIndicator))
{
if (!File.Exists(UpdateBuildIndicator))
{
File.Create(UpdateBuildIndicator).Dispose();
}
else
{
File.SetLastWriteTime(UpdateBuildIndicator, DateTime.Now);
}
}
return true;
}
[Required]
public string ReferencesFilePath { get; set; }
[Required]
public string ProjectDirectory { get; set; }
[Required]
public string AssemblyFile { get; set; }
[Required]
public string OriginalCopyPath { get; set; }
public string OutputPath { get; set; }
public string UpdateBuildIndicator { get; set; }
public string AssemblyOriginatorKeyFile { get; set; }
public bool SignAssembly { get; set; }
public bool DelaySign { get; set; }
// shamelessly copied from avalonia
string GetPdbPath(string p)
{
var d = Path.GetDirectoryName(p);
var f = Path.GetFileNameWithoutExtension(p);
var rv = f + ".pdb";
if (d != null)
rv = Path.Combine(d, rv);
return rv;
}
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using Microsoft.Build.Framework;
namespace Robust.Build.Tasks
{
/// <summary>
/// Taken from https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/Extensions.cs
/// </summary>
public static class Extensions
{
//shamefully copied from avalonia
public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp)
{
engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp));
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Linq;
using Pidgin;
using static Pidgin.Parser;
namespace Robust.Build.Tasks
{
public static class MathParsing
{
public static Parser<char, float> Single { get; } = Real.Select(c => (float) c);
public static Parser<char, float> Single1 { get; }
= Single.Between(SkipWhitespaces);
public static Parser<char, (float, float)> Single2 { get; }
= Single.Before(SkipWhitespaces).Repeat(2).Select(e =>
{
var arr = e.ToArray();
return (arr[0], arr[1]);
});
public static Parser<char, (float, float, float, float)> Single4 { get; }
= Single.Before(SkipWhitespaces).Repeat(4).Select(e =>
{
var arr = e.ToArray();
return (arr[0], arr[1], arr[2], arr[3]);
});
public static Parser<char, float[]> Thickness { get; }
= SkipWhitespaces.Then(
OneOf(
Try(Single1.Select(c => new[] {c})),
Try(Single2.Select(c => new[] {c.Item1, c.Item2})),
Try(Single4.Select(c => new[] {c.Item1, c.Item2, c.Item3, c.Item4}))
));
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections;
using System.IO;
using Microsoft.Build.Framework;
namespace Robust.Build.Tasks
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/Program.cs
/// </summary>
class Program
{
static int Main(string[] args)
{
if (args.Length != 3)
{
Console.Error.WriteLine("expected: input references output");
return 1;
}
return new CompileRobustXamlTask
{
AssemblyFile = args[0],
ReferencesFilePath = args[1],
OutputPath = args[2],
BuildEngine = new ConsoleBuildEngine(),
ProjectDirectory = Directory.GetCurrentDirectory()
}.Execute() ? 0 : 2;
}
}
class ConsoleBuildEngine : IBuildEngine
{
public void LogErrorEvent(BuildErrorEventArgs e)
{
Console.WriteLine($"ERROR: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogWarningEvent(BuildWarningEventArgs e)
{
Console.WriteLine($"WARNING: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogMessageEvent(BuildMessageEventArgs e)
{
Console.WriteLine($"MESSAGE: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogCustomEvent(CustomBuildEventArgs e)
{
Console.WriteLine($"CUSTOM: {e.Message}");
}
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties,
IDictionary targetOutputs) => throw new NotSupportedException();
public bool ContinueOnError { get; }
public int LineNumberOfTaskNode { get; }
public int ColumnNumberOfTaskNode { get; }
public string ProjectFileOfTaskNode { get; }
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Linq;
using System.Reflection.Emit;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
public abstract class RXamlVecLikeConstAstNode<T>
: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
where T : unmanaged
{
private readonly IXamlConstructor _constructor;
protected readonly T[] Values;
public RXamlVecLikeConstAstNode(
IXamlLineInfo lineInfo,
IXamlType type, IXamlConstructor constructor,
IXamlType componentType, T[] values)
: base(lineInfo)
{
_constructor = constructor;
Values = values;
var @params = constructor.Parameters;
if (@params.Count != values.Length)
throw new ArgumentException("Invalid amount of parameters");
if (@params.Any(c => c != componentType))
throw new ArgumentException("Invalid constructor: not all parameters match component type");
Type = new XamlAstClrTypeReference(lineInfo, type, false);
}
public IXamlAstTypeReference Type { get; }
public virtual XamlILNodeEmitResult Emit(
XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter codeGen)
{
codeGen.Newobj(_constructor);
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
public sealed class RXamlSingleVecLikeConstAstNode : RXamlVecLikeConstAstNode<float>
{
public RXamlSingleVecLikeConstAstNode(
IXamlLineInfo lineInfo,
IXamlType type, IXamlConstructor constructor,
IXamlType componentType, float[] values)
: base(lineInfo, type, constructor, componentType, values)
{
}
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter codeGen)
{
foreach (var value in Values)
{
codeGen.Emit(OpCodes.Ldc_R4, value);
}
return base.Emit(context, codeGen);
}
}
public sealed class RXamlInt32VecLikeConstAstNode : RXamlVecLikeConstAstNode<int>
{
public RXamlInt32VecLikeConstAstNode(
IXamlLineInfo lineInfo,
IXamlType type, IXamlConstructor constructor,
IXamlType componentType, int[] values)
: base(lineInfo, type, constructor, componentType, values)
{
}
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter codeGen)
{
foreach (var value in Values)
{
codeGen.Emit(OpCodes.Ldc_I4, value);
}
return base.Emit(context, codeGen);
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Linq;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
class RXamlWellKnownTypes
{
public XamlTypeWellKnownTypes XamlIlTypes { get; }
public IXamlType Single { get; }
public IXamlType Int32 { get; }
public IXamlType Vector2 { get; }
public IXamlConstructor Vector2ConstructorFull { get; }
public IXamlType Vector2i { get; }
public IXamlConstructor Vector2iConstructorFull { get; }
public RXamlWellKnownTypes(TransformerConfiguration cfg)
{
var ts = cfg.TypeSystem;
XamlIlTypes = cfg.WellKnownTypes;
Single = ts.GetType("System.Single");
Int32 = ts.GetType("System.Int32");
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2", Single, 2);
(Vector2i, Vector2iConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2i", Int32, 2);
(IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
{
var type = cfg.TypeSystem.GetType(name);
var ctor = type.GetConstructor(Enumerable.Repeat(componentType, componentCount).ToList());
return (type, ctor);
}
}
}
static class RXamlWellKnownTypesExtensions
{
public static RXamlWellKnownTypes GetRobustTypes(this AstTransformationContext ctx)
{
if (ctx.TryGetItem<RXamlWellKnownTypes>(out var rv))
return rv;
ctx.SetItem(rv = new RXamlWellKnownTypes(ctx.Configuration));
return rv;
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="16.8.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="Pidgin" Version="2.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\XamlX\src\XamlX.IL.Cecil\XamlX.IL.Cecil.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,201 @@
using System.Linq;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
/// <summary>
/// Emitters & Transformers based on:
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
/// </summary>
public class RobustXamlILCompiler : XamlILCompiler
{
public RobustXamlILCompiler(TransformerConfiguration configuration, XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> emitMappings, bool fillWithDefaults) : base(configuration, emitMappings, fillWithDefaults)
{
Transformers.Add(new AddNameScopeRegistration());
Transformers.Add(new RobustMarkRootObjectScopeNode());
Emitters.Add(new AddNameScopeRegistration.Emitter());
Emitters.Add(new RobustMarkRootObjectScopeNode.Emitter());
}
class AddNameScopeRegistration : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is XamlPropertyAssignmentNode pa)
{
if (pa.Property.Name == "Name"
&& pa.Property.DeclaringType.FullName == "Robust.Client.UserInterface.Control")
{
if (context.ParentNodes().FirstOrDefault() is XamlManipulationGroupNode mg
&& mg.Children.OfType<RobustNameScopeRegistrationXamlIlNode>().Any())
return node;
IXamlAstValueNode value = null;
for (var c = 0; c < pa.Values.Count; c++)
if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String))
{
value = pa.Values[c];
if (!(value is XamlAstTextNode))
{
var local = new XamlAstCompilerLocalNode(value);
// Wrap original in local initialization
pa.Values[c] = new XamlAstLocalInitializationNodeEmitter(value, value, local);
// Use local
value = local;
}
break;
}
if (value != null)
{
var objectType = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().FirstOrDefault()?.Type.GetClrType();
return new XamlManipulationGroupNode(pa)
{
Children =
{
pa,
new RobustNameScopeRegistrationXamlIlNode(value, objectType)
}
};
}
}
/*else if (pa.Property.CustomAttributes.Select(attr => attr.Type).Intersect(context.Configuration.TypeMappings.DeferredContentPropertyAttributes).Any())
{
pa.Values[pa.Values.Count - 1] =
new NestedScopeMetadataNode(pa.Values[pa.Values.Count - 1]);
}*/
}
return node;
}
class RobustNameScopeRegistrationXamlIlNode : XamlAstNode, IXamlAstManipulationNode
{
public IXamlAstValueNode Name { get; set; }
public IXamlType TargetType { get; }
public RobustNameScopeRegistrationXamlIlNode(IXamlAstValueNode name, IXamlType targetType) : base(name)
{
TargetType = targetType;
Name = name;
}
public override void VisitChildren(IXamlAstVisitor visitor)
=> Name = (IXamlAstValueNode)Name.Visit(visitor);
}
internal class Emitter : IXamlAstLocalsNodeEmitter<IXamlILEmitter, XamlILNodeEmitResult>
{
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
if (node is RobustNameScopeRegistrationXamlIlNode registration)
{
var scopeField = context.RuntimeContext.ContextType.Fields.First(f =>
f.Name == XamlCompiler.ContextNameScopeFieldName);
var namescopeRegisterFunction = context.Configuration.TypeSystem
.FindType("Robust.Client.UserInterface.XAML.NameScope").Methods
.First(m => m.Name == "Register");
using (var targetLoc = context.GetLocalOfType(context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control")))
{
codeGen
// var target = {pop}
.Stloc(targetLoc.Local)
// _context.NameScope.Register(Name, target)
.Ldloc(context.ContextLocal)
.Ldfld(scopeField);
context.Emit(registration.Name, codeGen, registration.Name.Type.GetClrType());
codeGen
.Ldloc(targetLoc.Local)
.EmitCall(namescopeRegisterFunction, true);
}
return XamlILNodeEmitResult.Void(1);
}
return default;
}
}
}
class RobustMarkRootObjectScopeNode : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (!context.ParentNodes().Any()
&& node is XamlValueWithManipulationNode mnode)
{
mnode.Manipulation = new XamlManipulationGroupNode(mnode,
new[]
{
mnode.Manipulation,
new HandleRootObjectScopeNode(mnode)
});
}
return node;
}
class HandleRootObjectScopeNode : XamlAstNode, IXamlAstManipulationNode
{
public HandleRootObjectScopeNode(IXamlLineInfo lineInfo) : base(lineInfo)
{
}
}
internal class Emitter : IXamlILAstNodeEmitter
{
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
if (!(node is HandleRootObjectScopeNode))
{
return null;
}
var controlType = context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control");
var next = codeGen.DefineLabel();
var dontAbsorb = codeGen.DefineLabel();
var end = codeGen.DefineLabel();
var contextScopeField = context.RuntimeContext.ContextType.Fields.First(f =>
f.Name == XamlCompiler.ContextNameScopeFieldName);
var controlNameScopeField = controlType.Fields.First(f => f.Name == "NameScope");
var nameScopeType = context.Configuration.TypeSystem
.FindType("Robust.Client.UserInterface.XAML.NameScope");
var nameScopeCompleteMethod = nameScopeType.Methods.First(m => m.Name == "Complete");
var nameScopeAbsorbMethod = nameScopeType.Methods.First(m => m.Name == "Absorb");
using (var local = codeGen.LocalsPool.GetLocal(controlType))
{
codeGen
.Isinst(controlType)
.Dup()
.Stloc(local.Local) //store control in local field
.Brfalse(next) //if control is null, move to next (this should never happen but whatev, avalonia does it)
.Ldloc(context.ContextLocal)
.Ldfld(contextScopeField)
.Ldloc(local.Local) //load control from local field
.Ldfld(controlNameScopeField) //load namescope field from control
.EmitCall(nameScopeAbsorbMethod, true)
.Ldloc(local.Local) //load control
.Ldloc(context.ContextLocal) //load contextObject
.Ldfld(contextScopeField) //load namescope field from context obj
.Stfld(controlNameScopeField) //store namescope field in control
.MarkLabel(next)
.Ldloc(context.ContextLocal)
.Ldfld(contextScopeField)
.EmitCall(nameScopeCompleteMethod, true); //set the namescope as complete
}
return XamlILNodeEmitResult.Void(1);
}
}
}
}
}

View File

@@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
/// <summary>
/// Helpers taken from:
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
/// </summary>
public partial class XamlCompiler
{
static bool CheckXamlName(IResource r) => r.Name.ToLowerInvariant().EndsWith(".xaml")
|| r.Name.ToLowerInvariant().EndsWith(".paml")
|| r.Name.ToLowerInvariant().EndsWith(".axaml");
private static bool MatchThisCall(Collection<Instruction> instructions, int idx)
{
var i = instructions[idx];
// A "normal" way of passing `this` to a static method:
// ldarg.0
// call void [Avalonia.Markup.Xaml]Avalonia.Markup.Xaml.AvaloniaXamlLoader::Load(object)
return i.OpCode == OpCodes.Ldarg_0 || (i.OpCode == OpCodes.Ldarg && i.Operand?.Equals(0) == true);
}
interface IResource : IFileSource
{
string Uri { get; }
string Name { get; }
void Remove();
}
interface IResourceGroup
{
string Name { get; }
IEnumerable<IResource> Resources { get; }
}
class EmbeddedResources : IResourceGroup
{
private readonly AssemblyDefinition _asm;
public string Name => "EmbeddedResource";
public IEnumerable<IResource> Resources => _asm.MainModule.Resources.OfType<EmbeddedResource>()
.Select(r => new WrappedResource(_asm, r)).ToList();
public EmbeddedResources(AssemblyDefinition asm)
{
_asm = asm;
}
class WrappedResource : IResource
{
private readonly AssemblyDefinition _asm;
private readonly EmbeddedResource _res;
public WrappedResource(AssemblyDefinition asm, EmbeddedResource res)
{
_asm = asm;
_res = res;
}
public string Uri => $"resm:{Name}?assembly={_asm.Name.Name}";
public string Name => _res.Name;
public string FilePath => Name;
public byte[] FileContents => _res.GetResourceData();
public void Remove() => _asm.MainModule.Resources.Remove(_res);
}
}
}
}

View File

@@ -0,0 +1,363 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Pidgin;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Parsers;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
/// Adjusted for our UI-Framework
/// </summary>
public partial class XamlCompiler
{
public static (bool success, bool writtentofile) Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory, string output, string strongNameKey)
{
var typeSystem = new CecilTypeSystem(references
.Where(r => !r.ToLowerInvariant().EndsWith("robust.build.tasks.dll"))
.Concat(new[] { input }), input);
var asm = typeSystem.TargetAssemblyDefinition;
if (asm.MainModule.GetType("CompiledRobustXaml", "XamlIlContext") != null)
{
// If this type exists, the assembly has already been processed by us.
// Do not run again, it would corrupt the file.
// This *shouldn't* be possible due to Inputs/Outputs dependencies in the build system,
// but better safe than sorry eh?
engine.LogWarningEvent(new BuildWarningEventArgs("XAMLIL", "", "", 0, 0, 0, 0, "Ran twice on same assembly file; ignoring.", "", ""));
return (true, false);
}
var compileRes = CompileCore(engine, typeSystem);
if (compileRes == null)
return (true, false);
if (compileRes == false)
return (false, false);
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
if (!string.IsNullOrWhiteSpace(strongNameKey))
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
asm.Write(output, writerParameters);
return (true, true);
}
static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem)
{
var asm = typeSystem.TargetAssemblyDefinition;
var embrsc = new EmbeddedResources(asm);
if (embrsc.Resources.Count(CheckXamlName) == 0)
// Nothing to do
return null;
var xamlLanguage = new XamlLanguageTypeMappings(typeSystem)
{
XmlnsAttributes =
{
typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"),
},
ContentAttributes =
{
typeSystem.GetType("Robust.Client.UserInterface.XAML.ContentAttribute")
},
UsableDuringInitializationAttributes =
{
typeSystem.GetType("Robust.Client.UserInterface.XAML.UsableDuringInitializationAttribute")
},
DeferredContentPropertyAttributes =
{
typeSystem.GetType("Robust.Client.UserInterface.XAML.DeferredContentAttribute")
},
RootObjectProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestRootObjectProvider"),
UriContextProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestUriContext"),
ProvideValueTarget = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestProvideValueTarget"),
};
var emitConfig = new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>
{
ContextTypeBuilderCallback = (b,c) => EmitNameScopeField(xamlLanguage, typeSystem, b, c)
};
var transformerconfig = new TransformerConfiguration(
typeSystem,
typeSystem.TargetAssembly,
xamlLanguage,
XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage), CustomValueConverter);
var contextDef = new TypeDefinition("CompiledRobustXaml", "XamlIlContext",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(contextDef);
var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
xamlLanguage, emitConfig);
var compiler =
new RobustXamlILCompiler(transformerconfig, emitConfig, true);
var loaderDispatcherDef = new TypeDefinition("CompiledRobustXaml", "!XamlLoader",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
var loaderDispatcherMethod = new MethodDefinition("TryLoad",
MethodAttributes.Static | MethodAttributes.Public,
asm.MainModule.TypeSystem.Object)
{
Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)}
};
loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
asm.MainModule.Types.Add(loaderDispatcherDef);
var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
m =>
m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 2 &&
m.ReturnType.FullName == "System.Boolean"
&& m.Parameters[0].ParameterType.FullName == "System.String"
&& m.Parameters[1].ParameterType.FullName == "System.String"));
bool CompileGroup(IResourceGroup group)
{
var typeDef = new TypeDefinition("CompiledRobustXaml", "!" + group.Name, TypeAttributes.Class,
asm.MainModule.TypeSystem.Object);
//typeDef.CustomAttributes.Add(new CustomAttribute(ed));
asm.MainModule.Types.Add(typeDef);
var builder = typeSystem.CreateTypeBuilder(typeDef);
foreach (var res in group.Resources.Where(CheckXamlName))
{
try
{
engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", MessageImportance.Low);
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
var parsed = XDocumentXamlParser.Parse(xaml);
var initialRoot = (XamlAstObjectNode) parsed.Root;
var classDirective = initialRoot.Children.OfType<XamlAstXmlDirective>()
.FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class");
string classname;
if (classDirective != null && classDirective.Values[0] is XamlAstTextNode tn)
{
classname = tn.Text;
}
else
{
classname = res.Name.Replace(".xaml","");
}
var classType = typeSystem.TargetAssembly.FindType(classname);
if (classType == null)
throw new Exception($"Unable to find type '{classname}'");
compiler.Transform(parsed);
var populateName = $"Populate:{res.Name}";
var buildName = $"Build:{res.Name}";
var classTypeDefinition = typeSystem.GetTypeReference(classType).Resolve();
var populateBuilder = typeSystem.CreateTypeBuilder(classTypeDefinition);
compiler.Compile(parsed, contextClass,
compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
classTypeDefinition == null),
compiler.DefineBuildMethod(builder, parsed, buildName, true),
null,
(closureName, closureBaseType) =>
populateBuilder.DefineSubType(closureBaseType, closureName, false),
res.Uri, res
);
//add compiled populate method
var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve().Methods
.First(m => m.Name == populateName);
const string TrampolineName = "!XamlIlPopulateTrampoline";
var trampoline = new MethodDefinition(TrampolineName,
MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
classTypeDefinition.Methods.Add(trampoline);
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
var foundXamlLoader = false;
// Find RobustXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
foreach (var method in classTypeDefinition.Methods
.Where(m => !m.Attributes.HasFlag(MethodAttributes.Static)))
{
var i = method.Body.Instructions;
for (var c = 1; c < i.Count; c++)
{
if (i[c].OpCode == OpCodes.Call)
{
var op = i[c].Operand as MethodReference;
if (op != null
&& op.Name == TrampolineName)
{
foundXamlLoader = true;
break;
}
if (op != null
&& op.Name == "Load"
&& op.Parameters.Count == 1
&& op.Parameters[0].ParameterType.FullName == "System.Object"
&& op.DeclaringType.FullName == "Robust.Client.UserInterface.XAML.RobustXamlLoader")
{
if (MatchThisCall(i, c - 1))
{
i[c].Operand = trampoline;
foundXamlLoader = true;
}
}
}
}
}
if (!foundXamlLoader)
{
var ctors = classTypeDefinition.GetConstructors()
.Where(c => !c.IsStatic).ToList();
// We can inject xaml loader into default constructor
if (ctors.Count == 1 && ctors[0].Body.Instructions.Count(o=>o.OpCode != OpCodes.Nop) == 3)
{
var i = ctors[0].Body.Instructions;
var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret));
i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline));
i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
}
else
{
throw new InvalidProgramException(
$"No call to RobustXamlLoader.Load(this) call found anywhere in the type {classType.FullName} and type seems to have custom constructors.");
}
}
//add compiled build method
var compiledBuildMethod = typeSystem.GetTypeReference(builder).Resolve().Methods
.First(m => m.Name == buildName);
var parameterlessCtor = classTypeDefinition.GetConstructors()
.FirstOrDefault(c => c.IsPublic && !c.IsStatic && !c.HasParameters);
if (compiledBuildMethod != null && parameterlessCtor != null)
{
var i = loaderDispatcherMethod.Body.Instructions;
var nop = Instruction.Create(OpCodes.Nop);
i.Add(Instruction.Create(OpCodes.Ldarg_0));
i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
i.Add(Instruction.Create(OpCodes.Call, stringEquals));
i.Add(Instruction.Create(OpCodes.Brfalse, nop));
if (parameterlessCtor != null)
i.Add(Instruction.Create(OpCodes.Newobj, parameterlessCtor));
else
{
i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
}
i.Add(Instruction.Create(OpCodes.Ret));
i.Add(nop);
}
}
catch (Exception e)
{
engine.LogWarningEvent(new BuildWarningEventArgs("XAMLIL", "", res.Uri, 0, 0, 0, 0,
e.ToString(), "", "CompileRobustXaml"));
}
}
return true;
}
if (embrsc.Resources.Count(CheckXamlName) != 0)
{
if (!CompileGroup(embrsc))
return false;
}
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
return true;
}
private static bool CustomValueConverter(
AstTransformationContext context,
IXamlAstValueNode node,
IXamlType type,
out IXamlAstValueNode result)
{
if (!(node is XamlAstTextNode textNode))
{
result = null;
return false;
}
var text = textNode.Text;
var types = context.GetRobustTypes();
if (type.Equals(types.Vector2))
{
var foo = MathParsing.Single2.Parse(text);
if (!foo.Success)
throw new XamlLoadException($"Unable to parse \"{text}\" as a Vector2", node);
var (x, y) = foo.Value;
result = new RXamlSingleVecLikeConstAstNode(
node,
types.Vector2, types.Vector2ConstructorFull,
types.Single, new[] {x, y});
return true;
}
result = null;
return false;
}
public const string ContextNameScopeFieldName = "RobustNameScope";
private static void EmitNameScopeField(XamlLanguageTypeMappings xamlLanguage, CecilTypeSystem typeSystem, IXamlTypeBuilder<IXamlILEmitter> typeBuilder, IXamlILEmitter constructor)
{
var nameScopeType = typeSystem.FindType("Robust.Client.UserInterface.XAML.NameScope");
var field = typeBuilder.DefineField(nameScopeType,
ContextNameScopeFieldName, true, false);
constructor
.Ldarg_0()
.Newobj(nameScopeType.GetConstructor())
.Stfld(field);
}
}
interface IResource : IFileSource
{
string Uri { get; }
string Name { get; }
void Remove();
}
interface IResourceGroup
{
string Name { get; }
IEnumerable<IResource> Resources { get; }
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Robust.Client.NameGenerator
{
/// <summary>
/// Taken from https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/NameReferenceSyntaxReceiver.cs
/// </summary>
internal class NameReferenceSyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new List<ClassDeclarationSyntax>();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax &&
classDeclarationSyntax.AttributeLists.Count > 0)
CandidateClasses.Add(classDeclarationSyntax);
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Compile Link="XamlX\filename" Include="../XamlX/src/XamlX/**/*.cs" />
<Compile Remove="../XamlX/src/XamlX/**/SreTypeSystem.cs" />
<Compile Remove="../XamlX/src/XamlX/obj/**" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,287 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using XamlX.TypeSystem;
namespace Robust.Client.NameGenerator
{
/// <summary>
/// Taken from https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/Infrastructure/RoslynTypeSystem.cs
/// </summary>
public class RoslynTypeSystem : IXamlTypeSystem
{
private readonly List<IXamlAssembly> _assemblies = new List<IXamlAssembly>();
public RoslynTypeSystem(CSharpCompilation compilation)
{
_assemblies.Add(new RoslynAssembly(compilation.Assembly));
var assemblySymbols = compilation
.References
.Select(compilation.GetAssemblyOrModuleSymbol)
.OfType<IAssemblySymbol>()
.Select(assembly => new RoslynAssembly(assembly))
.ToList();
_assemblies.AddRange(assemblySymbols);
}
public IReadOnlyList<IXamlAssembly> Assemblies => _assemblies;
public IXamlAssembly FindAssembly(string substring) => _assemblies[0];
public IXamlType FindType(string name)
{
foreach (var assembly in _assemblies)
{
var type = assembly.FindType(name);
if (type != null)
return type;
}
return null;
}
public IXamlType FindType(string name, string assembly)
{
foreach (var assemblyInstance in _assemblies)
{
var type = assemblyInstance.FindType(name);
if (type != null)
return type;
}
return null;
}
}
public class RoslynAssembly : IXamlAssembly
{
private readonly IAssemblySymbol _symbol;
public RoslynAssembly(IAssemblySymbol symbol) => _symbol = symbol;
public bool Equals(IXamlAssembly other) =>
other is RoslynAssembly roslynAssembly &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynAssembly._symbol);
public string Name => _symbol.Name;
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes =>
_symbol.GetAttributes()
.Select(data => new RoslynAttribute(data, this))
.ToList();
public IXamlType FindType(string fullName)
{
var type = _symbol.GetTypeByMetadataName(fullName);
return type is null ? null : new RoslynType(type, this);
}
}
public class RoslynAttribute : IXamlCustomAttribute
{
private readonly AttributeData _data;
private readonly RoslynAssembly _assembly;
public RoslynAttribute(AttributeData data, RoslynAssembly assembly)
{
_data = data;
_assembly = assembly;
}
public bool Equals(IXamlCustomAttribute other) =>
other is RoslynAttribute attribute &&
_data == attribute._data;
public IXamlType Type => new RoslynType(_data.AttributeClass, _assembly);
public List<object> Parameters =>
_data.ConstructorArguments
.Select(argument => argument.Value)
.ToList();
public Dictionary<string, object> Properties =>
_data.NamedArguments.ToDictionary(
pair => pair.Key,
pair => pair.Value.Value);
}
public class RoslynType : IXamlType
{
private static readonly SymbolDisplayFormat SymbolDisplayFormat = new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters |
SymbolDisplayGenericsOptions.IncludeTypeConstraints |
SymbolDisplayGenericsOptions.IncludeVariance);
private readonly RoslynAssembly _assembly;
private readonly INamedTypeSymbol _symbol;
public RoslynType(INamedTypeSymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlType other) =>
other is RoslynType roslynType &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynType._symbol);
public object Id => _symbol;
public string Name => _symbol.Name;
public string Namespace => _symbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat);
public string FullName => $"{Namespace}.{Name}";
public IXamlAssembly Assembly => _assembly;
public IReadOnlyList<IXamlProperty> Properties =>
_symbol.GetMembers()
.Where(member => member.Kind == SymbolKind.Property)
.OfType<IPropertySymbol>()
.Select(property => new RoslynProperty(property, _assembly))
.ToList();
public IReadOnlyList<IXamlEventInfo> Events { get; } = new List<IXamlEventInfo>();
public IReadOnlyList<IXamlField> Fields { get; } = new List<IXamlField>();
public IReadOnlyList<IXamlMethod> Methods { get; } = new List<IXamlMethod>();
public IReadOnlyList<IXamlConstructor> Constructors =>
_symbol.Constructors
.Select(method => new RoslynConstructor(method, _assembly))
.ToList();
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
public IReadOnlyList<IXamlType> GenericArguments { get; } = new List<IXamlType>();
public bool IsAssignableFrom(IXamlType type) => type == this;
public IXamlType MakeGenericType(IReadOnlyList<IXamlType> typeArguments) => this;
public IXamlType GenericTypeDefinition => this;
public bool IsArray => false;
public IXamlType ArrayElementType { get; } = null;
public IXamlType MakeArrayType(int dimensions) => null;
public IXamlType BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly);
public bool IsValueType { get; } = false;
public bool IsEnum { get; } = false;
public IReadOnlyList<IXamlType> Interfaces =>
_symbol.AllInterfaces
.Select(abstraction => new RoslynType(abstraction, _assembly))
.ToList();
public bool IsInterface => _symbol.IsAbstract;
public IXamlType GetEnumUnderlyingType() => null;
public IReadOnlyList<IXamlType> GenericParameters { get; } = new List<IXamlType>();
}
public class RoslynConstructor : IXamlConstructor
{
private readonly IMethodSymbol _symbol;
private readonly RoslynAssembly _assembly;
public RoslynConstructor(IMethodSymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlConstructor other) =>
other is RoslynConstructor roslynConstructor &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynConstructor._symbol);
public bool IsPublic => true;
public bool IsStatic => false;
public IReadOnlyList<IXamlType> Parameters =>
_symbol.Parameters
.Select(parameter => parameter.Type)
.OfType<INamedTypeSymbol>()
.Select(type => new RoslynType(type, _assembly))
.ToList();
}
public class RoslynProperty : IXamlProperty
{
private readonly IPropertySymbol _symbol;
private readonly RoslynAssembly _assembly;
public RoslynProperty(IPropertySymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlProperty other) =>
other is RoslynProperty roslynProperty &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynProperty._symbol);
public string Name => _symbol.Name;
public IXamlType PropertyType =>
_symbol.Type is INamedTypeSymbol namedTypeSymbol
? new RoslynType(namedTypeSymbol, _assembly)
: null;
public IXamlMethod Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly);
public IXamlMethod Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly);
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
public IReadOnlyList<IXamlType> IndexerParameters { get; } = new List<IXamlType>();
}
public class RoslynMethod : IXamlMethod
{
private readonly IMethodSymbol _symbol;
private readonly RoslynAssembly _assembly;
public RoslynMethod(IMethodSymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlMethod other) =>
other is RoslynMethod roslynMethod &&
SymbolEqualityComparer.Default.Equals(roslynMethod._symbol, _symbol);
public string Name => _symbol.Name;
public bool IsPublic => true;
public bool IsStatic => false;
public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly);
public IReadOnlyList<IXamlType> Parameters =>
_symbol.Parameters.Select(parameter => parameter.Type)
.OfType<INamedTypeSymbol>()
.Select(type => new RoslynType(type, _assembly))
.ToList();
public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType, _assembly);
public IXamlMethod MakeGenericMethod(IReadOnlyList<IXamlType> typeArguments) => null;
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
}
}

View File

@@ -0,0 +1,262 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Parsers;
using XamlX.Transform;
using XamlX.Transform.Transformers;
using XamlX.TypeSystem;
namespace Robust.Client.NameGenerator
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/NameReferenceGenerator.cs
/// Adjusted for our UI-Framework & needs.
/// </summary>
[Generator]
public class XamlUiPartialClassGenerator : ISourceGenerator
{
private const string AttributeName = "Robust.Client.AutoGenerated.GenerateTypedNameReferencesAttribute";
private const string AttributeFile = "GenerateTypedNameReferencesAttribute";
private const string AttributeCode = @"// <auto-generated />
using System;
namespace Robust.Client.AutoGenerated
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class GenerateTypedNameReferencesAttribute : Attribute { }
}
";
class NameVisitor : IXamlAstVisitor
{
private List<(string name, string type)> _names = new List<(string name, string type)>();
public static List<(string name, string type)> GetNames(IXamlAstNode node)
{
var visitor = new NameVisitor();
node.Visit(visitor);
return visitor._names;
}
private bool IsControl(IXamlType type) => type.FullName != "System.Object" &&
(type.FullName == "Robust.Client.UserInterface.Control" ||
IsControl(type.BaseType));
public IXamlAstNode Visit(IXamlAstNode node)
{
if (node is XamlAstObjectNode objectNode)
{
var clrtype = objectNode.Type.GetClrType();
var isControl = IsControl(clrtype);
//clrtype.Interfaces.Any(i =>
//i.IsInterface && i.FullName == "Robust.Client.UserInterface.IControl");
if (!isControl)
return node;
foreach (var child in objectNode.Children)
{
if (child is XamlAstXamlPropertyValueNode propertyValueNode &&
propertyValueNode.Property is XamlAstNamePropertyReference namedProperty &&
namedProperty.Name == "Name" &&
propertyValueNode.Values.Count > 0 &&
propertyValueNode.Values[0] is XamlAstTextNode text)
{
var reg = (text.Text, $@"{clrtype.Namespace}.{clrtype.Name}");
if (!_names.Contains(reg))
{
_names.Add(reg);
}
}
}
}
return node;
}
public void Push(IXamlAstNode node)
{
}
public void Pop()
{
}
}
private static string GenerateSourceCode(
INamedTypeSymbol classSymbol,
string xamlFile,
CSharpCompilation comp)
{
var className = classSymbol.Name;
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString();
var parsed = XDocumentXamlParser.Parse(xamlFile);
var typeSystem = new RoslynTypeSystem(comp);
var compiler =
new XamlILCompiler(
new TransformerConfiguration(typeSystem, typeSystem.Assemblies[0],
new XamlLanguageTypeMappings(typeSystem)
{
XmlnsAttributes = {typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute")}
}),
new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>(), false);
compiler.Transformers.Add(new TypeReferenceResolver());
compiler.Transform(parsed);
var initialRoot = (XamlAstObjectNode) parsed.Root;
var names = NameVisitor.GetNames(initialRoot);
var fieldAccess = classSymbol.IsSealed ? "private" : "protected";
//var names = NameVisitor.GetNames((XamlAstObjectNode)XDocumentXamlParser.Parse(xamlFile).Root);
var namedControls = names.Select(info => " " +
$"{fieldAccess} global::{info.type} {info.name} => " +
$"this.FindControl<global::{info.type}>(\"{info.name}\");");
return $@"// <auto-generated />
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace {nameSpace}
{{
partial class {className}
{{
{string.Join("\n", namedControls)}
}}
}}
";
}
public void Execute(GeneratorExecutionContext context)
{
var comp = (CSharpCompilation) context.Compilation;
if (comp.GetTypeByMetadataName(AttributeName) == null)
context.AddSource(AttributeFile, SourceText.From(AttributeCode, Encoding.UTF8));
if (!(context.SyntaxReceiver is NameReferenceSyntaxReceiver receiver))
{
return;
}
var symbols = UnpackAnnotatedTypes(context, comp, receiver);
if (symbols == null)
return;
foreach (var typeSymbol in symbols)
{
var xamlFileName = $"{typeSymbol.Name}.xaml";
var relevantXamlFile = context.AdditionalFiles.FirstOrDefault(t => t.Path.EndsWith(xamlFileName));
if (relevantXamlFile == null)
{
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"RXN0001",
$"Unable to discover the relevant Robust XAML file for {typeSymbol}.",
"Unable to discover the relevant Robust XAML file " +
$"expected at {xamlFileName}",
"Usage",
DiagnosticSeverity.Error,
true),
typeSymbol.Locations[0]));
continue;
}
var txt = relevantXamlFile.GetText()?.ToString();
if (txt == null)
{
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"RXN0002",
$"Unexpected empty Xaml-File was found at {xamlFileName}",
"Expected Content due to a Class with the same name being annotated with [GenerateTypedNameReferences].",
"Usage",
DiagnosticSeverity.Error,
true),
Location.Create(xamlFileName, new TextSpan(0, 0),
new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 0)))));
continue;
}
try
{
var sourceCode = GenerateSourceCode(typeSymbol, txt, comp);
context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8));
}
catch (Exception e)
{
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"AXN0003",
"Unhandled exception occured while generating typed Name references.",
$"Unhandled exception occured while generating typed Name references: {e}",
"Usage",
DiagnosticSeverity.Error,
true),
typeSymbol.Locations[0]));
continue;
}
}
}
private IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(in GeneratorExecutionContext context,
CSharpCompilation comp, NameReferenceSyntaxReceiver receiver)
{
var options = (CSharpParseOptions) comp.SyntaxTrees[0].Options;
var compilation =
comp.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(AttributeCode, Encoding.UTF8), options));
var symbols = new List<INamedTypeSymbol>();
var attributeSymbol = compilation.GetTypeByMetadataName(AttributeName);
foreach (var candidateClass in receiver.CandidateClasses)
{
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
var typeSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(candidateClass);
var relevantAttribute = typeSymbol.GetAttributes().FirstOrDefault(attr =>
attr.AttributeClass != null &&
attr.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
if (relevantAttribute == null)
{
continue;
}
var isPartial = candidateClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
if (isPartial)
{
symbols.Add(typeSymbol);
}
else
{
var missingPartialKeywordMessage =
$"The type {typeSymbol.Name} should be declared with the 'partial' keyword " +
"as it is annotated with the [GenerateTypedNameReferences] attribute.";
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"RXN0004",
missingPartialKeywordMessage,
missingPartialKeywordMessage,
"Usage",
DiagnosticSeverity.Error,
true),
Location.None));
return null;
}
}
return symbols;
}
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new NameReferenceSyntaxReceiver());
}
}
}

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using Robust.Client.GameObjects.Components.Animations;
using Robust.Client.GameObjects;
namespace Robust.Client.Animations
{

View File

@@ -1,7 +1,7 @@
using System;
using JetBrains.Annotations;
using Robust.Shared.Animations;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
namespace Robust.Client.Animations
{

View File

@@ -1,8 +1,7 @@
using System;
using Robust.Client.Animations;
using Robust.Shared.Animations;
namespace Content.Client.Animations
namespace Robust.Client.Animations
{
public class AnimationTrackControlProperty : AnimationTrackProperty
{

View File

@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
using Robust.Shared.GameObjects;
namespace Robust.Client.Animations
{

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Utility;
namespace Robust.Client.Animations

View File

@@ -1,6 +1,5 @@
using System;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Clyde;
namespace Robust.Client.Audio
{

View File

@@ -4,16 +4,15 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using NFluidsynth;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.Interfaces.Resources;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
using Logger = Robust.Shared.Log.Logger;
@@ -104,7 +103,7 @@ namespace Robust.Client.Audio.Midi
private const string OsxSoundfont =
"/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls";
private const string FallbackSoundfont = "/Resources/Midi/fallback.sf2";
private const string FallbackSoundfont = "/Midi/fallback.sf2";
private readonly ResourceLoaderCallbacks _soundfontLoaderCallbacks = new();
@@ -207,13 +206,10 @@ namespace Robust.Client.Audio.Midi
var renderer = new MidiRenderer(_settings!, soundfontLoader);
foreach (var file in _resourceManager.ContentFindFiles(new ResourcePath("/Audio/MidiCustom/")))
foreach (var file in _resourceManager.ContentFindFiles(("/Audio/MidiCustom/")))
{
if (file.Extension != "sf2" && file.Extension != "dls") continue;
if (_resourceManager.TryGetDiskFilePath(file, out var path))
{
renderer.LoadSoundfont(path);
}
renderer.LoadSoundfont(file.ToString());
}
// Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont.
@@ -382,10 +378,18 @@ namespace Robust.Client.Audio.Midi
public override IntPtr Open(string filename)
{
Stream? stream;
if (filename.StartsWith("/Resources/"))
if (string.IsNullOrEmpty(filename))
{
if (!IoCManager.Resolve<IResourceCache>().TryContentFileRead(filename.Substring(10), out stream))
return IntPtr.Zero;
}
Stream? stream;
var resourceCache = IoCManager.Resolve<IResourceCache>();
var resourcePath = new ResourcePath(filename);
if (resourcePath.IsRooted && resourceCache.ContentFileExists(filename))
{
if (!resourceCache.TryContentFileRead(filename, out stream))
return IntPtr.Zero;
}
else if (File.Exists(filename))

View File

@@ -1,11 +1,9 @@
using System;
using System.IO;
using System.Runtime.Intrinsics.X86;
using NFluidsynth;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Graphics;
using Robust.Shared.Asynchronous;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -72,9 +70,9 @@ namespace Robust.Client.Audio.Midi
int PlayerTotalTick { get; }
/// <summary>
/// Gets the current tick of the MIDI player.
/// Gets or sets (seeks) the current tick of the MIDI player.
/// </summary>
int PlayerTick { get; }
int PlayerTick { get; set; }
/// <summary>
/// Gets the current tick of the sequencer.
@@ -235,7 +233,16 @@ namespace Robust.Client.Audio.Midi
public bool DisableProgramChangeEvent { get; set; } = true;
public int PlayerTotalTick => _player?.GetTotalTicks ?? 0;
public int PlayerTick => _player?.CurrentTick ?? 0;
public int PlayerTick
{
get => _player?.CurrentTick ?? 0;
set
{
lock (_playerStateLock)
_player?.Seek(Math.Max(Math.Min(value, PlayerTotalTick), 0));
}
}
public uint SequencerTick => _sequencer?.Tick ?? 0;
public double SequencerTimeScale => _sequencer?.TimeScale ?? 0;
@@ -310,9 +317,8 @@ namespace Robust.Client.Audio.Midi
lock (_playerStateLock)
{
if (_player == null)
_player = new NFluidsynth.Player(_synth);
_player.Stop();
_player?.Dispose();
_player = new NFluidsynth.Player(_synth);
_player.AddMem(buffer);
_player.SetPlaybackCallback(MidiPlayerEventHandler);
_player.Play();
@@ -351,10 +357,7 @@ namespace Robust.Client.Audio.Midi
public void StopAllNotes()
{
for (var i = 0; i < 16; i++)
{
_synth.AllNotesOff(i);
}
_synth.AllNotesOff(-1);
}
public void LoadSoundfont(string filename, bool resetPresets = false)
@@ -475,12 +478,6 @@ namespace Robust.Client.Audio.Midi
lock(_playerStateLock)
switch (midiEvent.Type)
{
// Sometimes MIDI files spam these for no good reason and I can't find any info on what they are.
case 1:
case 5:
case 81:
break;
// Note On 0x80
case 144:
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
@@ -503,8 +500,10 @@ namespace Robust.Client.Audio.Midi
// Program Change - 0xC0
case 192:
if(!DisableProgramChangeEvent)
if (!DisableProgramChangeEvent)
_synth.ProgramChange(midiEvent.Channel, midiEvent.Program);
else
return;
break;
// Channel Pressure - 0xD0
@@ -517,13 +516,17 @@ namespace Robust.Client.Audio.Midi
_synth.PitchBend(midiEvent.Channel, midiEvent.Pitch);
break;
// Sometimes MIDI files spam these for no good reason and I can't find any info on what they are.
case 1:
case 5:
case 81:
// System Messages - 0xF0
case 240:
break;
return;
default:
_midiSawmill.Warning("Unhandled midi event of type {0}", midiEvent.Type, midiEvent);
break;
return;
}
}
catch (FluidSynthInteropException)

View File

@@ -1,21 +1,18 @@
using System;
using System;
using System.Net;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Debugging;
using Robust.Client.Interfaces.GameObjects;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Utility;
using Robust.Client.Debugging;
using Robust.Client.GameObjects;
using Robust.Client.GameStates;
using Robust.Client.Player;
using Robust.Client.Utility;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Client
@@ -25,7 +22,7 @@ namespace Robust.Client
{
[Dependency] private readonly IClientNetManager _net = default!;
[Dependency] private readonly IPlayerManager _playMan = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly INetConfigurationManager _configManager = default!;
[Dependency] private readonly IClientEntityManager _entityManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IDiscordRichPresence _discord = default!;
@@ -50,18 +47,28 @@ namespace Robust.Client
/// <inheritdoc />
public void Initialize()
{
_net.RegisterNetMessage<MsgServerInfo>(MsgServerInfo.NAME, HandleServerInfo);
_net.RegisterNetMessage<MsgSetTickRate>(MsgSetTickRate.NAME, HandleSetTickRate);
_net.RegisterNetMessage<MsgServerInfoReq>(MsgServerInfoReq.NAME);
_net.Connected += OnConnected;
_net.ConnectFailed += OnConnectFailed;
_net.Disconnect += OnNetDisconnect;
_configManager.OnValueChanged(CVars.NetTickrate, TickRateChanged, invokeImmediately: true);
_playMan.Initialize();
_debugDrawMan.Initialize();
Reset();
}
private void TickRateChanged(int tickrate)
{
if (GameInfo != null)
{
GameInfo.TickRate = (byte) tickrate;
}
_timing.TickRate = (byte) tickrate;
Logger.InfoS("client", $"Tickrate changed to: {tickrate} on tick {_timing.CurTick}");
}
/// <inheritdoc />
public void ConnectToServer(DnsEndPoint endPoint)
{
@@ -98,9 +105,39 @@ namespace Robust.Client
private void OnConnected(object? sender, NetChannelArgs args)
{
// request base info about the server
var msgInfo = _net.CreateNetMessage<MsgServerInfoReq>();
_net.ClientSendMessage(msgInfo);
_configManager.SyncWithServer();
_configManager.ReceivedInitialNwVars += OnReceivedClientData;
}
private void OnReceivedClientData(object? sender, EventArgs e)
{
_configManager.ReceivedInitialNwVars -= OnReceivedClientData;
var info = GameInfo;
var serverName = _configManager.GetCVar<string>("game.hostname");
if (info == null)
{
GameInfo = info = new ServerInfo(serverName);
}
else
{
info.ServerName = serverName;
}
var maxPlayers = _configManager.GetCVar<int>("game.maxplayers");
info.ServerMaxPlayers = maxPlayers;
var userName = _net.ServerChannel!.UserName;
var userId = _net.ServerChannel.UserId;
_discord.Update(info.ServerName, userName, info.ServerMaxPlayers.ToString());
// start up player management
_playMan.Startup(_net.ServerChannel!);
_playMan.LocalPlayer!.UserId = userId;
_playMan.LocalPlayer.Name = userName;
_playMan.LocalPlayer.StatusChanged += OnLocalStatusChanged;
}
/// <summary>
@@ -135,6 +172,7 @@ namespace Robust.Client
private void Reset()
{
_configManager.ClearReceivedInitialNwVars();
OnRunLevelChanged(ClientRunLevel.Initialize);
}
@@ -152,6 +190,7 @@ namespace Robust.Client
LastDisconnectReason = args.Reason;
IoCManager.Resolve<INetConfigurationManager>().FlushMessages();
_gameStates.Reset();
_playMan.Shutdown();
_entityManager.Shutdown();
@@ -160,42 +199,6 @@ namespace Robust.Client
Reset();
}
private void HandleServerInfo(MsgServerInfo msg)
{
var info = GameInfo;
if (info == null)
{
GameInfo = info = new ServerInfo(msg.ServerName);
}
else
{
info.ServerName = msg.ServerName;
}
info.ServerMaxPlayers = msg.ServerMaxPlayers;
info.TickRate = msg.TickRate;
_timing.TickRate = msg.TickRate;
Logger.InfoS("client", $"Tickrate changed to: {msg.TickRate}");
var userName = msg.MsgChannel.UserName;
var userId = msg.MsgChannel.UserId;
_discord.Update(info.ServerName, userName, info.ServerMaxPlayers.ToString());
// start up player management
_playMan.Startup(_net.ServerChannel!);
_playMan.LocalPlayer!.UserId = userId;
_playMan.LocalPlayer.Name = userName;
_playMan.LocalPlayer.StatusChanged += OnLocalStatusChanged;
}
private void HandleSetTickRate(MsgSetTickRate message)
{
_timing.TickRate = message.NewTickRate;
Logger.InfoS("client", $"Tickrate changed to: {message.NewTickRate} on tick {_timing.CurTick}");
}
private void OnLocalStatusChanged(object? obj, StatusEventArgs eventArgs)
{
// player finished fully connecting to the server.

View File

@@ -1,30 +1,12 @@
using System;
using System;
using Robust.Client.Audio.Midi;
using Robust.Client.Console;
using Robust.Client.Debugging;
using Robust.Client.GameObjects;
using Robust.Client.GameStates;
using Robust.Client.Graphics;
using Robust.Client.Graphics.ClientEye;
using Robust.Client.Graphics.Clyde;
using Robust.Client.Graphics.Lighting;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Input;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Debugging;
using Robust.Client.Interfaces.GameObjects;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Lighting;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.Map;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Interfaces.Utility;
using Robust.Client.Map;
using Robust.Client.Placement;
using Robust.Client.Player;
@@ -35,15 +17,14 @@ using Robust.Client.UserInterface;
using Robust.Client.Utility;
using Robust.Client.ViewVariables;
using Robust.Shared;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Resources;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
namespace Robust.Client
{
@@ -78,10 +59,12 @@ namespace Robust.Client
IoCManager.Register<IDebugDrawingManager, DebugDrawingManager>();
IoCManager.Register<ILightManager, LightManager>();
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
IoCManager.Register<IClientConsole, ClientConsole>();
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
IoCManager.Register<IFontManager, FontManager>();
IoCManager.Register<IFontManagerInternal, FontManager>();
IoCManager.Register<IMidiManager, MidiManager>();
IoCManager.Register<IAuthManager, AuthManager>();
switch (mode)
{
case GameController.DisplayMode.Headless:
@@ -115,6 +98,7 @@ namespace Robust.Client
IoCManager.Register<IViewVariablesManagerInternal, ViewVariablesManager>();
IoCManager.Register<IClientConGroupController, ClientConGroupController>();
IoCManager.Register<IScriptClient, ScriptClient>();
//IoCManager.Register<IXamlCompiler, XamlCompiler>();
}
}
}

View File

@@ -1,243 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Client.Interfaces.Console;
using Robust.Client.Log;
using Robust.Client.Utility;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public class AddStringArgs : EventArgs
{
public string Text { get; }
public Color Color { get; }
public AddStringArgs(string text, Color color)
{
Text = text;
Color = color;
}
}
public class AddFormattedMessageArgs : EventArgs
{
public readonly FormattedMessage Message;
public AddFormattedMessageArgs(FormattedMessage message)
{
Message = message;
}
}
internal sealed class ClientConsole : IClientConsole, IDebugConsole
{
private static readonly Color MsgColor = new Color(65, 105, 225);
[Dependency] private readonly IClientNetManager _network = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly ILogManager logManager = default!;
private readonly Dictionary<string, IConsoleCommand> _commands = new Dictionary<string, IConsoleCommand>();
private bool _requestedCommands;
/// <inheritdoc />
public void Initialize()
{
_network.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
_network.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
_network.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME);
Reset();
logManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
/// <inheritdoc />
public void Reset()
{
_commands.Clear();
_requestedCommands = false;
_network.Connected += OnNetworkConnected;
InitializeCommands();
SendServerCommandRequest();
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
{
SendServerCommandRequest();
}
/// <inheritdoc />
public void Dispose()
{
// We don't have anything to dispose.
}
public IReadOnlyDictionary<string, IConsoleCommand> Commands => _commands;
public void AddLine(string text, Color color)
{
AddString?.Invoke(this, new AddStringArgs(text, color));
}
public void AddLine(string text)
{
AddLine(text, Color.White);
}
public void Clear()
{
ClearText?.Invoke(this, EventArgs.Empty);
}
public event EventHandler<AddStringArgs>? AddString;
public event EventHandler? ClearText;
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
private void HandleConCmdAck(MsgConCmdAck msg)
{
AddLine("< " + msg.Text, MsgColor);
}
private void HandleConCmdReg(MsgConCmdReg msg)
{
foreach (var cmd in msg.Commands)
{
var commandName = cmd.Name;
// Do not do duplicate commands.
if (_commands.ContainsKey(commandName))
{
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
continue;
}
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
_commands[commandName] = command;
}
}
/// <summary>
/// Processes commands (chat messages starting with /)
/// </summary>
/// <param name="text">input text</param>
public void ProcessCommand(string text)
{
if (string.IsNullOrWhiteSpace(text))
return;
// echo the command locally
AddLine("> " + text, Color.Lime);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
CommandParsing.ParseArguments(text, args);
var commandname = args[0];
var forward = true;
if (_commands.ContainsKey(commandname))
{
var command = _commands[commandname];
args.RemoveAt(0);
forward = command.Execute(this, args.ToArray());
}
else if (!_network.IsConnected)
{
AddLine("Unknown command: " + commandname, Color.Red);
return;
}
if (forward)
SendServerConsoleCommand(text);
}
/// <summary>
/// Locates and registeres all local commands.
/// </summary>
private void InitializeCommands()
{
foreach (var t in _reflectionManager.GetAllChildren<IConsoleCommand>())
{
var instance = (IConsoleCommand)Activator.CreateInstance(t)!;
if (_commands.ContainsKey(instance.Command))
throw new InvalidOperationException($"Command already registered: {instance.Command}");
_commands[instance.Command] = instance;
}
}
/// <summary>
/// Requests remote commands from server.
/// </summary>
public void SendServerCommandRequest()
{
if (_requestedCommands)
return;
if (!_network.IsConnected)
return;
var msg = _network.CreateNetMessage<MsgConCmdReg>();
_network.ClientSendMessage(msg);
_requestedCommands = true;
}
/// <summary>
/// Sends a command directly to the server.
/// </summary>
private void SendServerConsoleCommand(string text)
{
if (_network == null || !_network.IsConnected)
return;
var msg = _network.CreateNetMessage<MsgConCmd>();
msg.Text = text;
_network.ClientSendMessage(msg);
}
public void AddFormattedLine(FormattedMessage message)
{
// Why the hell does this class implement IDebugConsole.
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
}
}
/// <summary>
/// These dummies are made purely so list and help can list server-side commands.
/// </summary>
[Reflect(false)]
internal class ServerDummyCommand : IConsoleCommand
{
internal ServerDummyCommand(string command, string help, string description)
{
Command = command;
Help = help;
Description = description;
}
public string Command { get; }
public string Help { get; }
public string Description { get; }
// Always forward to server.
public bool Execute(IDebugConsole console, params string[] args)
{
return true;
}
}
}

View File

@@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using Robust.Client.Log;
using Robust.Shared.Console;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public class AddStringArgs : EventArgs
{
public string Text { get; }
public bool Local { get; }
public bool Error { get; }
public AddStringArgs(string text, bool local, bool error)
{
Text = text;
Local = local;
Error = error;
}
}
public class AddFormattedMessageArgs : EventArgs
{
public readonly FormattedMessage Message;
public AddFormattedMessageArgs(FormattedMessage message)
{
Message = message;
}
}
/// <inheritdoc cref="IClientConsoleHost" />
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
{
private bool _requestedCommands;
/// <inheritdoc />
public void Initialize()
{
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
Reset();
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
private void ProcessCommand(MsgConCmd message)
{
string? text = message.Text;
LogManager.GetSawmill(SawmillName).Info($"SERVER:{text}");
ExecuteCommand(null, text);
}
/// <inheritdoc />
public void Reset()
{
AvailableCommands.Clear();
_requestedCommands = false;
NetManager.Connected += OnNetworkConnected;
LoadConsoleCommands();
SendServerCommandRequest();
}
/// <inheritdoc />
public event EventHandler<AddStringArgs>? AddString;
/// <inheritdoc />
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
/// <inheritdoc />
public void AddFormattedLine(FormattedMessage message)
{
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
}
/// <inheritdoc />
public override void WriteError(ICommonSession? session, string text)
{
OutputText(text, true, true);
}
/// <inheritdoc />
public override void ExecuteCommand(ICommonSession? session, string command)
{
if (string.IsNullOrWhiteSpace(command))
return;
// echo the command locally
WriteError(null, "> " + command);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
CommandParsing.ParseArguments(command, args);
var commandName = args[0];
if (AvailableCommands.ContainsKey(commandName))
{
var command1 = AvailableCommands[commandName];
args.RemoveAt(0);
command1.Execute(new ConsoleShell(this, null), command, args.ToArray());
}
else
WriteError(null, "Unknown command: " + commandName);
}
/// <inheritdoc />
public override void RemoteExecuteCommand(ICommonSession? session, string command)
{
if (!NetManager.IsConnected) // we don't care about session on client
return;
var msg = NetManager.CreateNetMessage<MsgConCmd>();
msg.Text = command;
NetManager.ClientSendMessage(msg);
}
/// <inheritdoc />
public override void WriteLine(ICommonSession? session, string text)
{
OutputText(text, true, false);
}
/// <inheritdoc />
public void Dispose()
{
// We don't have anything to dispose.
}
private void OutputText(string text, bool local, bool error)
{
AddString?.Invoke(this, new AddStringArgs(text, local, error));
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
{
SendServerCommandRequest();
}
private void HandleConCmdAck(MsgConCmdAck msg)
{
OutputText("< " + msg.Text, false, msg.Error);
}
private void HandleConCmdReg(MsgConCmdReg msg)
{
foreach (var cmd in msg.Commands)
{
string? commandName = cmd.Name;
// Do not do duplicate commands.
if (AvailableCommands.ContainsKey(commandName))
{
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
continue;
}
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
AvailableCommands[commandName] = command;
}
}
/// <summary>
/// Requests remote commands from server.
/// </summary>
private void SendServerCommandRequest()
{
if (_requestedCommands)
return;
if (!NetManager.IsConnected)
return;
var msg = NetManager.CreateNetMessage<MsgConCmdReg>();
NetManager.ClientSendMessage(msg);
_requestedCommands = true;
}
}
/// <summary>
/// These dummies are made purely so list and help can list server-side commands.
/// </summary>
[Reflect(false)]
internal class ServerDummyCommand : IConsoleCommand
{
internal ServerDummyCommand(string command, string help, string description)
{
Command = command;
Help = help;
Description = description;
}
public string Command { get; }
public string Description { get; }
public string Help { get; }
// Always forward to server.
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
shell.RemoteExecuteCommand(argStr);
}
}
}

View File

@@ -1,24 +1,23 @@
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using JetBrains.Annotations;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
namespace Robust.Client.Console.Commands
{
[UsedImplicitly]
internal sealed class AddCompCommand : IClientCommand
internal sealed class AddCompCommand : IConsoleCommand
{
public string Command => "addcomp";
public string Description => "Adds a component to an entity";
public string Help => "addcomp <uid> <componentName>";
public string Command => "addcompc";
public string Description => "Adds a component to an entity on the client";
public string Help => "addcompc <uid> <componentName>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Wrong number of arguments");
shell.WriteLine("Wrong number of arguments");
return;
}
@@ -38,16 +37,18 @@ namespace Robust.Server.Console.Commands
}
}
internal sealed class RemoveCompCommand : IClientCommand
[UsedImplicitly]
internal sealed class RemoveCompCommand : IConsoleCommand
{
public string Command => "rmcomp";
public string Command => "rmcompc";
public string Description => "Removes a component from an entity.";
public string Help => "rmcomp <uid> <componentName>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public string Help => "rmcompc <uid> <componentName>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Wrong number of arguments");
shell.WriteLine("Wrong number of arguments");
return;
}

View File

@@ -1,7 +1,7 @@
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Player;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Client.Console.Commands
@@ -13,18 +13,17 @@ namespace Robust.Client.Console.Commands
public string Description => "Spawns a client-side entity with specific type at your feet.";
public string Help => "cspawn <entity type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
if (player?.ControlledEntity == null)
{
console.AddLine("You don't have an attached entity.");
return false;
shell.WriteLine("You don't have an attached entity.");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
entityManager.SpawnEntity(args[0], player.ControlledEntity.Transform.Coordinates);
return false;
}
}
}

View File

@@ -1,23 +1,21 @@
using System;
using System.Linq;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Configuration;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands
{
[UsedImplicitly]
internal sealed class CVarCommand : SharedCVarCommand, IConsoleCommand
{
public bool Execute(IDebugConsole console, params string[] args)
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
console.AddLine("Must provide exactly one or two arguments.", Color.Red);
return false;
shell.WriteError("Must provide exactly one or two arguments.");
return;
}
var configManager = IoCManager.Resolve<IConfigurationManager>();
@@ -26,21 +24,21 @@ namespace Robust.Client.Console.Commands
if (name == "?")
{
var cvars = configManager.GetRegisteredCVars().OrderBy(c => c);
console.AddLine(string.Join("\n", cvars));
return false;
shell.WriteLine(string.Join("\n", cvars));
return;
}
if (!configManager.IsCVarRegistered(name))
{
console.AddLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.", Color.Red);
return false;
shell.WriteError($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.");
return;
}
if (args.Length == 1)
{
// Read CVar
var value = configManager.GetCVar<object>(name);
console.AddLine(value.ToString() ?? "");
shell.WriteLine(value.ToString() ?? "");
}
else
{
@@ -54,11 +52,9 @@ namespace Robust.Client.Console.Commands
}
catch (FormatException)
{
console.AddLine($"Input value is in incorrect format for type {type}");
shell.WriteLine($"Input value is in incorrect format for type {type}");
}
}
return false;
}
}
@@ -69,10 +65,9 @@ namespace Robust.Client.Console.Commands
public string Description => "Saves the client configuration to the config file";
public string Help => "saveconfig";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IConfigurationManager>().SaveToFile();
return false;
}
}

View File

@@ -1,13 +1,8 @@
// This file is for commands that do something to the console itself.
// This file is for commands that do something to the console itself.
// Not some generic console command type.
// Couldn't think of a better name sorry.
using System;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands
{
@@ -17,10 +12,9 @@ namespace Robust.Client.Console.Commands
public string Help => "Clears the debug console of all messages.";
public string Description => "Clears the console.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
console.Clear();
return false;
shell.Clear();
}
}
@@ -30,15 +24,12 @@ namespace Robust.Client.Console.Commands
public string Help => "Fills the console with some nonsense for debugging.";
public string Description => "Fill up the console for debugging.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
Color[] colors = { Color.Green, Color.Blue, Color.Red };
var random = IoCManager.Resolve<IRobustRandom>();
for (int x = 0; x < 50; x++)
{
console.AddLine("filling...", colors[random.Next(0, colors.Length)]);
shell.WriteLine("filling...");
}
return false;
}
}
}

View File

@@ -6,37 +6,25 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using Robust.Client.Input;
using System.Threading;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Debugging;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Lighting;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Debugging;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.ResourceManagement.ResourceTypes;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Asynchronous;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Resources;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -49,16 +37,14 @@ namespace Robust.Client.Console.Commands
public string Help => "Dump entity list";
public string Description => "Dumps entity list of UIDs and prototype.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
foreach (var e in entityManager.GetEntities().OrderBy(e => e.Uid))
{
console.AddLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.", Color.White);
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.");
}
return false;
}
}
@@ -68,12 +54,12 @@ namespace Robust.Client.Console.Commands
public string Help => "Usage: getcomponentregistration <componentName>";
public string Description => "Gets component registration information";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var componentFactory = IoCManager.Resolve<IComponentFactory>();
@@ -94,19 +80,17 @@ namespace Robust.Client.Console.Commands
message.Append($", NSE: {registration.NetworkSynchronizeExistence}, references:");
console.AddLine(message.ToString(), Color.White);
shell.WriteLine(message.ToString());
foreach (var type in registration.References)
{
console.AddLine($" {type}", Color.White);
shell.WriteLine($" {type}");
}
}
catch (UnknownComponentException)
{
console.AddLine($"No registration found for '{args[0]}'", Color.Red);
shell.WriteError($"No registration found for '{args[0]}'");
}
return false;
}
}
@@ -119,14 +103,14 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles a debug monitor in the F3 menu.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var monitor = IoCManager.Resolve<IUserInterfaceManager>().DebugMonitors;
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
switch (args[0])
@@ -159,11 +143,9 @@ namespace Robust.Client.Console.Commands
monitor.ShowInput ^= true;
break;
default:
console.AddLine($"Invalid key: {args[0]}");
shell.WriteLine($"Invalid key: {args[0]}");
break;
}
return false;
}
}
@@ -173,7 +155,7 @@ namespace Robust.Client.Console.Commands
public string Help => "Throws an exception";
public string Description => "Throws an exception";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
throw new InvalidOperationException("Fuck");
}
@@ -185,11 +167,10 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Enables debug drawing over all bounding boxes in the game, showing their size.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IDebugDrawing>();
mgr.DebugColliders = !mgr.DebugColliders;
return false;
}
}
@@ -199,11 +180,10 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Enables debug drawing over all entity positions in the game.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IDebugDrawing>();
mgr.DebugPositions = !mgr.DebugPositions;
return false;
}
}
@@ -213,25 +193,24 @@ namespace Robust.Client.Console.Commands
public string Help => "Usage: showrays <raylifetime>";
public string Description => "Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
return false;
shell.WriteError($"{args[0]} is not a valid integer.");
return;
}
var mgr = IoCManager.Resolve<IDebugDrawingManager>();
mgr.DebugDrawRays = !mgr.DebugDrawRays;
console.AddLine("Toggled showing rays to:" + mgr.DebugDrawRays.ToString(), Color.Green);
shell.WriteError("Toggled showing rays to:" + mgr.DebugDrawRays.ToString());
mgr.DebugRayLifetime = TimeSpan.FromSeconds((double)int.Parse(args[0], CultureInfo.InvariantCulture));
return false;
}
}
@@ -241,10 +220,9 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Immediately disconnect from the server and go back to the main menu.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IClientNetManager>().ClientDisconnect("Disconnect command used.");
return false;
}
}
@@ -257,33 +235,33 @@ namespace Robust.Client.Console.Commands
public string Description => "Displays verbose diagnostics for an entity.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if ((!new Regex(@"^c?[0-9]+$").IsMatch(args[0])))
{
console.AddLine("Malformed UID", Color.Red);
return false;
shell.WriteError("Malformed UID");
return;
}
var uid = EntityUid.Parse(args[0]);
var entmgr = IoCManager.Resolve<IEntityManager>();
if (!entmgr.TryGetEntity(uid, out var entity))
{
console.AddLine("That entity does not exist. Sorry lad.", Color.Red);
return false;
shell.WriteError("That entity does not exist. Sorry lad.");
return;
}
console.AddLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
console.AddLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
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())
{
console.AddLine(component.ToString() ?? "");
shell.WriteLine(component.ToString() ?? "");
if (component is IComponentDebug debug)
{
foreach (var line in debug.GetDebugString().Split('\n'))
@@ -293,12 +271,10 @@ namespace Robust.Client.Console.Commands
continue;
}
console.AddLine("\t" + line);
shell.WriteLine("\t" + line);
}
}
}
return false;
}
}
@@ -308,12 +284,12 @@ namespace Robust.Client.Console.Commands
public string Help => "sggcell <gridID> <vector2i> [offset]\nThat vector2i param is in the form x<int>,y<int>.";
public string Description => "Lists entities on a snap grid cell.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2 && args.Length != 3)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
string gridId = args[0];
@@ -322,14 +298,14 @@ namespace Robust.Client.Console.Commands
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
return false;
shell.WriteError($"{args[0]} is not a valid integer.");
return;
}
if (!new Regex(@"^-?[0-9]+,-?[0-9]+$").IsMatch(indices))
{
console.AddLine("mapIndicies must be of form x<int>,y<int>", Color.Red);
return false;
shell.WriteError("mapIndicies must be of form x<int>,y<int>");
return;
}
SnapGridOffset selectedOffset;
@@ -339,8 +315,8 @@ namespace Robust.Client.Console.Commands
}
else
{
console.AddLine("given offset type is not defined", Color.Red);
return false;
shell.WriteError("given offset type is not defined");
return;
}
var mapMan = IoCManager.Resolve<IMapManager>();
@@ -354,16 +330,13 @@ namespace Robust.Client.Console.Commands
int.Parse(indices.Split(',')[1], CultureInfo.InvariantCulture)),
selectedOffset))
{
console.AddLine(entity.Owner.Uid.ToString());
shell.WriteLine(entity.Owner.Uid.ToString());
}
}
else
{
console.AddLine("grid does not exist", Color.Red);
return false;
shell.WriteError("grid does not exist");
}
return false;
}
}
@@ -373,19 +346,17 @@ namespace Robust.Client.Console.Commands
public string Description => "Changes the name used when attempting to connect to the server.";
public string Help => Command + " <name>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var client = IoCManager.Resolve<IBaseClient>();
client.PlayerNameOverride = args[0];
console.AddLine($"Overriding player name to \"{args[0]}\".", Color.White);
return false;
shell.WriteLine($"Overriding player name to \"{args[0]}\".");
}
}
@@ -395,12 +366,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Pre-caches a resource.";
public string Help => "ldrsc <path> <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var resourceCache = IoCManager.Resolve<IResourceCache>();
var reflection = IoCManager.Resolve<IReflectionManager>();
@@ -412,8 +383,8 @@ namespace Robust.Client.Console.Commands
}
catch(ArgumentException)
{
console.AddLine("Unable to find type", Color.Red);
return false;
shell.WriteError("Unable to find type");
return;
}
var getResourceMethod =
@@ -423,7 +394,6 @@ namespace Robust.Client.Console.Commands
DebugTools.Assert(getResourceMethod != null);
var generic = getResourceMethod!.MakeGenericMethod(type);
generic.Invoke(resourceCache, new object[] { args[0], true });
return false;
}
}
@@ -433,12 +403,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Reloads a resource.";
public string Help => "rldrsc <path> <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var resourceCache = IoCManager.Resolve<IResourceCache>();
var reflection = IoCManager.Resolve<IReflectionManager>();
@@ -450,15 +420,14 @@ namespace Robust.Client.Console.Commands
}
catch(ArgumentException)
{
console.AddLine("Unable to find type", Color.Red);
return false;
shell.WriteError("Unable to find type");
return;
}
var getResourceMethod = resourceCache.GetType().GetMethod("ReloadResource", new[] { typeof(string) });
DebugTools.Assert(getResourceMethod != null);
var generic = getResourceMethod!.MakeGenericMethod(type);
generic.Invoke(resourceCache, new object[] { args[0] });
return false;
}
}
@@ -468,18 +437,18 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets the tile count of a grid";
public string Help => "Usage: gridtc <gridId>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.");
return false;
shell.WriteLine($"{args[0]} is not a valid integer.");
return;
}
var gridId = new GridId(int.Parse(args[0]));
@@ -487,13 +456,11 @@ namespace Robust.Client.Console.Commands
if (mapManager.TryGetGrid(gridId, out var grid))
{
console.AddLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
return false;
shell.WriteLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
}
else
{
console.AddLine($"No grid exists with id {id}",Color.Red);
return false;
shell.WriteError($"No grid exists with id {id}");
}
}
}
@@ -504,7 +471,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Dump GUI tree to /guidump.txt in user data.";
public string Help => "guidump";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var root = IoCManager.Resolve<IUserInterfaceManager>().RootControl;
var res = IoCManager.Resolve<IResourceManager>();
@@ -514,8 +481,6 @@ namespace Robust.Client.Console.Commands
{
_writeNode(root, 0, writer);
}
return false;
}
private static void _writeNode(Control control, int indents, TextWriter writer)
@@ -575,7 +540,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Open a dummy UI testing window";
public string Help => "uitest";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var window = new SS14Window { CustomMinimumSize = (500, 400)};
var tabContainer = new TabContainer();
@@ -667,8 +632,6 @@ namespace Robust.Client.Console.Commands
});
window.OpenCentered();
return false;
}
}
@@ -678,11 +641,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Sets the system clipboard";
public string Help => "setclipboard <text>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IClipboardManager>();
mgr.SetText(args[0]);
return false;
}
}
@@ -692,11 +654,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets the system clipboard";
public string Help => "getclipboard";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IClipboardManager>();
console.AddLine(mgr.GetText());
return false;
shell.WriteLine(mgr.GetText());
}
}
@@ -706,11 +667,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles light rendering.";
public string Help => "togglelight";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
mgr.Enabled = !mgr.Enabled;
return false;
if (!mgr.LockConsoleAccess)
mgr.Enabled = !mgr.Enabled;
}
}
@@ -720,12 +681,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles fov for client.";
public string Help => "togglefov";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IEyeManager>();
if (mgr.CurrentEye != null)
mgr.CurrentEye.DrawFov = !mgr.CurrentEye.DrawFov;
return false;
}
}
@@ -735,11 +695,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles hard fov for client (for debugging space-station-14#2353).";
public string Help => "togglehardfov";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
mgr.DrawHardFov = !mgr.DrawHardFov;
return false;
if (!mgr.LockConsoleAccess)
mgr.DrawHardFov = !mgr.DrawHardFov;
}
}
@@ -749,11 +709,24 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles shadow rendering.";
public string Help => "toggleshadows";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
mgr.DrawShadows = !mgr.DrawShadows;
return false;
if (!mgr.LockConsoleAccess)
mgr.DrawShadows = !mgr.DrawShadows;
}
}
internal class ToggleLightBuf : IConsoleCommand
{
public string Command => "togglelightbuf";
public string Description => "Toggles lighting rendering. This includes shadows but not FOV.";
public string Help => "togglelightbuf";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
if (!mgr.LockConsoleAccess)
mgr.DrawLighting = !mgr.DrawLighting;
}
}
@@ -763,7 +736,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Run the GC.";
public string Help => "gc [generation]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length == 0)
{
@@ -774,10 +747,21 @@ namespace Robust.Client.Console.Commands
if (int.TryParse(args[0], out int result))
GC.Collect(result);
else
console.AddLine("Failed to parse argument.",Color.Red);
shell.WriteError("Failed to parse argument.");
}
}
}
return false;
internal class GcFullCommand : IConsoleCommand
{
public string Command => "gcf";
public string Description => "Run the GC, fully, compacting LOH and everything.";
public string Help => "gcf";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(2, GCCollectionMode.Forced, true, true);
}
}
@@ -790,16 +774,16 @@ namespace Robust.Client.Console.Commands
public string Help => "gc_mode\nSee current GC Latencymode\ngc_mode [type]\n Change GC Latency mode to [type]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var prevMode = GCSettings.LatencyMode;
if (args.Length == 0)
{
console.AddLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
console.AddLine("possible modes:");
shell.WriteLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
shell.WriteLine("possible modes:");
foreach (int mode in (int[]) Enum.GetValues(typeof(GCLatencyMode)))
{
console.AddLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
shell.WriteLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
}
}
else
@@ -811,16 +795,14 @@ namespace Robust.Client.Console.Commands
}
else if (!Enum.TryParse(args[0], true, out mode))
{
console.AddLine($"unknown gc latency mode: {args[0]}");
return false;
shell.WriteLine($"unknown gc latency mode: {args[0]}");
return;
}
console.AddLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
shell.WriteLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
GCSettings.LatencyMode = mode;
console.AddLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
shell.WriteLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
}
return false;
}
}
@@ -834,15 +816,13 @@ namespace Robust.Client.Console.Commands
public string Help => "szr_stats";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
console.AddLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
console.AddLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
return false;
shell.WriteLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
shell.WriteLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
}
}
@@ -853,7 +833,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets info about a chunk under your mouse cursor.";
public string Help => Command;
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mapMan = IoCManager.Resolve<IMapManager>();
var inputMan = IoCManager.Resolve<IInputManager>();
@@ -863,8 +843,8 @@ namespace Robust.Client.Console.Commands
if (!mapMan.TryFindGridAt(mousePos, out var grid))
{
console.AddLine("No grid under your mouse cursor.");
return false;
shell.WriteLine("No grid under your mouse cursor.");
return;
}
var internalGrid = (IMapGridInternal)grid;
@@ -872,8 +852,7 @@ namespace Robust.Client.Console.Commands
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
var chunk = internalGrid.GetChunk(chunkIndex);
console.AddLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
return false;
shell.WriteLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
}
}
@@ -890,7 +869,7 @@ namespace Robust.Client.Console.Commands
public static ConcurrentDictionary<string, bool>? _reloadShadersQueued = new();
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IResourceCacheInternal resC;
if (args.Length == 1)
@@ -899,8 +878,8 @@ namespace Robust.Client.Console.Commands
{
if (_watchers != null)
{
console.AddLine("Already watching.");
return false;
shell.WriteLine("Already watching.");
return;
}
resC = IoCManager.Resolve<IResourceCacheInternal>();
@@ -969,11 +948,11 @@ namespace Robust.Client.Console.Commands
{
IoCManager.Resolve<IResourceCache>()
.ReloadResource<ShaderSourceResource>(resPath);
console.AddLine($"Reloaded shader: {resPath}");
shell.WriteLine($"Reloaded shader: {resPath}");
}
catch (Exception)
{
console.AddLine($"Failed to reload shader: {resPath}");
shell.WriteLine($"Failed to reload shader: {resPath}");
}
_reloadShadersQueued.TryRemove(ev.FullPath, out var _);
@@ -993,17 +972,17 @@ namespace Robust.Client.Console.Commands
++created;
}
console.AddLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
shell.WriteLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
return false;
return;
}
if (args[0] == "-watch")
{
if (_watchers == null)
{
console.AddLine("No shader directory watchers active.");
return false;
shell.WriteLine("No shader directory watchers active.");
return;
}
var disposed = 0;
@@ -1015,19 +994,19 @@ namespace Robust.Client.Console.Commands
_watchers = null;
console.AddLine($"Disposed of {disposed} shader directory watchers.");
shell.WriteLine($"Disposed of {disposed} shader directory watchers.");
return false;
return;
}
}
if (args.Length > 1)
{
console.AddLine("Not implemented.");
return false;
shell.WriteLine("Not implemented.");
return;
}
console.AddLine("Reloading content shader resources...");
shell.WriteLine("Reloading content shader resources...");
resC = IoCManager.Resolve<IResourceCacheInternal>();
@@ -1039,13 +1018,11 @@ namespace Robust.Client.Console.Commands
}
catch (Exception)
{
console.AddLine($"Failed to reload shader: {path}");
shell.WriteLine($"Failed to reload shader: {path}");
}
}
console.AddLine("Done.");
return false;
shell.WriteLine("Done.");
}
}
@@ -1056,14 +1033,14 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggle fov and light debug layers";
public string Help => "cldbglyr <layer>: Toggle <layer>\ncldbglyr: Turn all Layers off";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var clyde = IoCManager.Resolve<IClydeInternal>();
if (args.Length < 1)
{
clyde.DebugLayers = ClydeDebugLayers.None;
return false;
return;
}
clyde.DebugLayers = args[0] switch
@@ -1072,8 +1049,6 @@ namespace Robust.Client.Console.Commands
"light" => ClydeDebugLayers.Light,
_ => ClydeDebugLayers.None
};
return false;
}
}
@@ -1083,12 +1058,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Keys key info for a key";
public string Help => "keyinfo <Key>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var clyde = IoCManager.Resolve<IClydeInternal>();
@@ -1101,15 +1076,13 @@ namespace Robust.Client.Console.Commands
var scanCode = clyde.GetKeyScanCode(key);
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
console.AddLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
shell.WriteLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
}
else if (int.TryParse(args[0], out var scanCode))
{
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
console.AddLine($"name via scan code: '{nameScanCode}'");
shell.WriteLine($"name via scan code: '{nameScanCode}'");
}
return false;
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands
{
@@ -12,23 +11,21 @@ namespace Robust.Client.Console.Commands
public string Description => "Dumps a type's members in a format suitable for the sandbox configuration file.";
public string Help => "Usage: dmetamem <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var type = Type.GetType(args[0]);
if (type == null)
{
console.AddLine("That type does not exist", Color.Red);
return false;
shell.WriteError("That type does not exist");
return;
}
foreach (var sig in AssemblyTypeChecker.DumpMetaMembers(type))
{
System.Console.WriteLine(@$"- ""{sig}""");
console.AddLine(sig);
shell.WriteLine(sig);
}
return false;
}
}
#endif

View File

@@ -1,8 +1,7 @@
using System.Linq;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Interfaces.Network;
using System.Linq;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Network;
namespace Robust.Client.Console.Commands
{
@@ -12,37 +11,36 @@ namespace Robust.Client.Console.Commands
public string Help => "When no arguments are provided, displays a generic help text. When an argument is passed, display the help text for the command with that name.";
public string Description => "Display help text.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
switch (args.Length)
{
case 0:
console.AddLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
shell.WriteLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
break;
case 1:
string commandname = args[0];
if (!console.Commands.ContainsKey(commandname))
if (!shell.ConsoleHost.RegisteredCommands.ContainsKey(commandname))
{
if (!IoCManager.Resolve<IClientNetManager>().IsConnected)
{
// No server so nothing to respond with unknown command.
console.AddLine("Unknown command: " + commandname, Color.Red);
return false;
shell.WriteError("Unknown command: " + commandname);
return;
}
// TODO: Maybe have a server side help?
return false;
return;
}
IConsoleCommand command = console.Commands[commandname];
console.AddLine(string.Format("{0} - {1}", command.Command, command.Description));
console.AddLine(command.Help);
IConsoleCommand command = shell.ConsoleHost.RegisteredCommands[commandname];
shell.WriteLine(string.Format("{0} - {1}", command.Command, command.Description));
shell.WriteLine(command.Help);
break;
default:
console.AddLine("Invalid amount of arguments.", Color.Red);
shell.WriteError("Invalid amount of arguments.");
break;
}
return false;
}
}
@@ -55,7 +53,7 @@ namespace Robust.Client.Console.Commands
"only commands that contain the given string in their name will be listed.";
public string Description => "List all commands, optionally with a filter.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var filter = "";
if (args.Length == 1)
@@ -64,14 +62,12 @@ namespace Robust.Client.Console.Commands
}
var conGroup = IoCManager.Resolve<IClientConGroupController>();
foreach (var command in console.Commands.Values
foreach (var command in shell.ConsoleHost.RegisteredCommands.Values
.Where(p => p.Command.Contains(filter) && conGroup.CanCommand(p.Command))
.OrderBy(c => c.Command))
{
console.AddLine(command.Command + ": " + command.Description);
shell.WriteLine(command.Command + ": " + command.Description);
}
return false;
}
}
}

View File

@@ -0,0 +1,67 @@
#if !FULL_RELEASE
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Robust.Client.Utility;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Network;
namespace Robust.Client.Console.Commands
{
internal sealed class LauncherAuthCommand : IConsoleCommand
{
public string Command => "launchauth";
public string Description => "Load authentication tokens from launcher data to aid in testing of live servers";
public string Help => "launchauth [account name]";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var wantName = args.Length > 0 ? args[0] : null;
var basePath = Path.GetDirectoryName(UserDataDir.GetUserDataDir())!;
var cfgPath = Path.Combine(basePath, "launcher", "launcher_config.json");
var data = JsonSerializer.Deserialize<LauncherConfig>(File.ReadAllText(cfgPath))!;
var login = wantName != null
? data.Logins.FirstOrDefault(p => p.Username == wantName)
: data.Logins.FirstOrDefault();
if (login == null)
{
shell.WriteLine("Unable to find a matching login");
return;
}
var token = login.Token.Token;
var userId = login.UserId;
var cfg = IoCManager.Resolve<IAuthManager>();
cfg.Token = token;
cfg.UserId = new NetUserId(Guid.Parse(userId));
}
private sealed class LauncherConfig
{
[JsonInclude] [JsonPropertyName("logins")]
public LauncherLogin[] Logins = default!;
}
private sealed class LauncherLogin
{
[JsonInclude] public string Username = default!;
[JsonInclude] public string UserId = default!;
[JsonInclude] public LauncherToken Token = default!;
}
private sealed class LauncherToken
{
[JsonInclude] public string Token = default!;
}
}
}
#endif

View File

@@ -1,8 +1,7 @@
using System.Linq;
using System.Runtime.Loader;
using System.Text;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -13,18 +12,16 @@ namespace Robust.Client.Console.Commands
public string Description => "Lists loaded assemblies by load context.";
public string Help => Command;
public bool Execute(IDebugConsole console, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
foreach (var context in AssemblyLoadContext.All)
{
console.AddLine($"{context.Name}:");
shell.WriteLine($"{context.Name}:");
foreach (var assembly in context.Assemblies.OrderBy(a => a.FullName))
{
console.AddLine($" {assembly.FullName}");
shell.WriteLine($" {assembly.FullName}");
}
}
return false;
}
}
}

View File

@@ -1,7 +1,6 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Log;
using System;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -13,12 +12,12 @@ namespace Robust.Client.Console.Commands
+ "\n sawmill: A label prefixing log messages. This is the one you're setting the level for."
+ "\n level: The log level. Must match one of the values of the LogLevel enum.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return false;
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
return;
}
var name = args[0];
@@ -32,13 +31,12 @@ namespace Robust.Client.Console.Commands
{
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return false;
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
level = result;
}
Logger.GetSawmill(name).Level = level;
return false;
}
}
@@ -51,12 +49,12 @@ namespace Robust.Client.Console.Commands
+ "\n level: The log level. Must match one of the values of the LogLevel enum."
+ "\n message: The message to be logged. Wrap this in double quotes if you want to use spaces.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 3)
{
console.AddLine("Invalid argument amount. Expected 3 arguments.", Color.Red);
return false;
shell.WriteError("Invalid argument amount. Expected 3 arguments.");
return;
}
var name = args[0];
@@ -64,13 +62,12 @@ namespace Robust.Client.Console.Commands
var message = args[2]; // yes this doesn't support spaces idgaf.
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return false;
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
var level = result;
Logger.LogS(level, name, message);
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
using Robust.Client.Interfaces.Console;
using System;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -9,10 +9,9 @@ namespace Robust.Client.Console.Commands
public string Description => "Kills the game client instantly.";
public string Help => "Kills the game client instantly, leaving no traces. No telling the server goodbye";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
Environment.Exit(0);
return false;
}
}
}

View File

@@ -1,22 +1,19 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands
{
#if CLIENT_SCRIPTING
internal sealed class ScriptConsoleCommand : IConsoleCommand
internal sealed class ScriptCommand : IConsoleCommand
{
public string Command => "csi";
public string Description => "Opens a C# interactive console.";
public string Help => "csi";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
new ScriptConsoleClient().OpenCentered();
return false;
}
}
@@ -26,33 +23,29 @@ namespace Robust.Client.Console.Commands
public string Description => "Opens a variable watch window.";
public string Help => "watch";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
new WatchWindow().OpenCentered();
return false;
}
}
#endif
internal sealed class ServerScriptConsoleCommand : IConsoleCommand
internal sealed class ServerScriptCommand : IConsoleCommand
{
public string Command => "scsi";
public string Description => "Opens a C# interactive console on the server.";
public string Help => "scsi";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IScriptClient>();
if (!mgr.CanScript)
{
console.AddLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
return false;
shell.WriteError(Loc.GetString("You do not have server side scripting permission."));
return;
}
mgr.StartSession();
return false;
}
}
}

View File

@@ -1,5 +1,4 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Network;
@@ -11,7 +10,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Sends garbage to the server.";
public string Help => "The server will reply with 'no u'";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
// MsgStringTableEntries is registered as NetMessageAccept.Client so the server will immediately deny it.
// And kick us.
@@ -19,8 +18,6 @@ namespace Robust.Client.Console.Commands
var msg = net.CreateNetMessage<MsgStringTableEntries>();
msg.Entries = new MsgStringTableEntries.Entry[0];
net.ClientSendMessage(msg);
return false;
}
}
}

View File

@@ -0,0 +1,34 @@
using JetBrains.Annotations;
using Robust.Client.Input;
using Robust.Shared.Console;
using Robust.Shared.IoC;
namespace Robust.Client.Console.Commands
{
[UsedImplicitly]
public class SetInputContextCommand : IConsoleCommand
{
public string Command => "setinputcontext";
public string Description => "Sets the active input context.";
public string Help => "setinputcontext <context>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteLine("Invalid number of arguments!");
return;
}
var inputMan = IoCManager.Resolve<IInputManager>();
if (!inputMan.Contexts.Exists(args[0]))
{
shell.WriteLine("Context not found!");
return;
}
inputMan.Contexts.SetActiveContext(args[0]);
}
}
}

View File

@@ -1,6 +1,4 @@
using System;
namespace Robust.Client.Console
namespace Robust.Client.Console
{
public interface IClientConGroupController : IClientConGroupImplementation
{

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Client.Interfaces.Console;
namespace Robust.Client.Console
{
public interface IClientConsole : IDisposable
{
/// <summary>
/// Initializes the console into a useable state.
/// </summary>
void Initialize();
/// <summary>
/// Resets the console to a post-initialized state.
/// </summary>
void Reset();
event EventHandler<AddStringArgs> AddString;
event EventHandler<AddFormattedMessageArgs> AddFormatted;
event EventHandler ClearText;
IReadOnlyDictionary<string, IConsoleCommand> Commands { get; }
/// <summary>
/// Parses console commands (verbs).
/// </summary>
/// <param name="text"></param>
void ProcessCommand(string text);
void SendServerCommandRequest();
}
}

View File

@@ -0,0 +1,24 @@
using System;
using Robust.Shared.Console;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public interface IClientConsoleHost : IConsoleHost, IDisposable
{
/// <summary>
/// Initializes the console into a useable state.
/// </summary>
void Initialize();
/// <summary>
/// Resets the console to a post-initialized state.
/// </summary>
void Reset();
event EventHandler<AddStringArgs> AddString;
event EventHandler<AddFormattedMessageArgs> AddFormatted;
void AddFormattedLine(FormattedMessage message);
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
namespace Robust.Client.Console

View File

@@ -10,10 +10,10 @@ using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Text;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.ViewVariables;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Reflection;
using Robust.Shared.Scripting;
using Robust.Shared.Utility;

View File

@@ -7,10 +7,10 @@ using Microsoft.CodeAnalysis.Scripting;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Reflection;
using Robust.Shared.Scripting;
using Robust.Shared.Timing;

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.IO;
using Robust.Shared.Interfaces.Resources;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;

View File

@@ -1,17 +1,9 @@
using System;
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.Debugging;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Input;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
@@ -26,6 +18,7 @@ namespace Robust.Client.Debugging
[Dependency] private readonly IComponentManager _componentManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
@@ -48,7 +41,7 @@ namespace Robust.Client.Debugging
if (value)
{
_overlayManager.AddOverlay(new PhysicsOverlay(_componentManager, _eyeManager,
_prototypeManager, _inputManager));
_prototypeManager, _inputManager, _physicsManager));
}
else
{
@@ -86,6 +79,7 @@ namespace Robust.Client.Debugging
private readonly IComponentManager _componentManager;
private readonly IEyeManager _eyeManager;
private readonly IInputManager _inputManager;
private readonly IPhysicsManager _physicsManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
private readonly ShaderInstance _shader;
@@ -94,12 +88,13 @@ namespace Robust.Client.Debugging
private Vector2 _hoverStartScreen = Vector2.Zero;
private List<IPhysBody> _hoverBodies = new();
public PhysicsOverlay(IComponentManager compMan, IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager)
public PhysicsOverlay(IComponentManager compMan, IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager, IPhysicsManager physicsManager)
: base(nameof(PhysicsOverlay))
{
_componentManager = compMan;
_eyeManager = eyeMan;
_inputManager = inputManager;
_physicsManager = physicsManager;
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
var cache = IoCManager.Resolve<IResourceCache>();
@@ -158,23 +153,20 @@ namespace Robust.Client.Debugging
_hoverStartScreen = mouseScreenPos;
var viewport = _eyeManager.GetWorldViewport();
foreach (var boundingBox in _componentManager.EntityQuery<IPhysicsComponent>())
{
var physBody = (IPhysBody) boundingBox;
if (viewport.IsEmpty()) return;
var mapId = _eyeManager.CurrentMap;
foreach (var physBody in _physicsManager.GetCollidingEntities(mapId, viewport))
{
// all entities have a TransformComponent
var transform = physBody.Entity.Transform;
// if not on the same map, continue
if (transform.MapID != _eyeManager.CurrentMap || !transform.IsMapTransform)
continue;
var worldBox = physBody.WorldAABB;
var colorEdge = Color.Red.WithAlpha(0.33f);
if (worldBox.IsEmpty()) continue;
// if not on screen, or too small, continue
if (!worldBox.Intersects(in viewport) || worldBox.IsEmpty())
continue;
var colorEdge = Color.Red.WithAlpha(0.33f);
foreach (var shape in physBody.PhysicsShapes)
{

View File

@@ -1,15 +1,11 @@
using Robust.Client.Interfaces.Debugging;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.IoC;
using Robust.Shared.Network.Messages;
using System;
using System.Collections.Generic;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics;
using Robust.Shared.Maths;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Shared.Timing;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.Network;
namespace Robust.Client.Debugging
{

View File

@@ -1,4 +1,4 @@
namespace Robust.Client.Interfaces.Debugging
namespace Robust.Client.Debugging
{
/// <summary>
/// A collection of visual debug overlays for the client game.

View File

@@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Robust.Shared.Timing;
namespace Robust.Client.Interfaces.Debugging
namespace Robust.Client.Debugging
{
public interface IDebugDrawingManager
{

View File

@@ -1,20 +1,17 @@
using System;
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Robust.Client.Console;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.GameObjects;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Interfaces.Utility;
using Robust.Client.GameObjects;
using Robust.Client.GameStates;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Placement;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Client.UserInterface;
using Robust.Client.Utility;
using Robust.Client.ViewVariables;
using Robust.LoaderApi;
@@ -22,15 +19,11 @@ using Robust.Shared;
using Robust.Shared.Asynchronous;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Interfaces.Timers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
@@ -50,7 +43,7 @@ namespace Robust.Client
[Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManager = default!;
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IClientConsole _console = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly IClientEntityManager _entityManager = default!;
[Dependency] private readonly IPlacementManager _placementManager = default!;
@@ -67,6 +60,7 @@ namespace Robust.Client
[Dependency] private readonly IComponentManager _componentManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustMappedStringSerializer _stringSerializer = default!;
[Dependency] private readonly IAuthManager _authManager = default!;
private CommandLineArgs? _commandLineArgs;
private bool _disableAssemblyLoadContext;
@@ -164,9 +158,10 @@ namespace Robust.Client
_userInterfaceManager.Initialize();
_networkManager.Initialize(false);
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
_serializer.Initialize();
_inputManager.Initialize();
_console.Initialize();
_consoleHost.Initialize();
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
_prototypeManager.Resync();
_mapManager.Initialize();
@@ -185,6 +180,8 @@ namespace Robust.Client
_client.PlayerNameOverride = _commandLineArgs.Username;
}
_authManager.LoadFromEnv();
_clyde.Ready();
if ((_commandLineArgs?.Connect == true || _commandLineArgs?.Launcher == true)

View File

@@ -1,4 +1,4 @@
using Robust.Client.Input;
using Robust.Client.Input;
namespace Robust.Client
{

View File

@@ -1,67 +1,9 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Robust.Client.Graphics.Clyde;
using Robust.Client.Console;
using Robust.Client.Debugging;
using Robust.Client.GameObjects;
using Robust.Client.GameStates;
using Robust.Client.Graphics;
using Robust.Client.Graphics.ClientEye;
using Robust.Client.Graphics.Lighting;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Input;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Debugging;
using Robust.Client.Interfaces.GameObjects;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Lighting;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.Map;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Interfaces.Utility;
using Robust.Client.Map;
using Robust.Client.Placement;
using Robust.Client.Player;
using Robust.Client.Reflection;
using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Client.UserInterface;
using Robust.Client.Utility;
using Robust.Client.ViewVariables;
using Robust.Shared;
using Robust.Shared.Asynchronous;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Exceptions;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Resources;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Interfaces.Timers;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Physics;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timers;
using Robust.Shared.Timing;
using Robust.Shared.Reflection;
namespace Robust.Client
{

View File

@@ -1,4 +1,4 @@
using Robust.Client;
using Robust.Client;
using Robust.LoaderApi;
[assembly: LoaderEntryPoint(typeof(GameController.LoaderEntryPoint))]
@@ -11,7 +11,7 @@ namespace Robust.Client
{
public void Main(IMainArgs args)
{
GameController.Start(args.Args, contentStart: false, args);
Start(args.Args, contentStart: false, args);
}
}
}

View File

@@ -1,7 +1,5 @@
using System;
using Robust.Client.Interfaces;
using System;
using Robust.LoaderApi;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Timing;

View File

@@ -0,0 +1,3 @@
Everything in this folder and subfolders needs to be in:
Robust.Client.GameObjects

View File

@@ -1,18 +1,4 @@
using Robust.Client.GameObjects.Components;
using Robust.Client.GameObjects.Components.Animations;
using Robust.Client.GameObjects.Components.Containers;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Appearance;
using Robust.Shared.GameObjects.Components.Eye;
using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.GameObjects.Components.Renderable;
using Robust.Shared.GameObjects.Components.Timers;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Robust.Client.GameObjects
@@ -55,15 +41,13 @@ namespace Robust.Client.GameObjects
Register<AppearanceComponent>();
RegisterReference<AppearanceComponent, SharedAppearanceComponent>();
Register<AppearanceTestComponent>();
Register<SnapGridComponent>();
Register<ClientUserInterfaceComponent>();
RegisterReference<ClientUserInterfaceComponent, SharedUserInterfaceComponent>();
RegisterIgnore("IgnorePause");
Register<AnimationPlayerComponent>();
Register<ContainerManagerComponent>();

View File

@@ -1,11 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Client.Interfaces.GameObjects;
using Robust.Shared.Exceptions;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;

View File

@@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.GameStates;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects

View File

@@ -5,7 +5,7 @@ using Robust.Client.Animations;
using Robust.Shared.GameObjects;
using static Robust.Client.Animations.AnimationPlaybackShared;
namespace Robust.Client.GameObjects.Components.Animations
namespace Robust.Client.GameObjects
{
/// <summary>
/// Plays back <see cref="Animation"/>s on entities.
@@ -48,11 +48,21 @@ namespace Robust.Client.GameObjects.Components.Animations
return;
}
List<string>? toRemove = null;
// TODO: Get rid of this ToArray() allocation.
foreach (var (key, playback) in _playingAnimations.ToArray())
{
var keep = UpdatePlayback(Owner, playback, frameTime);
if (!keep)
{
toRemove ??= new List<string>();
toRemove.Add(key);
}
}
if (toRemove != null)
{
foreach (var key in toRemove)
{
_playingAnimations.Remove(key);
AnimationCompleted?.Invoke(key);

View File

@@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Appearance;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -54,17 +50,17 @@ namespace Robust.Client.GameObjects
return (T) data[key];
}
public override bool TryGetData<T>(Enum key, [MaybeNullWhen(false)] out T data)
public override bool TryGetData<T>(Enum key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
public override bool TryGetData<T>(string key, [MaybeNullWhen(false)] out T data)
public override bool TryGetData<T>(string key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
internal bool TryGetData<T>(object key, [MaybeNullWhen(false)] out T data)
internal bool TryGetData<T>(object key, [NotNullWhen(true)] out T data)
{
if (this.data.TryGetValue(key, out var dat))
{
@@ -72,12 +68,14 @@ namespace Robust.Client.GameObjects
return true;
}
data = default;
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();
@@ -85,10 +83,9 @@ namespace Robust.Client.GameObjects
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState == null)
if (curState is not AppearanceComponentState actualState)
return;
var actualState = (AppearanceComponentState) curState;
data = actualState.Data;
MarkDirty();
}

View File

@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.ViewVariables;
namespace Robust.Client.GameObjects.Components.Containers
namespace Robust.Client.GameObjects
{
public sealed partial class ContainerManagerComponent
{

View File

@@ -2,14 +2,10 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Containers;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.ViewVariables;
namespace Robust.Client.GameObjects.Components.Containers
namespace Robust.Client.GameObjects
{
public sealed partial class ContainerManagerComponent : SharedContainerManagerComponent
{

View File

@@ -1,8 +1,5 @@
using Robust.Client.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Eye;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;

View File

@@ -1,9 +1,9 @@
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
@@ -31,11 +31,11 @@ namespace Robust.Client.GameObjects
}
}
private static IDirectionalTextureProvider TextureForConfig(ObjectSerializer serializer, IResourceCache resourceCache)
private static IRsiStateLike TextureForConfig(ObjectSerializer serializer, IResourceCache resourceCache)
{
DebugTools.Assert(serializer.Reading);
if (serializer.TryGetCacheData<IDirectionalTextureProvider>(SerializationCache, out var dirTex))
if (serializer.TryGetCacheData<IRsiStateLike>(SerializationCache, out var dirTex))
{
return dirTex;
}
@@ -93,7 +93,7 @@ namespace Robust.Client.GameObjects
}
}
public static IDirectionalTextureProvider? GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
public static IRsiStateLike? GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
{
if (!prototype.Components.TryGetValue("Icon", out var mapping))
{

View File

@@ -1,10 +1,9 @@
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Robust.Client.GameObjects.Components
namespace Robust.Client.GameObjects
{
/// <summary>
/// Defines data fields used in the <see cref="InputSystem"/>.

View File

@@ -1,7 +1,5 @@
using System;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;

View File

@@ -1,16 +1,13 @@
using Robust.Client.GameObjects.EntitySystems;
using System;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using System;
namespace Robust.Client.GameObjects
{
@@ -83,6 +80,18 @@ namespace Robust.Client.GameObjects
set => _energy = value;
}
/// <summary>
/// Soft shadow strength multiplier.
/// Has no effect if soft shadows are not enabled.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[Animatable]
public float Softness
{
get => _softness;
set => _softness = value;
}
[ViewVariables(VVAccess.ReadWrite)]
public bool VisibleNested
{
@@ -115,6 +124,7 @@ namespace Robust.Client.GameObjects
private bool _maskAutoRotate;
private Angle _rotation;
private float _energy;
private float _softness;
/// <summary>
/// Radius, in meters.
@@ -167,6 +177,7 @@ namespace Robust.Client.GameObjects
serializer.DataFieldCached(ref _color, "color", Color.White);
serializer.DataFieldCached(ref _enabled, "enabled", true);
serializer.DataFieldCached(ref _energy, "energy", 1f);
serializer.DataFieldCached(ref _softness, "softness", 1f);
serializer.DataFieldCached(ref _maskAutoRotate, "autoRot", false);
serializer.DataFieldCached(ref _visibleNested, "nestedvisible", true);

View File

@@ -1,9 +1,8 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.Client.Interfaces.GameObjects
namespace Robust.Client.GameObjects
{
public interface IRenderableComponent : IComponent
{

View File

@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Shaders;
using Robust.Shared.Animations;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Robust.Client.Interfaces.GameObjects.Components
namespace Robust.Client.GameObjects
{
public interface ISpriteComponent : IComponent, IAnimationProperties
{
@@ -205,29 +203,4 @@ namespace Robust.Client.Interfaces.GameObjects.Components
IEnumerable<ISpriteLayer> AllLayers { get; }
}
public interface ISpriteLayer
{
SpriteComponent.DirectionOffset DirOffset { get; set; }
RSI? Rsi { get; set; }
RSI.StateId RsiState { get; set; }
RSI? ActualRsi { get; }
Texture? Texture { get; set; }
Angle Rotation { get; set; }
Vector2 Scale { get; set; }
bool Visible { get; set; }
Color Color { get; set; }
float AnimationTime { get; set; }
int AnimationFrame { get; }
bool AutoAnimated { get; set; }
RSI.State.Direction EffectiveDirection(Angle worldRotation);
Vector2 LocalToLayer(Vector2 localPos);
}
}
}

View File

@@ -0,0 +1,30 @@
using Robust.Client.Graphics;
using Robust.Shared.Maths;
namespace Robust.Client.GameObjects
{
public interface ISpriteLayer
{
SpriteComponent.DirectionOffset DirOffset { get; set; }
RSI? Rsi { get; set; }
RSI.StateId RsiState { get; set; }
RSI? ActualRsi { get; }
Texture? Texture { get; set; }
Angle Rotation { get; set; }
Vector2 Scale { get; set; }
bool Visible { get; set; }
Color Color { get; set; }
float AnimationTime { get; set; }
int AnimationFrame { get; }
bool AutoAnimated { get; set; }
RSI.State.Direction EffectiveDirection(Angle worldRotation);
Vector2 LocalToLayer(Vector2 localPos);
}
}

View File

@@ -1,33 +1,24 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Text;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Graphics;
using Robust.Client.Graphics.ClientEye;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Renderable;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Reflection;
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.Utility;
using System.Linq;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
@@ -197,34 +188,34 @@ namespace Robust.Client.GameObjects
public void CopyFrom(SpriteComponent other)
{
//deep copying things to avoid entanglement
this._baseRsi = other._baseRsi;
this._directional = other._directional;
this._visible = other._visible;
this._layerMapShared = other._layerMapShared;
this.color = other.color;
this.offset = other.offset;
this.rotation = other.rotation;
this.scale = other.scale;
this.drawDepth = other.drawDepth;
this.Layers = new List<Layer>(other.Layers.Count);
_baseRsi = other._baseRsi;
_directional = other._directional;
_visible = other._visible;
_layerMapShared = other._layerMapShared;
color = other.color;
offset = other.offset;
rotation = other.rotation;
scale = other.scale;
drawDepth = other.drawDepth;
Layers = new List<Layer>(other.Layers.Count);
foreach (var otherLayer in other.Layers)
{
this.Layers.Add(new Layer(otherLayer, this));
Layers.Add(new Layer(otherLayer, this));
}
this.IsInert = other.IsInert;
this.LayerMap = other.LayerMap.ToDictionary(entry => entry.Key,
IsInert = other.IsInert;
LayerMap = other.LayerMap.ToDictionary(entry => entry.Key,
entry => entry.Value);
if (other.PostShader != null)
{
// only need to copy the shader if it's mutable
this.PostShader = other.PostShader.Mutable ? other.PostShader.Duplicate() : other.PostShader;
PostShader = other.PostShader.Mutable ? other.PostShader.Duplicate() : other.PostShader;
}
else
{
this.PostShader = null;
PostShader = null;
}
this.RenderOrder = other.RenderOrder;
RenderOrder = other.RenderOrder;
}
/// <inheritdoc />
@@ -991,6 +982,9 @@ namespace Robust.Client.GameObjects
var mRotation = Matrix3.CreateRotation(angle);
Matrix3.Multiply(ref mRotation, ref mOffset, out var transform);
// Only apply scale if needed.
if(!Scale.EqualsApprox(Vector2.One)) transform.Multiply(Matrix3.CreateScale(Scale));
transform.Multiply(worldTransform);
RenderInternal(drawingHandle, worldRotation, overrideDirection, transform);
@@ -1043,7 +1037,7 @@ namespace Robust.Client.GameObjects
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
var layerSpecificDir = layer.EffectiveDirection(state, worldRotation, overrideDirection);
@@ -1276,7 +1270,7 @@ namespace Robust.Client.GameObjects
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
if (!state.IsAnimated)
@@ -1404,7 +1398,7 @@ namespace Robust.Client.GameObjects
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
if (state.IsAnimated)
@@ -1415,9 +1409,9 @@ namespace Robust.Client.GameObjects
}
}
private RSI.State GetFallbackState()
internal static RSI.State GetFallbackState(IResourceCache cache)
{
var rsi = resourceCache.GetResource<RSIResource>("/Textures/error.rsi").RSI;
var rsi = cache.GetResource<RSIResource>("/Textures/error.rsi").RSI;
return rsi["error"];
}
@@ -1735,14 +1729,14 @@ namespace Robust.Client.GameObjects
var rsi = ActualRsi;
if (rsi == null)
{
state = _parent.GetFallbackState();
state = GetFallbackState(_parent.resourceCache);
Logger.ErrorS(LogCategory, "No RSI to pull new state from! Trace:\n{0}", Environment.StackTrace);
}
else
{
if (!rsi.TryGetState(stateId, out state))
{
state = _parent.GetFallbackState();
state = GetFallbackState(_parent.resourceCache);
Logger.ErrorS(LogCategory, "State '{0}' does not exist in RSI. Trace:\n{1}", stateId,
Environment.StackTrace);
}
@@ -1793,7 +1787,7 @@ namespace Robust.Client.GameObjects
}
}
public IDirectionalTextureProvider? Icon
public IRsiStateLike? Icon
{
get
{
@@ -1803,13 +1797,13 @@ namespace Robust.Client.GameObjects
var texture = layer.Texture;
if (!layer.State.IsValid) return null;
if (!layer.State.IsValid) return texture;
// Pull texture from RSI state instead.
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
return state;
@@ -1867,21 +1861,21 @@ namespace Robust.Client.GameObjects
}
public static IDirectionalTextureProvider? GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
public static IRsiStateLike GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
{
var icon = IconComponent.GetPrototypeIcon(prototype, resourceCache);
if (icon != null) return icon;
if (!prototype.Components.TryGetValue("Sprite", out var spriteNode))
if (!prototype.Components.ContainsKey("Sprite"))
{
return resourceCache.GetFallback<TextureResource>().Texture;
return GetFallbackState(resourceCache);
}
var dummy = new DummyIconEntity() {Prototype = prototype};
var dummy = new DummyIconEntity {Prototype = prototype};
var spriteComponent = dummy.AddComponent<SpriteComponent>();
dummy.Delete();
return spriteComponent?.Icon ?? resourceCache.GetFallback<TextureResource>().Texture;
return spriteComponent.Icon ?? GetFallbackState(resourceCache);
}
#region DummyIconEntity

View File

@@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Players;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using IComponent = Robust.Shared.Interfaces.GameObjects.IComponent;
namespace Robust.Client.GameObjects.Components.UserInterface
namespace Robust.Client.GameObjects
{
public class ClientUserInterfaceComponent : SharedUserInterfaceComponent
{

View File

@@ -1,7 +1,6 @@
using Robust.Client.Interfaces.GameStates;
using Robust.Client.GameStates;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;

View File

@@ -1,13 +1,12 @@
using Robust.Client.GameObjects.Components.Animations;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.GameObjects;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
internal sealed class AnimationPlayerSystem : EntitySystem
{
public override void FrameUpdate(float frameTime)
{
foreach (var animationPlayerComponent in EntityManager.ComponentManager.EntityQuery<AnimationPlayerComponent>())
foreach (var animationPlayerComponent in EntityManager.ComponentManager.EntityQuery<AnimationPlayerComponent>(true))
{
animationPlayerComponent.Update(frameTime);
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.GameObjects;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
internal sealed class AppearanceSystem : EntitySystem
{

View File

@@ -1,12 +1,12 @@
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.GameObjects;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
class AppearanceTestSystem : EntitySystem
{
public override void Update(float frameTime)
{
foreach (var appearanceTestComponent in EntityManager.ComponentManager.EntityQuery<AppearanceTestComponent>())
foreach (var appearanceTestComponent in EntityManager.ComponentManager.EntityQuery<AppearanceTestComponent>(true))
{
appearanceTestComponent.OnUpdate(frameTime);
}

View File

@@ -2,23 +2,18 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
[UsedImplicitly]
public class AudioSystem : EntitySystem
@@ -56,12 +51,12 @@ namespace Robust.Client.GameObjects.EntitySystems
private void PlayAudioPositionalHandler(PlayAudioPositionalMessage ev)
{
var gridId = ev.Coordinates.GetGridId(_entityManager);
if (!_mapManager.GridExists(gridId))
var mapId = ev.Coordinates.GetMapId(_entityManager);
if (!_mapManager.MapExists(mapId))
{
Logger.Error(
$"Server tried to play sound on grid {gridId}, which does not exist. Ignoring.");
$"Server tried to play sound on map {mapId}, which does not exist. Ignoring.");
return;
}
@@ -111,13 +106,13 @@ namespace Robust.Client.GameObjects.EntitySystems
if (stream.TrackingCoordinates != null)
{
var coords = stream.TrackingCoordinates.Value;
if (_mapManager.GridExists(coords.GetGridId(_entityManager)))
if (_mapManager.MapExists(coords.GetMapId(_entityManager)))
{
mapPos = stream.TrackingCoordinates.Value.ToMap(_entityManager);
}
else
{
// Grid no longer exists, delete stream.
// Map no longer exists, delete stream.
StreamDone(stream);
continue;
}
@@ -356,4 +351,76 @@ namespace Robust.Client.GameObjects.EntitySystems
event Action PlaybackDone;
}
public static class AudioSystemExtensions
{
/// <summary>
/// Play an audio file following an entity.
/// </summary>
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="entity">The entity "emitting" the audio.</param>
/// <param name="audioParams"></param>
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
public static IPlayingAudioStream? Play(
this IEntity entity,
string filename,
AudioParams? audioParams,
AudioSystem? audioSystem = null)
{
audioSystem ??= EntitySystem.Get<AudioSystem>();
return audioSystem.Play(filename, entity, audioParams);
}
/// <summary>
/// Play an audio stream following an entity.
/// </summary>
/// <param name="stream">The audio stream to play.</param>
/// <param name="entity">The entity "emitting" the audio.</param>
/// <param name="audioParams"></param>
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
public static IPlayingAudioStream? Play(
this IEntity entity,
AudioStream stream,
AudioParams? audioParams = null,
AudioSystem? audioSystem = null)
{
audioSystem ??= EntitySystem.Get<AudioSystem>();
return audioSystem.Play(stream, entity, audioParams);
}
/// <summary>
/// Play an audio file at a static position.
/// </summary>
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="coordinates">The coordinates at which to play the audio.</param>
/// <param name="audioParams"></param>
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
public static IPlayingAudioStream? Play(
this EntityCoordinates coordinates,
string filename,
AudioParams? audioParams = null,
AudioSystem? audioSystem = null)
{
audioSystem ??= EntitySystem.Get<AudioSystem>();
return audioSystem.Play(filename, coordinates, audioParams);
}
/// <summary>
/// Play an audio stream at a static position.
/// </summary>
/// <param name="stream">The audio stream to play.</param>
/// <param name="coordinates">The coordinates at which to play the audio.</param>
/// <param name="audioParams"></param>
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
public static IPlayingAudioStream? Play(
this EntityCoordinates coordinates,
AudioStream stream,
AudioParams? audioParams = null,
AudioSystem? audioSystem = null)
{
audioSystem ??= EntitySystem.Get<AudioSystem>();
return audioSystem.Play(stream, coordinates, audioParams);
}
}
}

View File

@@ -3,15 +3,11 @@ using System.Linq;
using JetBrains.Annotations;
using Robust.Client.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
// NOTE: this class handles both snap grid updates of occluders, as well as occluder tree updates (via its parent).
// This seems like it's doing somewhat double work because it already has an update queue for occluders but...

View File

@@ -1,9 +1,8 @@
using System.Collections.Generic;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
public class ContainerSystem : EntitySystem
{

View File

@@ -1,25 +1,15 @@
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using System;
using System.Collections.Generic;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
@@ -57,47 +47,54 @@ namespace Robust.Client.GameObjects
public void CreateEffect(EffectSystemMessage message)
{
// The source of effects is either local actions during FirstTimePredicted, or the network at LastServerTick
// When replaying predicted input, don't spam effects.
if(gameTiming.InPrediction && !gameTiming.IsFirstTimePredicted)
return;
if (message.AttachedEntityUid != null && message.Coordinates != default)
{
Logger.Warning("Set both an AttachedEntityUid and EntityCoordinates on an EffectSystemMessage for sprite {0} which is not supported!", message.EffectSprite);
}
var gameTime = gameTiming.CurTime;
if (gameTime > message.DeathTime) //Did we already die in transit? That's pretty troubling isn't it
if (message.LifeTime <= TimeSpan.Zero)
{
Logger.Warning("Effect using sprite {0} died in transit to the client", message.EffectSprite);
Logger.Warning("Effect using sprite {0} had zero lifetime.", message.EffectSprite);
return;
}
//Create effect from creation message
var effect = new Effect(message, resourceCache, _mapManager, _entityManager);
effect.Deathtime = gameTiming.CurTime + message.LifeTime;
if (effect.AttachedEntityUid != null)
{
effect.AttachedEntity = _entityManager.GetEntity(effect.AttachedEntityUid.Value);
}
//Age the effect through a single update to the previous update tick of the effect system
//effect.Update((float)((lasttimeprocessed - effect.Age).TotalSeconds));
_Effects.Add(effect);
}
public override void FrameUpdate(float frameTime)
{
var curTime = gameTiming.CurTime;
for (int i = 0; i < _Effects.Count; i++)
{
var effect = _Effects[i];
//Update variables of the effect via its deltas
effect.Update(frameTime);
//These effects have died
if (effect.Age > effect.Deathtime)
// Effects are purely visual, so they don't need to be ran through prediction.
// once CurTime ever passes DeathTime (clients render the top at IsFirstTimePredicted, where this happens) just remove them.
if (curTime > effect.Deathtime)
{
//Remove from the effects list and decrement the iterator
_Effects.Remove(effect);
i--;
}
else
{
//Update variables of the effect via its deltas
effect.Update(frameTime);
}
}
}
@@ -205,14 +202,9 @@ namespace Robust.Client.GameObjects
public bool Shaded = true;
/// <summary>
/// Effect's age -- from 0f
/// CurTime after which the effect will "die"
/// </summary>
public TimeSpan Age = TimeSpan.Zero;
/// <summary>
/// Time after which the effect will "die"
/// </summary>
public TimeSpan Deathtime = TimeSpan.FromSeconds(1);
public TimeSpan Deathtime;
private readonly IMapManager _mapManager;
private readonly IEntityManager _entityManager;
@@ -245,8 +237,6 @@ namespace Robust.Client.GameObjects
RadialAcceleration = effectcreation.RadialAcceleration;
TangentialVelocity = effectcreation.TangentialVelocity;
TangentialAcceleration = effectcreation.TangentialAcceleration;
Age = effectcreation.Born;
Deathtime = effectcreation.DeathTime;
Rotation = effectcreation.Rotation;
RotationRate = effectcreation.RotationRate;
Size = effectcreation.Size;
@@ -260,10 +250,6 @@ namespace Robust.Client.GameObjects
public void Update(float frameTime)
{
Age += TimeSpan.FromSeconds(frameTime);
if (Age >= Deathtime)
return;
Velocity += Acceleration * frameTime;
RadialVelocity += RadialAcceleration * frameTime;
TangentialVelocity += TangentialAcceleration * frameTime;

View File

@@ -1,8 +1,8 @@
using System;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Graphics;
using Robust.Client.Physics;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.IoC;
@@ -10,7 +10,7 @@ using Robust.Shared.Maths;
#nullable enable
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
/// <summary>
/// Updates the position of every Eye every frame, so that the camera follows the player around.
@@ -87,7 +87,7 @@ namespace Robust.Client.GameObjects.EntitySystems
}
}
foreach (var eyeComponent in EntityManager.ComponentManager.EntityQuery<EyeComponent>())
foreach (var eyeComponent in EntityManager.ComponentManager.EntityQuery<EyeComponent>(true))
{
eyeComponent.UpdateEyePosition();
}

View File

@@ -1,19 +1,15 @@
using System;
using Robust.Client.GameObjects.Components;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Input;
using Robust.Client.GameStates;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Players;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
/// <summary>
/// Client-side processing of all input commands through the simulation.
@@ -44,7 +40,7 @@ namespace Robust.Client.GameObjects.EntitySystems
/// <param name="message">Arguments for this event.</param>
/// <param name="replay">if true, current cmd state will not be checked or updated - use this for "replaying" an
/// old input that was saved or buffered until further processing could be done</param>
public bool HandleInputCommand(ICommonSession session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
{
#if DEBUG
@@ -128,7 +124,8 @@ namespace Robust.Client.GameObjects.EntitySystems
if (!entity.TryGetComponent(out InputComponent? inputComp))
{
Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity.Uid}, entProto={entity.Prototype}");
Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity.Uid}, entProto={entity.Prototype}. Setting default \"{InputContextContainer.DefaultContextName}\" context...");
inputMan.Contexts.SetActiveContext(InputContextContainer.DefaultContextName);
return;
}
@@ -138,7 +135,8 @@ namespace Robust.Client.GameObjects.EntitySystems
}
else
{
Logger.ErrorS("input.context", $"Unknown context: entId={entity.Uid}, entProto={entity.Prototype}, context={inputComp.ContextName}");
Logger.ErrorS("input.context", $"Unknown context: entId={entity.Uid}, entProto={entity.Prototype}, context={inputComp.ContextName}. . Setting default \"{InputContextContainer.DefaultContextName}\" context...");
inputMan.Contexts.SetActiveContext(InputContextContainer.DefaultContextName);
}
}

View File

@@ -1,8 +1,8 @@
using Robust.Client.Audio.Midi;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
public class MidiSystem : EntitySystem
{

View File

@@ -1,16 +1,13 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Client.Physics;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Robust.Client.GameObjects.EntitySystems
namespace Robust.Client.GameObjects
{
/// <summary>
/// Keeps track of <see cref="DynamicTree{T}"/>s for various rendering-related components.

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