Compare commits

..

205 Commits

Author SHA1 Message Date
ElectroJr
60f18d5f36 Version: 0.9.3.0 2022-04-05 18:06:43 +12:00
mirrorcult
e72d3de256 Local event for BUI opening (#2687) 2022-04-05 15:52:02 +10:00
Moony
ba9846b9c4 Fix vector2 CVars (#2686) 2022-04-05 15:03:10 +10:00
Paul
09586284dc Version: 0.9.2.0 2022-04-04 20:28:53 +02:00
Paul
a1ee4374b2 add a check for major version == 0 2022-04-04 20:28:26 +02:00
Paul Ritter
4de6f25f11 updates version-script to use the new version format (#2683) 2022-04-04 20:25:12 +02:00
Paul Ritter
582d8a5587 pvsrange vec2 + eyezoom (#2676)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
2022-04-04 20:20:13 +02:00
Leon Friedrich
ec53b04f99 Add NotNullWhen attribute to TryComp (#2681) 2022-04-04 11:29:02 +02:00
metalgearsloth
950fc94408 Physicsmap tweaks (#2663) 2022-04-04 17:10:15 +10:00
Leon Friedrich
58d12e6e09 Remove Component.OnAdd() (#2660) 2022-04-04 16:11:47 +10:00
ElectroJr
94323005c4 Version: 0.9.1 2022-04-04 17:43:01 +12:00
metalgearsloth
4989842057 Remove IgnorePause (#2649) 2022-04-04 15:41:38 +10:00
Paul
80172636a8 version 0.9 - serv3 refactor 2022-04-03 02:00:41 +02:00
Paul Ritter
8491f7be24 New Serv3 api just dropped (#2605)
Co-authored-by: Paul Ritter <ritter.paul1@gmail.com>
2022-04-03 01:59:48 +02:00
Pieter-Jan Briers
de8c2c14bb Replace Lidgren encryption stuff with Libsodium (#2646) 2022-04-02 16:24:04 +02:00
mirrorcult
baebfff321 Merge pull request #2673 from ElectroJr/fix-description 2022-04-01 17:02:43 -07:00
Paul
24191404aa version 0.8.86 2022-04-02 01:28:12 +02:00
Paul Ritter
990842f5c2 fixes pvs dirty crash (#2674) 2022-04-02 06:09:14 +11:00
ElectroJr
2c2bd3f330 Version: 0.8.85 2022-04-02 00:38:07 +13:00
Leon Friedrich
1ae14c4bfa Add error tolerance to physics map (#2672) 2022-04-01 22:07:27 +11:00
metalgearsloth
61a1701dc9 Fix PVS take 3 (#2671) 2022-04-01 22:04:30 +11:00
ElectroJr
c6ea11346e fix entity description proxy method. 2022-04-02 00:02:58 +13:00
mirrorcult
ad7c871e28 Make effects properly clean up after itself (#2670) 2022-04-01 13:00:54 +11:00
metalgearsloth
c77b3f8022 Version: 0.8.84 2022-04-01 10:10:20 +11:00
metalgearsloth
b263f4a1df Fix PVS crash 2 (#2668)
Co-authored-by: Paul <ritter.paul1@googlemail.com>
2022-04-01 10:09:31 +11:00
Paul
1a11d41bba disable benchmarks workflow 2022-03-31 17:06:48 +02:00
Leon Friedrich
14d0a77644 Make directional sprite matrices static (#2661) 2022-04-01 00:11:17 +11:00
ElectroJr
b75045ce1a Version: 0.8.83 2022-03-31 15:25:31 +13:00
Leon Friedrich
1d8a8e15ef Add a is-In-container metadata flag. (#2585) 2022-03-31 13:23:33 +11:00
Leon Friedrich
28a087c267 Add GetEntitiesIntersectingBody (#2641) 2022-03-31 11:52:35 +11:00
Jane Lewis
8dc849b6bb Cache Sprite AABBs (#2622) 2022-03-31 11:52:00 +11:00
mirrorcult
7e56f46f35 Remove network component messages (#2659) 2022-03-31 10:24:27 +11:00
metalgearsloth
4453a23069 Version: 0.8.82 2022-03-30 23:34:50 +11:00
metalgearsloth
57be0bc845 Fix nullability (#2658) 2022-03-30 23:34:31 +11:00
Paul Ritter
430910ff36 sudo the benchmarks to elevate them to high prio 2022-03-30 14:10:13 +02:00
Paul Ritter
4c95807e2d how 2022-03-30 14:08:53 +02:00
metalgearsloth
92fd04552d Version: 0.8.81 2022-03-30 22:58:42 +11:00
Leon Friedrich
9c7e244821 ECS collision wake (#2656) 2022-03-30 22:57:57 +11:00
Vera Aguilera Puerto
41f64b5c3c Add overload to IntersectRayWithPredicate. (#2650) 2022-03-30 22:57:40 +11:00
Paul Ritter
4003003580 caching trees in pvs (#2652)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2022-03-30 22:57:31 +11:00
Paul Ritter
f49341b53c im a dummy 2022-03-30 11:16:09 +02:00
Leon Friedrich
61d9d9a832 Make the UI System TryGet functions not log failed resolves (#2657) 2022-03-30 16:37:34 +11:00
Paul
c0b7dd053f working on benchmarks workflow 2022-03-30 00:31:51 +02:00
Paul
f642e10acc versionbump to test benchmarks 2022-03-30 00:25:50 +02:00
Paul Ritter
2fb64480db benchmark workflow & storing it in db (#2653) 2022-03-29 21:43:32 +02:00
metalgearsloth
075b5ec203 Kill local comp messages (#2648) 2022-03-29 23:37:58 +11:00
metalgearsloth
21f2fe6b1b Minor spawn opt (#2651) 2022-03-29 12:34:42 +02:00
metalgearsloth
89080ef7c7 Version: 0.8.79 2022-03-29 18:40:32 +11:00
metalgearsloth
f5d36dc9ca Fix physics parenting bug (#2654) 2022-03-29 18:39:49 +11:00
wrexbe
ef87610b36 Map loader saving change (#2638)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2022-03-28 16:56:16 +11:00
mirrorcult
c89f1e4d31 Paramless ctor for joints (#2647)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2022-03-28 16:31:04 +11:00
metalgearsloth
c4805d89e0 Version: 0.8.78 2022-03-27 17:34:43 +11:00
Vera Aguilera Puerto
659ffb028a Dispose of SequencerEvents in MidiRenderer.
Oops!
2022-03-27 00:03:07 +01:00
metalgearsloth
a14c956ccc Nuke EntMapIdChangedMessage (#2621)
Co-authored-by: metalgearsloth <metalgearsloth@gmail.com>
2022-03-26 18:14:56 +01:00
metalgearsloth
facb3914a4 Fix joints crash (#2609) 2022-03-26 18:13:56 +01:00
Vera Aguilera Puerto
aab8f7876b Benchmarks for EntityManager (#2578) 2022-03-26 17:55:00 +01:00
Pieter-Jan Briers
1f199ea71c Add ITaskManager.BlockWaitOnTask()
Smugleaf needed this.

Also changed RobustSynchronizationContext to use channels instead of ConcurrentQueue internally.
2022-03-26 17:02:05 +01:00
metalgearsloth
00b557b77a Struct enumerator for anchored entities (#2626) 2022-03-26 12:53:25 +01:00
metalgearsloth
eac536cffe overlay allocs (#2642) 2022-03-26 12:48:11 +01:00
DrSmugleaf
bf75c6f247 Add IEntitySystemManager.GetEntitySystemOrNull (#2643) 2022-03-26 16:35:09 +11:00
metalgearsloth
1518c79291 Struct enumerator for GetAllGrids (#2624) 2022-03-26 03:17:42 +11:00
metalgearsloth
aae2f72d1a PVS pooling 4 (#2640) 2022-03-25 11:51:59 +01:00
Jane Lewis
273aa3d6ea Show player rotation in debug overlay (#2631) 2022-03-25 18:12:53 +11:00
metalgearsloth
756c4510eb Reduce prediction alloc (#2639) 2022-03-25 18:10:31 +11:00
Paul
cd50e89aec v0.8.77 2022-03-24 23:51:55 +01:00
metalgearsloth
25b648b929 Reduce PVS allocs a bunch (again (again)) (#2630) 2022-03-24 23:48:37 +01:00
metalgearsloth
1dbbb08250 Reduce server allocs a bunch (#2625) 2022-03-24 23:39:52 +01:00
DrSmugleaf
5432f7311e Fix keyboard focused being released and grabbed again immediately afterwards (#2634) 2022-03-24 15:25:25 +01:00
Zoldorf
e662802b4c Update build-docfx.yml 2022-03-23 17:32:12 -06:00
Vera Aguilera Puerto
bae2c390bb Improve Color.FromHex exception message.
It now includes the invalid hexcode argument.
Something in content kept getting these exceptions on prod and knowing the invalid argument would be very helpful when debugging these in the future.
2022-03-23 16:00:28 +01:00
Leon Friedrich
7976926eaf fix sequence node Except() (#2610)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2022-03-23 23:34:21 +11:00
metalgearsloth
4aee730753 Version: 0.8.76 2022-03-23 20:48:42 +11:00
metalgearsloth
e87c342aa2 inline robusttree pool (#2628) 2022-03-23 20:47:53 +11:00
Paul
d335d74d8f adds maxpoolsize to treepool 2022-03-22 18:02:00 +01:00
metalgearsloth
bc68b0e3ec Version: 0.8.75 2022-03-22 13:57:31 +11:00
metalgearsloth
b1eadbc58f Fix PVS allocation issues (#2623) 2022-03-22 13:55:57 +11:00
Silver
2d3165a16f remove inheritedmembers div 2022-03-21 18:02:35 -06:00
Zoldorf
d99d25de37 Create build-docfx.yml 2022-03-21 17:22:46 -06:00
Zoldorf
be035ab002 :mistake: 2022-03-21 17:22:08 -06:00
Zoldorf
12c8883eea Update build-test.yml 2022-03-21 15:36:41 -06:00
Zoldorf
758c7c1869 Update build-test.yml 2022-03-21 15:28:12 -06:00
Zoldorf
72ab1f5a1a Update build-test.yml 2022-03-21 15:25:40 -06:00
Zoldorf
6b9c1369c0 Update build-test.yml 2022-03-21 15:22:32 -06:00
Zoldorf
fd1f1dc64a Delete build-docfx.yml 2022-03-21 15:12:24 -06:00
Zoldorf
e211368bc4 Update build-docfx.yml 2022-03-21 15:07:41 -06:00
Zoldorf
39307f916d build-docfx.yml 2022-03-21 15:04:24 -06:00
metalgearsloth
7378cc50a1 Version: 0.8.74 2022-03-21 20:09:16 +11:00
Silver
867b9ad932 Adds Docfx to RobustToolbox 2022-03-20 23:08:59 -06:00
Paul Ritter
03064255f6 Makes Robusttree Children nullable to save allocations (#2570) 2022-03-21 03:02:52 +11:00
Pieter-Jan Briers
c9ccd0b873 Fix file dialogs crashing on macOS 2022-03-20 16:47:20 +01:00
metalgearsloth
650eceea7e Remove IoC + GetComp for every tile intersection (#2617) 2022-03-20 16:54:05 +11:00
DrSmugleaf
452ad5a6e5 Version: 0.8.73 2022-03-19 19:41:22 +01:00
Leon Friedrich
7c292c75d4 Add more information to reflection errors. (#2613) 2022-03-20 00:11:42 +11:00
DrSmugleaf
cab4113697 Add myself to CODEOWNERS 2022-03-19 11:27:54 +01:00
metalgearsloth
f1a5de79b5 Change entitylookup bounds to method (#2611) 2022-03-19 16:14:24 +11:00
metalgearsloth
191e80c175 Add EntityQuery support to filters (#2608) 2022-03-19 15:54:13 +11:00
Leon Friedrich
241bce56c8 Add Resolve() to component queries (#2607) 2022-03-19 15:53:39 +11:00
metalgearsloth
b105639b31 More transform ECS methods (#2606) 2022-03-18 10:32:49 +11:00
ElectroJr
419e63ecd5 Version: 0.8.72 2022-03-16 15:32:19 +13:00
metalgearsloth
46e632bf9d Fix effect overlay crash (#2604) 2022-03-15 18:05:13 +11:00
Leon Friedrich
a8871f4ff4 Add an assert to positional audio (#2602) 2022-03-15 14:54:50 +11:00
Paul
65f28c023d also write + bugfix + ratio 2022-03-15 00:43:40 +01:00
Paul
e506f6aba2 error for invalid customtypeserializer 2022-03-15 00:36:05 +01:00
metalgearsloth
93ae74b5a7 Update nuget dependencies (#2581) 2022-03-14 08:57:20 +01:00
Kara D
7b25fa98ee Version: 0.8.71 2022-03-13 19:30:44 -07:00
mirrorcult
e320b7abb4 Merge pull request #2599 from Sanctuary-Station/staging 2022-03-13 19:26:16 -07:00
Acruid
6b9b56ca83 Grid Components Allocate Grids (#2597)
* Deleting a map is now routed through MapComponent deletion.

* Remove map and grid deletions from map networking, just delete the entity if you want to remove the map.

* Moved the chunkSize property of created grids from the networked MapData to the MapGridComponent state.

* Remove unused IMapManager.DefaultMap property.

* Removed MapCreationTick field from MapManager.MapCollection.

* Removed _maps hashset field from MapManager.

* Removed CreatedMaps array from network MapData.

* MapGrid.ParentMapId is now derived from the bound TransformComponent, and isn't required in the MapGrid ctor.
Removed MapGrid.CreatedTick, it can be found on MapGridComponent.CreationTick.

* Remove a bunch of ApplyGameStatePre code duplication.

* Completely refactored CreateGrid.

* Adds AddComponentUninitialized to the ECS system. This allows you to access a component before it is initialized in a using block.

* Use AddComponentUninitialized to allocate a grid after the component is allocated.

* MapLoader now creates the grids after creating the map entities.

* Chunksize and TileSize properties are now actually read out of the map yaml.
TileSize has a public Setter.

* Minor cleanup.

* Moved grid allocation onto the MapGridComponent.

* Final Cleanup.

* Merge Fail.

* Fixed test, grid was getting deleted because it was empty.

* Remove DeletedChunkDatum from grid networking.

* ApplyMapGridState moved from map manager to the MapGridComponent.
2022-03-13 18:28:59 -07:00
Jezithyr
4d19790b3d This should fix the font caching issue? 2022-03-13 16:51:09 -07:00
Jezithyr
2027863d4c Addressing PR Feedback 2022-03-13 15:59:32 -07:00
Vera Aguilera Puerto
4a4444a1ee Adds MemoryContentRoot. (#2590) 2022-03-13 23:52:34 +01:00
Jezithyr
f10316d384 Fixing missing semicolons 2022-03-13 15:23:45 -07:00
Jezithyr
778be40c86 Condensing code 2022-03-13 14:43:24 -07:00
Jesse Rougeau
2046323c71 Update Robust.Client/UserInterface/UserInterfaceManager.cs
More condensing

Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com>
2022-03-13 13:06:02 -07:00
Jesse Rougeau
71bb5cd58e Update Robust.Client/UserInterface/UserInterfaceManager.cs
Condensing sizing logic

Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com>
2022-03-13 13:05:30 -07:00
Jezithyr
71c6e437fa Removing debug and fixing indentation 2022-03-12 20:36:23 -07:00
Jezithyr
26e1474179 Implemented auto UI Scaling :D
- Implemented autoUI scaling, scaling supports down to 520 by 520 pixel screens(Idk why you'd even have this)
- UI scaling can be adjusted by overriding the window root class
2022-03-12 20:31:13 -07:00
Kara D
89a7459fda Update .NET version for build-test workflow
This should hopefully make it catch CS8983
2022-03-12 19:06:23 -07:00
Paul Ritter
a61adf5631 makes budgets clientside & replicated (#2593) 2022-03-13 00:06:40 +11:00
Leon Friedrich
97db1712f3 Fix grid traversal velocities (#2594)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2022-03-12 23:57:30 +11:00
Paul
ea7012d114 version 0.8.70 2022-03-09 20:58:17 +01:00
Moony
d3eaea5697 Add support for tile variants. (#2577) 2022-03-09 20:56:58 +01:00
metalgearsloth
4ca3fbe4bf Version: 0.8.69 2022-03-10 00:29:08 +11:00
Vera Aguilera Puerto
81fea7595a AttachToGridOrMap checks if the grid/map is being terminated/is deleted. (#2592) 2022-03-10 00:27:18 +11:00
metalgearsloth
2e0806cc1f Version: 0.8.68 2022-03-08 15:48:39 +11:00
metalgearsloth
5325650e92 Fix disappearing grids (#2587)
Co-authored-by: metalgearsloth <metalgearsloth@gmail.com>
2022-03-08 12:25:03 +11:00
Acruid
31764d3c2d Mark C# events on MapManager as Obsolete (#2586)
* Obsoleted C# grid events from MapManager.GridCollection. Use the ECS EventBus events instead.

* Obsoleted C# events from MapManager.MapCollection. Use the ECS EventBus events instead.
2022-03-07 15:51:00 -08:00
Leon Friedrich
9df5152610 Decrease error tolerance for angular velocity updates (#2584) 2022-03-06 23:55:56 +11:00
metalgearsloth
c6cd780ab5 Remove funky merge artifact 2022-03-06 18:26:02 +11:00
Vera Aguilera Puerto
023f845c48 SpriteComponent exception specifies failing RSI path, if any. 2022-03-03 21:29:01 +01:00
Vera Aguilera Puerto
5e977da9bc Fix BaseServer.Shutdown bug when not specifying a reason.
If a shutdown was requested before the main loop had been started, it would only work if the reason was non-null. This is because `string? _shutdownReason` essentially acts as a shutdown request flag with its nullability.
2022-03-03 15:41:44 +01:00
metalgearsloth
fa1de04df7 Version: 0.8.67 2022-03-03 21:18:00 +11:00
metalgearsloth
33251222cd EntityLookup as a system (#2573) 2022-03-03 21:17:01 +11:00
Kara D
70c1e8680f Version: 0.8.66 2022-03-01 15:06:19 -07:00
Kara D
0d214769e6 vs2022 fix 2022-03-01 15:05:49 -07:00
Paul
495bbe891a obsoletes serhooks 2022-03-01 21:49:24 +01:00
Rember
5f68f569c3 BaseContainer.Insert() will now reset the entity's rotation after res… (#2576) 2022-03-01 23:23:56 +11:00
Leon Friedrich
52c883cb1f Fix lsmap and lsgrid (#2574) 2022-03-01 20:20:03 +11:00
metalgearsloth
4b20bae597 RobustUnitTest tweaks (#2568) 2022-03-01 13:35:32 +11:00
Leon Friedrich
64ff046ceb loadmap command tweak (#2572)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2022-02-28 21:17:58 +11:00
Kara D
5f2566a97d Version: 0.8.65 2022-02-27 12:59:54 -07:00
Kara D
c3735d1d5e vs2022 moment 2022-02-27 12:59:25 -07:00
metalgearsloth
749e547773 Version: 0.8.64 2022-02-28 00:45:53 +11:00
metalgearsloth
af8a010f43 Optimise PVS cangetcompstate (#2560) 2022-02-28 00:45:16 +11:00
metalgearsloth
a8a29e814f Allow content to override occlusion directions (#2528) 2022-02-27 23:15:45 +11:00
metalgearsloth
c3cb5406f6 Fix culling disabled (#2485)
Co-authored-by: metalgearsloth <metalgearsloth@gmail.com>
2022-02-27 22:59:36 +11:00
mirrorcult
73f26d93ca Merge pull request #2565 from ElectroJr/mapping-actions 2022-02-26 20:06:48 -07:00
metalgearsloth
5e86a99060 Version: 0.8.63 2022-02-27 13:00:41 +11:00
Paul Ritter
ce9070b966 changes pvs chunkorder to use a tree-structure, should fix children being sent without parents (#2562)
Co-authored-by: Paul Ritter <ritter.paul1@gmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2022-02-27 13:00:10 +11:00
Vera Aguilera Puerto
aa5bcefaf2 Add System.Threading.Monitor (lock keyword) to sandbox whitelist. 2022-02-26 16:50:41 +01:00
ElectroJr
2df267fc28 Merge remote-tracking branch 'upstream/master' into mapping-actions 2022-02-24 04:13:45 +13:00
ElectroJr
306fcce337 remove todo 2022-02-24 04:05:04 +13:00
ElectroJr
3683b66ef8 Allow SpriteSpecifier to load from entity prototype 2022-02-23 23:37:05 +13:00
Acruid
08a52fb892 MapChunk Cleanup (#2555)
* All IPhysShapes now expose a property to get the local AABB.

* Removed IMapChunk. It's internal, we only have 1 implementation in the engine, no need for abstraction, and removing it helps perf.

* Cleaned up issues in MapChunk file.

* Encapsulate _tiles access inside MapChunk.

* Remove IEnumerable<TileRef> from MapChunk.

* Remove CollidesWithChunk

* Move CalcWorldAABB and RegenerateCollision from MapChunk to MapGrid.
Remove MapChunk.GridId.

* Removed MapChunk.GetAllTiles

* Removed the terrible mocked unit tests.

* Moved the GetTileRef functions from MapChunk to MapGrid.

* Add an event raised on MapChunk when a tile is modified.
Completely remove the IMapGrid dependency from MapChunk.

* Fix bug where you cannot change the tile damage of a tile.
2022-02-21 20:49:30 -08:00
Pieter-Jan Briers
fd3c54b373 Seal some classes. 2022-02-21 14:02:34 +01:00
Pieter-Jan Briers
adee05b009 Make client start with non-seekable streams.
Not marking #2439 as fixed yet due to lack of thorough testing.
2022-02-21 11:23:37 +01:00
Pieter-Jan Briers
a66d40eb19 CVar to force ResourceManager to provide seekability for streams.
To help with #2439
2022-02-21 11:23:37 +01:00
metalgearsloth
d065a96e01 Don't serialize grid fixtures (#2477) 2022-02-21 20:15:30 +11:00
Kara D
ef7709aa78 Version: 0.8.62 2022-02-20 19:34:25 -07:00
Paul Ritter
305b330075 bandaid 2022-02-21 02:30:03 +01:00
Acruid
bec4297ce1 Move Map Pause data from MapManager to MapComponent. (#2543)
* Encapsulated existing _pausedMaps access in MapManager.Pause.
Added more unit tests.

* Moved the MapPaused flag from MapManager.Pause to MapComponent.

* Moved the MapPreInit flag from MapManager.Pause to MapComponent.

* Changed visibility so content can't access the flags directly.

* It's not the code that is wrong, it's the tests that are wrong!

* Removed completely obsolete bookkeeping event.
2022-02-20 13:36:45 -08:00
metalgearsloth
18b21b3d60 Version: 0.8.61 2022-02-20 17:44:49 +11:00
mirrorcult
9b263417b9 Merge pull request #2541 from metalgearsloth/2022-02-14_1 2022-02-19 12:33:46 -07:00
Acruid
13da0a7925 Adds command buffer to client, and input command injection (#2538)
Adds the command buffer to the console.
adds wait <ticks> command to console.
Adds the `incmd` command to inject input commands directly into the simulation, bypassing the frontend.
Removes client side permission check for debug builds.
2022-02-18 19:27:38 -08:00
Kara D
bb28db2412 Version: 0.8.60 2022-02-18 15:50:15 -07:00
mirrorcult
489d150ae0 Merge pull request #2558 from PaulRitter/2022_02_18_real_pvs_hours 2022-02-18 15:49:20 -07:00
Paul Ritter
077dbaf933 yo this slaps 2022-02-18 20:49:03 +01:00
Paul Ritter
26f83ac7a2 bored trainride 2022-02-17 15:08:55 +01:00
metalgearsloth
822009b429 Version: 0.8.59 2022-02-17 18:05:50 +11:00
Leon Friedrich
e07a4e516c Add random vector functions to IRobustRandom (#2551) 2022-02-17 18:05:12 +11:00
metalgearsloth
9ec38b5538 Optimise PVS recursion (#2524) 2022-02-17 18:03:14 +11:00
metalgearsloth
97da770978 More physics ECS (#2529) 2022-02-17 17:52:27 +11:00
metalgearsloth
3acbc8235d Funny struct enumerator for xform kids Part 2 (#2492) 2022-02-17 17:19:08 +11:00
Sam Weaver
a32359d5d4 Only include shadow casting lights in the "max lights per scene" count (#2446)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2022-02-17 14:24:36 +11:00
Paul
78fd39aee2 could fix a racecondition? maybe? 2022-02-16 23:55:04 +01:00
Kara D
fca5c14c67 Version: 0.8.58 2022-02-16 15:53:10 -07:00
Kara D
f60e3a14ef Fix VS2022 17.1 issue 2022-02-16 15:49:19 -07:00
Leon Friedrich
503a7032f9 Hopefully fix appearance component mispredict reconciliation (#2460) 2022-02-16 23:26:13 +01:00
metalgearsloth
918bbd3f01 Version: 0.8.57 2022-02-17 01:06:34 +11:00
Leon Friedrich
a1441d5051 Properly support sprite layer transforms. (#2533) 2022-02-17 00:06:14 +11:00
Leon Friedrich
58d6189c40 Allow sprite layer data to be set using PrototypeLayerData. (#2442) 2022-02-16 19:37:40 +11:00
Leon Friedrich
58defed1d2 Fix InteractionOutline 2: Electric boogaloo (#2511)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2022-02-16 18:32:40 +11:00
Paul
27a94384d0 v0.8.56 2022-02-15 18:16:07 +01:00
Paul Ritter
4b8f5815db pvs caching (#2547)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2022-02-15 18:14:44 +01:00
metalgearsloth
d830eef435 Version: 0.8.55 2022-02-15 21:16:35 +11:00
metalgearsloth
e7c4bf7341 Don't use worldbounds for PVS (#2473) 2022-02-15 21:07:41 +11:00
ike709
162e646404 Fix a weird issue with config (#2546)
Co-authored-by: ike709 <ike709@github.com>
2022-02-15 20:57:32 +11:00
metalgearsloth
93049fcacf More transform optimisations (#2519) 2022-02-15 20:43:49 +11:00
metalgearsloth
5b90db4beb inline 2022-02-14 18:30:33 +11:00
metalgearsloth
7692ff736b Some more ECS stuff
- Proxy methods on EntityManager
- Transform system stuff
2022-02-14 18:24:52 +11:00
Kara D
f25ad8ece9 Version: 0.8.54 2022-02-13 20:25:00 -07:00
mirrorcult
1aaf3b9250 Merge pull request #2521 from ElectroJr/public-AppearanceSystem 2022-02-13 20:24:23 -07:00
mirrorcult
11f6cff6df Merge pull request #2539 from Acruid/vv_fix_entName 2022-02-13 20:01:53 -07:00
mirrorcult
c2ea57c95a Merge pull request #2503 from Ygg01/linguini-multiline-error-fix 2022-02-13 20:00:47 -07:00
Ygg01
f61ae5da6b Update Linguini and line info in errors 2022-02-14 01:50:11 +01:00
Acruid
8ae9a5f2da Fix a typo where setting the description of an entity in VV sets the name. 2022-02-13 16:35:51 -08:00
Ygg01
3f7e89e006 Merge remote-tracking branch 'origin/master' into linguini-multiline-error-fix 2022-02-13 23:35:28 +01:00
Ygg01
cee4e4d62e Enable Fluent logs for only .ftl files (#2532) 2022-02-12 21:59:08 +11:00
metalgearsloth
14a3783760 Version: 0.8.53 2022-02-12 15:56:21 +11:00
mirrorcult
b4607f7b1f Draw effects below FOV (#2534) 2022-02-12 15:54:50 +11:00
Acruid
5a28c16cae Map Pausing Fixes (#2520) 2022-02-12 15:54:03 +11:00
ElectroJr
9e8bf861ea Merge remote-tracking branch 'upstream/master' into public-AppearanceSystem 2022-02-11 20:43:19 +13:00
ElectroJr
4cfb9210d0 Make AppearanceSystem public 2022-02-09 04:55:31 +13:00
Ygg01
681f77a796 Change newline append 2022-02-04 02:01:52 +01:00
Ygg01
2a7f1cbf48 Ensure that tests work regardless of platform 2022-02-03 04:06:53 +01:00
Ygg01
c4e63cfdc7 Fix multiline errors, add some tests 2022-02-03 01:43:45 +01:00
380 changed files with 11675 additions and 7217 deletions

2
.github/CODEOWNERS vendored
View File

@@ -11,7 +11,7 @@
/Robust.Analyzers @PaulRitter
/Robust.*/GameStates @PaulRitter
/Robust.Shared/Analyzers @PaulRitter
/Robust.*/Serialization @PaulRitter
/Robust.*/Serialization @PaulRitter @DrSmugleaf
/Robust.*/Prototypes @PaulRitter
/Robust.Shared/GameObjects/ComponentDependencies @PaulRitter
/Robust.*/Containers @PaulRitter

33
.github/workflows/benchmarks.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Benchmarks
#on:
# push
#schedule:
# - cron: '0 5 * * *'
#push:
# tags:
# - 'v*'
env:
ROBUST_BENCHMARKS_ENABLE_SQL: 1
ROBUST_BENCHMARKS_SQL_ADDRESS: ${{ secrets.BENCHMARKS_WRITE_ADDRESS }}
ROBUST_BENCHMARKS_SQL_PORT: ${{ secrets.BENCHMARKS_WRITE_PORT }}
ROBUST_BENCHMARKS_SQL_USER: ${{ secrets.BENCHMARKS_WRITE_USER }}
ROBUST_BENCHMARKS_SQL_PASSWORD: ${{ secrets.BENCHMARKS_WRITE_PASSWORD }}
ROBUST_BENCHMARKS_SQL_DATABASE: benchmarks
jobs:
benchmark:
name: Run Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Install dependencies
run: dotnet restore
- name: Run benchmark
run: cd Robust.Benchmarks && sudo dotnet run --filter '*' --configuration Release

34
.github/workflows/build-docfx.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Build & Publish DocFX
on:
schedule:
- cron: "0 0 * * 0"
jobs:
docfx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Install dependencies
run: dotnet restore
- name: Build Project
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Build DocFX
uses: nikeee/docfx-action@v1.0.0
with:
args: Robust.Docfx/docfx.json
- name: Publish Docfx Documentation on GitHub Pages
uses: maxheld83/ghpages@master
env:
BUILD_DIR: Robust.Docfx/_robust-site
GH_PAT: ${{ secrets.GH_PAT }}

View File

@@ -22,10 +22,12 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.100
dotnet-version: 6.0.x
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Test Engine
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -v n

2
.gitignore vendored
View File

@@ -76,3 +76,5 @@ MSBuild/Robust.Custom.targets
release/
Robust.Docfx/*-site
Robust.Docfx/api

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

View File

@@ -6,9 +6,9 @@
</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" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Globalization;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Order;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Validators;
using Robust.Benchmarks.Exporters;
namespace Robust.Benchmarks.Configs;
public sealed class DefaultSQLConfig : IConfig
{
public static readonly IConfig Instance = new DefaultSQLConfig();
private DefaultSQLConfig(){}
public IEnumerable<IExporter> GetExporters()
{
yield return SQLExporter.Default;
}
public IEnumerable<IColumnProvider> GetColumnProviders() => DefaultConfig.Instance.GetColumnProviders();
public IEnumerable<ILogger> GetLoggers() => DefaultConfig.Instance.GetLoggers();
public IEnumerable<IDiagnoser> GetDiagnosers() => DefaultConfig.Instance.GetDiagnosers();
public IEnumerable<IAnalyser> GetAnalysers() => DefaultConfig.Instance.GetAnalysers();
public IEnumerable<Job> GetJobs() => DefaultConfig.Instance.GetJobs();
public IEnumerable<IValidator> GetValidators() => DefaultConfig.Instance.GetValidators();
public IEnumerable<HardwareCounter> GetHardwareCounters() => DefaultConfig.Instance.GetHardwareCounters();
public IEnumerable<IFilter> GetFilters() => DefaultConfig.Instance.GetFilters();
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => DefaultConfig.Instance.GetLogicalGroupRules();
public IOrderer Orderer => DefaultConfig.Instance.Orderer!;
public SummaryStyle SummaryStyle => DefaultConfig.Instance.SummaryStyle;
public ConfigUnionRule UnionRule => DefaultConfig.Instance.UnionRule;
public string ArtifactsPath => DefaultConfig.Instance.ArtifactsPath;
public CultureInfo CultureInfo => DefaultConfig.Instance.CultureInfo!;
public ConfigOptions Options => DefaultConfig.Instance.Options;
}

View File

@@ -0,0 +1,54 @@
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
[Virtual]
public class AddRemoveComponentBenchmark
{
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{
_entityManager.SpawnEntity(null, coords);
}
}
[Benchmark]
public void AddRemoveComponent()
{
for (var i = 2; i <= N+1; i++)
{
var uid = new EntityUid(i);
_entityManager.AddComponent<A>(uid);
_entityManager.RemoveComponent<A>(uid);
}
}
[ComponentProtoName("A")]
public sealed class A : Component
{
}
}

View File

@@ -0,0 +1,61 @@
using System;
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
[Virtual]
public class GetComponentBenchmark
{
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
public A[] Comps = default!;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
Comps = new A[N+2];
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, coords);
_entityManager.AddComponent<A>(uid);
}
}
[Benchmark]
public A[] GetComponent()
{
for (var i = 2; i <= N+1; i++)
{
Comps[i] = _entityManager.GetComponent<A>(new EntityUid(i));
}
// Return something so the JIT doesn't optimize out all the GetComponent calls.
return Comps;
}
[ComponentProtoName("A")]
public sealed class A : Component
{
}
}

View File

@@ -0,0 +1,62 @@
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
[Virtual]
public class SpawnDeleteEntityBenchmark
{
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
private MapCoordinates _mapCoords = MapCoordinates.Nullspace;
private EntityCoordinates _entCoords = EntityCoordinates.Invalid;
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
_mapCoords = new MapCoordinates(0, 0, new MapId(1));
var uid = _simulation.AddMap(_mapCoords.MapId);
_entCoords = new EntityCoordinates(uid, 0, 0);
}
[Benchmark(Baseline = true)]
public void SpawnDeleteEntityMapCoords()
{
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, _mapCoords);
_entityManager.DeleteEntity(uid);
}
}
[Benchmark]
public void SpawnDeleteEntityEntCoords()
{
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, _entCoords);
_entityManager.DeleteEntity(uid);
}
}
[ComponentProtoName("A")]
public sealed class A : Component
{
}
}

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Mathematics;
using BenchmarkDotNet.Reports;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Npgsql;
namespace Robust.Benchmarks.Exporters;
public sealed class SQLExporter : IExporter
{
public static readonly IExporter Default = new SQLExporter();
private SQLExporter(){}
public void ExportToLog(Summary summary, ILogger logger)
{
Export(summary, logger);
}
public IEnumerable<string> ExportToFiles(Summary summary, ILogger consoleLogger)
{
Export(summary, consoleLogger);
return Array.Empty<string>();
}
private bool TryGetEnvironmentVariable(string name, ILogger logger, [NotNullWhen(true)] out string? value)
{
value = Environment.GetEnvironmentVariable(name);
if (value == null)
logger.WriteError($"ROBUST_BENCHMARKS_ENABLE_SQL is set, but {name} is missing.");
return value != null;
}
private void Export(Summary summary, ILogger logger)
{
if (!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_ADDRESS", logger, out var address) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_PORT", logger, out var rawPort) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_USER", logger, out var user) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_PASSWORD", logger, out var password) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_DATABASE", logger, out var db) ||
!TryGetEnvironmentVariable("GITHUB_SHA", logger, out var gitHash))
return;
if (!int.TryParse(rawPort, out var port))
{
logger.WriteError("Failed parsing ROBUST_BENCHMARKS_SQL_PORT to int.");
return;
}
var builder = new DbContextOptionsBuilder<BenchmarkContext>();
var connectionString = new NpgsqlConnectionStringBuilder
{
Host = address,
Port = port,
Database = db,
Username = user,
Password = password
}.ConnectionString;
builder.UseNpgsql(connectionString);
using var ctx = new BenchmarkContext(builder.Options);
try
{
ctx.Database.Migrate();
ctx.BenchmarkRuns.Add(BenchmarkRun.FromSummary(summary, gitHash));
ctx.SaveChanges();
}
finally
{
ctx.Dispose();
}
}
public string Name => "sql";
}
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<BenchmarkContext>
{
public BenchmarkContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BenchmarkContext>();
optionsBuilder.UseNpgsql("Server=localhost");
return new BenchmarkContext(optionsBuilder.Options);
}
}
public class BenchmarkContext : DbContext
{
public DbSet<BenchmarkRun> BenchmarkRuns { get; set; } = default!;
public BenchmarkContext() { }
public BenchmarkContext(DbContextOptions<BenchmarkContext> options) : base(options) { }
}
public class BenchmarkRun
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ulong Id { get; set; }
public string GitHash { get; set; } = string.Empty;
[Column(TypeName = "timestamptz")]
public DateTime RunDate { get; set; }
public string Name { get; set; } = string.Empty;
[Column(TypeName = "jsonb")]
public BenchmarkRunReport[] Reports { get; set; } = Array.Empty<BenchmarkRunReport>();
public static BenchmarkRun FromSummary(Summary summary, string gitHash)
{
return new BenchmarkRun
{
Reports = summary.Reports.Select(r => new BenchmarkRunReport
{
Parameters = r.BenchmarkCase.Parameters.Items.Select(p => new BenchmarkRunParameter
{
Name = p.Name,
Value = p.Value
}).ToArray(),
Statistics = r.ResultStatistics
}).ToArray(),
Name = summary.BenchmarksCases.First().FolderInfo,
RunDate = DateTime.UtcNow,
GitHash = gitHash
};
}
}
public class BenchmarkRunReport
{
public BenchmarkRunParameter[] Parameters { get; set; } = Array.Empty<BenchmarkRunParameter>();
public Statistics Statistics { get; set; } = default!;
}
public class BenchmarkRunParameter
{
public string Name { get; set; } = string.Empty;
public object Value { get; set; } = default!;
}

View File

@@ -0,0 +1,55 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Robust.Benchmarks.Exporters;
#nullable disable
namespace Robust.Benchmarks.Migrations
{
[DbContext(typeof(BenchmarkContext))]
[Migration("20220328231938_InitialCreate")]
partial class InitialCreate
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
{
b.Property<decimal>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)");
b.Property<string>("GitHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<BenchmarkRunReport[]>("Reports")
.IsRequired()
.HasColumnType("jsonb");
b.Property<DateTime>("RunDate")
.HasColumnType("Date");
b.HasKey("Id");
b.ToTable("BenchmarkRuns");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Robust.Benchmarks.Exporters;
#nullable disable
namespace Robust.Benchmarks.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "BenchmarkRuns",
columns: table => new
{
Id = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
GitHash = table.Column<string>(type: "text", nullable: false),
RunDate = table.Column<DateTime>(type: "Date", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Reports = table.Column<BenchmarkRunReport[]>(type: "jsonb", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_BenchmarkRuns", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "BenchmarkRuns");
}
}
}

View File

@@ -0,0 +1,53 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Robust.Benchmarks.Exporters;
#nullable disable
namespace Robust.Benchmarks.Migrations
{
[DbContext(typeof(BenchmarkContext))]
partial class BenchmarkContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
{
b.Property<decimal>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)");
b.Property<string>("GitHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<BenchmarkRunReport[]>("Reports")
.IsRequired()
.HasColumnType("jsonb");
b.Property<DateTime>("RunDate")
.HasColumnType("Date");
b.HasKey("Id");
b.ToTable("BenchmarkRuns");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -9,6 +9,9 @@ namespace Robust.Benchmarks.NumericsHelpers
[Params(32, 128)]
public int N { get; set; }
[Params(1,2)]
public int T { get; set; }
private float[] _inputA = default!;
private float[] _inputB = default!;
private float[] _output = default!;

View File

@@ -1,6 +1,8 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using System;
using Robust.Benchmarks.Configs;
using Robust.Benchmarks.Exporters;
namespace Robust.Benchmarks
{
@@ -16,7 +18,8 @@ namespace Robust.Benchmarks
Console.WriteLine("THE DEBUG BUILD IS ONLY GOOD FOR FIXING A CRASHING BENCHMARK\n");
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig());
#else
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
var config = Environment.GetEnvironmentVariable("ROBUST_BENCHMARKS_ENABLE_SQL") != null ? DefaultSQLConfig.Instance : null;
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
#endif
}
}

View File

@@ -10,9 +10,15 @@
<ItemGroup>
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0-rc.2" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Engine.targets" />
</Project>

View File

@@ -1,7 +1,6 @@
using System.Globalization;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Result;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Validation;
using Robust.Shared.Serialization.Markdown.Value;
@@ -19,10 +18,10 @@ namespace Robust.Benchmarks.Serialization
: new ErrorNode(node, $"Failed parsing int value: {node.Value}");
}
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
public int Read(ISerializationManager serializationManager, ValueDataNode node,
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null, int _ = default)
{
return new DeserializedValue<int>(int.Parse(node.Value, CultureInfo.InvariantCulture));
return int.Parse(node.Value, CultureInfo.InvariantCulture);
}
public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,

View File

@@ -28,7 +28,7 @@ namespace Robust.Benchmarks.Serialization.Copy
var seedMapping = yamlStream.Documents[0].RootNode.ToDataNodeCast<SequenceDataNode>().Cast<MappingDataNode>(0);
Seed = SerializationManager.ReadValueOrThrow<SeedDataDefinition>(seedMapping);
Seed = SerializationManager.Read<SeedDataDefinition>(seedMapping);
}
private const string String = "ABC";

View File

@@ -36,7 +36,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
Max: 10
PotencyDivisor: 10";
[DataField("id", required: true)] public string ID { get; set; } = default!;
[IdDataFieldAttribute] public string ID { get; set; } = default!;
#region Tracking
[DataField("name")] public string Name { get; set; } = string.Empty;

View File

@@ -2,7 +2,6 @@
using BenchmarkDotNet.Attributes;
using Robust.Benchmarks.Serialization.Definitions;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization.Manager.Result;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
@@ -42,32 +41,32 @@ namespace Robust.Benchmarks.Serialization.Read
private ValueDataNode FlagThirtyOne { get; } = new("ThirtyOne");
[Benchmark]
public string? ReadString()
public string ReadString()
{
return SerializationManager.ReadValue<string>(StringNode);
return SerializationManager.Read<string>(StringNode);
}
[Benchmark]
public int? ReadInteger()
public int ReadInteger()
{
return SerializationManager.ReadValue<int>(IntNode);
return SerializationManager.Read<int>(IntNode);
}
[Benchmark]
public DataDefinitionWithString? ReadDataDefinitionWithString()
public DataDefinitionWithString ReadDataDefinitionWithString()
{
return SerializationManager.ReadValue<DataDefinitionWithString>(StringDataDefNode);
return SerializationManager.Read<DataDefinitionWithString>(StringDataDefNode);
}
[Benchmark]
public SeedDataDefinition? ReadSeedDataDefinition()
public SeedDataDefinition ReadSeedDataDefinition()
{
return SerializationManager.ReadValue<SeedDataDefinition>(SeedNode);
return SerializationManager.Read<SeedDataDefinition>(SeedNode);
}
[Benchmark]
[BenchmarkCategory("flag")]
public DeserializationResult ReadFlagZero()
public object? ReadFlagZero()
{
return SerializationManager.ReadWithTypeSerializer(
typeof(int),
@@ -77,7 +76,7 @@ namespace Robust.Benchmarks.Serialization.Read
[Benchmark]
[BenchmarkCategory("flag")]
public DeserializationResult ReadThirtyOne()
public object? ReadThirtyOne()
{
return SerializationManager.ReadWithTypeSerializer(
typeof(int),
@@ -87,7 +86,7 @@ namespace Robust.Benchmarks.Serialization.Read
[Benchmark]
[BenchmarkCategory("customTypeSerializer")]
public DeserializationResult ReadIntegerCustomSerializer()
public object? ReadIntegerCustomSerializer()
{
return SerializationManager.ReadWithTypeSerializer(
typeof(int),

View File

@@ -46,84 +46,84 @@ namespace Robust.Benchmarks.Serialization
[BenchmarkCategory("read")]
public string[]? ReadEmptyString()
{
return SerializationManager.ReadValue<string[]>(EmptyNode);
return SerializationManager.Read<string[]>(EmptyNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public string[]? ReadOneString()
{
return SerializationManager.ReadValue<string[]>(OneIntNode);
return SerializationManager.Read<string[]>(OneIntNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public string[]? ReadTenStrings()
{
return SerializationManager.ReadValue<string[]>(TenIntsNode);
return SerializationManager.Read<string[]>(TenIntsNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public int[]? ReadEmptyInt()
{
return SerializationManager.ReadValue<int[]>(EmptyNode);
return SerializationManager.Read<int[]>(EmptyNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public int[]? ReadOneInt()
{
return SerializationManager.ReadValue<int[]>(OneIntNode);
return SerializationManager.Read<int[]>(OneIntNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public int[]? ReadTenInts()
{
return SerializationManager.ReadValue<int[]>(TenIntsNode);
return SerializationManager.Read<int[]>(TenIntsNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public DataDefinitionWithString[]? ReadEmptyStringDataDef()
{
return SerializationManager.ReadValue<DataDefinitionWithString[]>(EmptyNode);
return SerializationManager.Read<DataDefinitionWithString[]>(EmptyNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public DataDefinitionWithString[]? ReadOneStringDataDef()
{
return SerializationManager.ReadValue<DataDefinitionWithString[]>(OneStringDefNode);
return SerializationManager.Read<DataDefinitionWithString[]>(OneStringDefNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public DataDefinitionWithString[]? ReadTenStringDataDefs()
{
return SerializationManager.ReadValue<DataDefinitionWithString[]>(TenStringDefsNode);
return SerializationManager.Read<DataDefinitionWithString[]>(TenStringDefsNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public SealedDataDefinitionWithString[]? ReadEmptySealedStringDataDef()
{
return SerializationManager.ReadValue<SealedDataDefinitionWithString[]>(EmptyNode);
return SerializationManager.Read<SealedDataDefinitionWithString[]>(EmptyNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public SealedDataDefinitionWithString[]? ReadOneSealedStringDataDef()
{
return SerializationManager.ReadValue<SealedDataDefinitionWithString[]>(OneStringDefNode);
return SerializationManager.Read<SealedDataDefinitionWithString[]>(OneStringDefNode);
}
[Benchmark]
[BenchmarkCategory("read")]
public SealedDataDefinitionWithString[]? ReadTenSealedStringDataDefs()
{
return SerializationManager.ReadValue<SealedDataDefinitionWithString[]>(TenStringDefsNode);
return SerializationManager.Read<SealedDataDefinitionWithString[]>(TenStringDefsNode);
}
}
}

View File

@@ -28,7 +28,7 @@ namespace Robust.Benchmarks.Serialization.Write
var seedMapping = yamlStream.Documents[0].RootNode.ToDataNodeCast<SequenceDataNode>().Cast<MappingDataNode>(0);
Seed = SerializationManager.ReadValueOrThrow<SeedDataDefinition>(seedMapping);
Seed = SerializationManager.Read<SeedDataDefinition>(seedMapping);
}
private const string String = "ABC";

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env pwsh
param([String]$name)
if ($name -eq "")
{
Write-Error "must specify migration name"
exit
}
dotnet ef migrations add --context BenchmarkContext -o Migrations $name

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
if [ -z "$1" ] ; then
echo "Must specify migration name"
exit 1
fi
dotnet ef migrations add --context BenchmarkContext -o Migrations "$1"

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="16.8.0" />
<PackageReference Include="Microsoft.Build.Framework" Version="17.0.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="Pidgin" Version="2.5.0" />
</ItemGroup>

View File

@@ -5,8 +5,8 @@
</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.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>

View File

@@ -619,6 +619,7 @@ namespace Robust.Client.Audio.Midi
var seqEv = (SequencerEvent) midiEvent;
seqEv.Dest = _debugEvents ? _debugRegister : _synthRegister;
_sequencer.SendAt(seqEv, time, absolute);
seqEv.Dispose();
}
public void Dispose()

View File

@@ -26,7 +26,6 @@ namespace Robust.Client
[Dependency] private readonly IPlayerManager _playMan = default!;
[Dependency] private readonly INetConfigurationManager _configManager = default!;
[Dependency] private readonly IClientEntityManager _entityManager = default!;
[Dependency] private readonly IEntityLookup _entityLookup = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IDiscordRichPresence _discord = default!;
[Dependency] private readonly IGameTiming _timing = default!;
@@ -213,7 +212,6 @@ namespace Robust.Client
{
_entityManager.Startup();
_mapManager.Startup();
_entityLookup.Startup();
_timing.ResetSimTime();
_timing.Paused = false;
@@ -224,7 +222,6 @@ namespace Robust.Client
IoCManager.Resolve<INetConfigurationManager>().FlushMessages();
_gameStates.Reset();
_playMan.Shutdown();
_entityLookup.Shutdown();
_entityManager.Shutdown();
_mapManager.Shutdown();
_discord.ClearPresence();

View File

@@ -47,7 +47,6 @@ namespace Robust.Client
IoCManager.Register<IMapManagerInternal, NetworkedMapManager>();
IoCManager.Register<INetworkedMapManager, NetworkedMapManager>();
IoCManager.Register<IEntityManager, ClientEntityManager>();
IoCManager.Register<IEntityLookup, EntityLookup>();
IoCManager.Register<IReflectionManager, ClientReflectionManager>();
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
@@ -72,7 +71,6 @@ namespace Robust.Client
IoCManager.Register<IStateManager, StateManager>();
IoCManager.Register<IUserInterfaceManager, UserInterfaceManager>();
IoCManager.Register<IUserInterfaceManagerInternal, UserInterfaceManager>();
IoCManager.Register<IDebugDrawing, DebugDrawing>();
IoCManager.Register<ILightManager, LightManager>();
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
IoCManager.Register<IMidiManager, MidiManager>();

View File

@@ -109,13 +109,13 @@ namespace Robust.Client.Console
if (AvailableCommands.ContainsKey(commandName))
{
var playerManager = IoCManager.Resolve<IPlayerManager>();
#if !DEBUG
if (!_conGroup.CanCommand(commandName) && playerManager.LocalPlayer?.Session.Status > SessionStatus.Connecting)
{
WriteError(null, $"Insufficient perms for command: {commandName}");
return;
}
#endif
var command1 = AvailableCommands[commandName];
args.RemoveAt(0);
var shell = new ConsoleShell(this, null);

View File

@@ -168,7 +168,7 @@ namespace Robust.Client.Console.Commands
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IDebugDrawing>();
var mgr = EntitySystem.Get<DebugDrawingSystem>();
mgr.DebugPositions = !mgr.DebugPositions;
}
}
@@ -861,7 +861,7 @@ namespace Robust.Client.Console.Commands
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
var chunk = internalGrid.GetChunk(chunkIndex);
shell.WriteLine($"worldBounds: {chunk.CalcWorldAABB()} localBounds: {chunk.CalcLocalBounds()}");
shell.WriteLine($"worldBounds: {internalGrid.CalcWorldAABB(chunk)} localBounds: {chunk.CachedBounds}");
}
}

View File

@@ -6,17 +6,21 @@ using Robust.Shared.Maths;
namespace Robust.Client.Debugging
{
/// <inheritdoc />
public sealed class DebugDrawing : IDebugDrawing
/// <summary>
/// A collection of visual debug overlays for the client game.
/// </summary>
public sealed class DebugDrawingSystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
private bool _debugPositions;
/// <inheritdoc />
/// <summary>
/// Toggles the visual overlay of the local origin for each entity on screen.
/// </summary>
public bool DebugPositions
{
get => _debugPositions;
@@ -42,13 +46,13 @@ namespace Robust.Client.Debugging
private sealed class EntityPositionOverlay : Overlay
{
private readonly IEntityLookup _lookup;
private readonly EntityLookupSystem _lookup;
private readonly IEyeManager _eyeManager;
private readonly IEntityManager _entityManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager, IEntityManager entityManager)
public EntityPositionOverlay(EntityLookupSystem lookup, IEyeManager eyeManager, IEntityManager entityManager)
{
_lookup = lookup;
_eyeManager = eyeManager;
@@ -61,13 +65,11 @@ namespace Robust.Client.Debugging
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var viewport = _eyeManager.GetWorldViewport();
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
foreach (var entity in _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, viewport))
{
var transform = _entityManager.GetComponent<TransformComponent>(entity);
var center = transform.WorldPosition;
var worldRotation = transform.WorldRotation;
var (center, worldRotation) = xformQuery.GetComponent(entity).GetWorldPositionRotation();
var xLine = worldRotation.RotateVec(Vector2.UnitX);
var yLine = worldRotation.RotateVec(Vector2.UnitY);

View File

@@ -1,13 +0,0 @@
namespace Robust.Client.Debugging
{
/// <summary>
/// A collection of visual debug overlays for the client game.
/// </summary>
public interface IDebugDrawing
{
/// <summary>
/// Toggles the visual overlay of the local origin for each entity on screen.
/// </summary>
bool DebugPositions { get; set; }
}
}

View File

@@ -50,7 +50,6 @@ namespace Robust.Client
[Dependency] private readonly IClientConsoleHost _console = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly IClientEntityManager _entityManager = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly IPlacementManager _placementManager = default!;
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
[Dependency] private readonly IOverlayManagerInternal _overlayManager = default!;
@@ -133,7 +132,7 @@ namespace Robust.Client
_console.Initialize();
_prototypeManager.Initialize();
_prototypeManager.LoadDirectory(Options.PrototypeDirectory);
_prototypeManager.Resync();
_prototypeManager.ResolveResults();
_entityManager.Initialize();
_mapManager.Initialize();
_gameStateManager.Initialize();
@@ -455,6 +454,7 @@ namespace Robust.Client
private void Tick(FrameEventArgs frameEventArgs)
{
_modLoader.BroadcastUpdate(ModUpdateLevel.PreEngine, frameEventArgs);
_console.CommandBufferExecute();
_timerManager.UpdateTimers(frameEventArgs);
_taskManager.ProcessPendingTasks();
@@ -470,7 +470,6 @@ namespace Robust.Client
// The last real tick is the current tick! This way we won't be in "prediction" mode.
_gameTiming.LastRealTick = _gameTiming.CurTick;
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds, noPredictions: false);
_lookup.Update();
}
_modLoader.BroadcastUpdate(ModUpdateLevel.PostEngine, frameEventArgs);
@@ -510,7 +509,7 @@ namespace Robust.Client
logManager.GetSawmill("discord").Level = LogLevel.Warning;
logManager.GetSawmill("net.predict").Level = LogLevel.Info;
logManager.GetSawmill("szr").Level = LogLevel.Info;
logManager.GetSawmill("loc").Level = LogLevel.Error;
logManager.GetSawmill("loc").Level = LogLevel.Warning;
#if DEBUG_ONLY_FCE_INFO
#if DEBUG_ONLY_FCE_LOG
@@ -575,7 +574,6 @@ namespace Robust.Client
_networkManager.Shutdown("Client shutting down");
_midiManager.Shutdown();
IoCManager.Resolve<IEntityLookup>().Shutdown();
_entityManager.Shutdown();
_clyde.Shutdown();
_clydeAudio.Shutdown();

View File

@@ -27,7 +27,6 @@ namespace Robust.Client.GameObjects
public override void Initialize()
{
SetupNetworking();
ReceivedComponentMessage += (_, compMsg) => DispatchComponentMessage(compMsg);
ReceivedSystemMessage += (_, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
base.Initialize();
@@ -52,9 +51,6 @@ namespace Robust.Client.GameObjects
public override IEntityNetworkManager EntityNetManager => this;
/// <inheritdoc />
public event EventHandler<NetworkComponentMessage>? ReceivedComponentMessage;
/// <inheritdoc />
public event EventHandler<object>? ReceivedSystemMessage;
@@ -105,26 +101,6 @@ namespace Robust.Client.GameObjects
throw new NotSupportedException();
}
/// <inheritdoc />
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public void SendComponentNetworkMessage(INetChannel? channel, EntityUid entity, IComponent component, ComponentMessage message)
{
var componentType = component.GetType();
var netId = ComponentFactory.GetRegistration(componentType).NetID;
if (!netId.HasValue)
throw new ArgumentException($"Component {componentType} does not have a NetID.", nameof(component));
var msg = _networkManager.CreateNetMessage<MsgEntity>();
msg.Type = EntityMessageType.ComponentMessage;
msg.EntityUid = entity;
msg.NetId = netId.Value;
msg.ComponentMessage = message;
msg.SourceTick = _gameTiming.CurTick;
_networkManager.ClientSendMessage(msg);
}
private void HandleEntityNetworkMessage(MsgEntity message)
{
if (message.SourceTick <= _gameStateManager.CurServerTick)
@@ -143,10 +119,6 @@ namespace Robust.Client.GameObjects
{
switch (message.Type)
{
case EntityMessageType.ComponentMessage:
ReceivedComponentMessage?.Invoke(this, new NetworkComponentMessage(message));
return;
case EntityMessageType.SystemMessage:
var msg = message.SystemMessage;
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());

View File

@@ -8,7 +8,7 @@ using Robust.Shared.ViewVariables;
namespace Robust.Client.GameObjects
{
[ComponentReference(typeof(OccluderComponent))]
internal sealed class ClientOccluderComponent : OccluderComponent
public sealed class ClientOccluderComponent : OccluderComponent
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
@@ -71,13 +71,29 @@ namespace Robust.Client.GameObjects
{
Occluding = OccluderDir.None;
if (Deleted || !_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
if (Deleted)
return;
// Content may want to override the default behavior for occlusion.
var xform = _entityManager.GetComponent<TransformComponent>(Owner);
var ev = new OccluderDirectionsEvent
{
Component = xform,
};
_entityManager.EventBus.RaiseLocalEvent(Owner, ref ev);
if (ev.Handled)
{
Occluding = ev.Directions;
return;
}
var grid = _mapManager.GetGrid(_entityManager.GetComponent<TransformComponent>(Owner).GridID);
var position = _entityManager.GetComponent<TransformComponent>(Owner).Coordinates;
if (!xform.Anchored)
return;
var grid = _mapManager.GetGrid(xform.GridID);
var position = xform.Coordinates;
void CheckDir(Direction dir, OccluderDir oclDir)
{
foreach (var neighbor in grid.GetInDir(position, dir))
@@ -90,7 +106,7 @@ namespace Robust.Client.GameObjects
}
}
var angle = _entityManager.GetComponent<TransformComponent>(Owner).LocalRotation;
var angle = xform.LocalRotation;
var dirRolling = angle.GetCardinalDir();
// dirRolling starts at effective south
@@ -105,15 +121,28 @@ namespace Robust.Client.GameObjects
CheckDir(dirRolling, OccluderDir.East);
}
}
[Flags]
internal enum OccluderDir : byte
{
None = 0,
North = 1,
East = 1 << 1,
South = 1 << 2,
West = 1 << 3,
}
[Flags]
public enum OccluderDir : byte
{
None = 0,
North = 1,
East = 1 << 1,
South = 1 << 2,
West = 1 << 3,
}
/// <summary>
/// Raised by occluders when trying to get occlusion directions.
/// </summary>
[ByRefEvent]
public struct OccluderDirectionsEvent
{
public bool Handled = false;
public OccluderDir Directions = OccluderDir.None;
public TransformComponent Component = default!;
public OccluderDirectionsEvent() {}
}
}

View File

@@ -25,6 +25,8 @@ namespace Robust.Client.GameObjects
[Animatable]
Vector2 Scale { get; set; }
Box2 Bounds { get; }
/// <summary>
/// A rotation applied to all layers.
/// </summary>
@@ -119,7 +121,8 @@ namespace Robust.Client.GameObjects
/// This is useful to allow layer map configs to be defined in prototypes,
/// while still allowing code to create configs if they're absent.
/// </remarks>
void LayerMapReserveBlank(object key);
/// <returns>Index of the new layer.</returns>
int LayerMapReserveBlank(object key);
/// <summary>
/// Adds a layer without texture (thus falling back to the error texture).
@@ -145,8 +148,8 @@ namespace Robust.Client.GameObjects
void RemoveLayer(int layer);
void RemoveLayer(object layerKey);
void LayerSetShader(int layer, ShaderInstance shader);
void LayerSetShader(object layerKey, ShaderInstance shader);
void LayerSetShader(int layer, ShaderInstance shader, string? prototype = null);
void LayerSetShader(object layerKey, ShaderInstance shader, string? prototype = null);
void LayerSetShader(int layer, string shaderName);
void LayerSetShader(object layerKey, string shaderName);
@@ -217,8 +220,8 @@ namespace Robust.Client.GameObjects
int GetLayerDirectionCount(ISpriteLayer layer);
/// <summary>
/// Calculate sprite bounding box in world-space coordinates.
/// Calculate the rotated sprite bounding box in world-space coordinates.
/// </summary>
Box2 CalculateBoundingBox(Vector2 worldPos);
Box2Rotated CalculateRotatedBoundingBox(Vector2 worldPosition, Angle worldRotation, IEye? eye = null);
}
}

View File

@@ -25,8 +25,6 @@ namespace Robust.Client.GameObjects
RSI.State.Direction EffectiveDirection(Angle worldRotation);
Vector2 LocalToLayer(Vector2 localPos);
/// <summary>
/// Layer size in pixels.
/// Don't account layer scale or sprite world transform.

View File

@@ -84,10 +84,16 @@ namespace Robust.Client.GameObjects
foreach (var sprite in comp.SpriteTree.QueryAabb(localAABB))
{
var worldPos = _entityManager.GetComponent<TransformComponent>(sprite.Owner).WorldPosition;
var bounds = sprite.CalculateBoundingBox(worldPos);
var (worldPos, worldRot) = _entityManager.GetComponent<TransformComponent>(sprite.Owner).GetWorldPositionRotation();
var bounds = sprite.CalculateRotatedBoundingBox(worldPos, worldRot);
// Get scaled down bounds used to indicate the "south" of a sprite.
var localBound = bounds.Box;
var smallLocal = localBound.Scale(0.2f).Translated(-new Vector2(0f, localBound.Extents.Y));
var southIndicator = new Box2Rotated(smallLocal, bounds.Rotation, bounds.Origin);
handle.DrawRect(bounds, Color.Red.WithAlpha(0.2f));
handle.DrawRect(bounds.Scale(0.2f).Translated(-new Vector2(0f, bounds.Extents.Y)), Color.Blue.WithAlpha(0.5f));
handle.DrawRect(southIndicator, Color.Blue.WithAlpha(0.5f));
}
}
}

View File

@@ -75,6 +75,10 @@ namespace Robust.Client.GameObjects
(BoundUserInterface) _dynamicTypeFactory.CreateInstance(type, new[] {this, wrapped.UiKey});
boundInterface.Open();
_openInterfaces[wrapped.UiKey] = boundInterface;
var playerSession = _playerManager.LocalPlayer?.Session;
if(playerSession != null)
_entityManager.EventBus.RaiseLocalEvent(Owner, new BoundUIOpenedEvent(wrapped.UiKey, Owner, playerSession));
}
internal void Close(object uiKey, bool remoteCall)

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
@@ -6,7 +7,7 @@ using Robust.Shared.GameStates;
namespace Robust.Client.GameObjects
{
[UsedImplicitly]
internal sealed class AppearanceSystem : SharedAppearanceSystem
public sealed class AppearanceSystem : SharedAppearanceSystem
{
private readonly Queue<ClientAppearanceComponent> _queuedUpdates = new();
@@ -50,10 +51,33 @@ namespace Robust.Client.GameObjects
if (!stateDiff) return;
component.AppearanceData = actualState.Data;
component.AppearanceData = CloneAppearanceData(actualState.Data);
MarkDirty(component);
}
/// <summary>
/// Take in an appearance data dictionary and attempt to clone it.
/// </summary>
/// <remarks>
/// As some appearance data values are not simple value-type objects, this is not just a shallow clone.
/// </remarks>
private Dictionary<object, object> CloneAppearanceData(Dictionary<object, object> data)
{
Dictionary<object, object> newDict = new(data.Count);
foreach (var (key, value) in data)
{
if (value.GetType().IsValueType)
newDict[key] = value;
else if (value is ICloneable cloneable)
newDict[key] = cloneable.Clone();
else
throw new NotSupportedException("Invalid object in appearance data dictionary. Appearance data must be cloneable");
}
return newDict;
}
public override void MarkDirty(AppearanceComponent component)
{
if (component.AppearanceDirty)
@@ -107,7 +131,7 @@ namespace Robust.Client.GameObjects
[ByRefEvent]
public struct AppearanceChangeEvent
{
public AppearanceComponent Component = default!;
public IReadOnlyDictionary<object, object> AppearanceData = default!;
public AppearanceComponent Component;
public IReadOnlyDictionary<object, object> AppearanceData;
}
}

View File

@@ -168,7 +168,7 @@ namespace Robust.Client.GameObjects
// This container is expecting an entity... but it got parented to some other entity???
// Ah well, the sever should send a new container state that updates expected entities so just ignore it for now.
return;
}
}
RemoveExpectedEntity(message.Entity);
@@ -210,68 +210,101 @@ namespace Robust.Client.GameObjects
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
foreach (var toUpdate in _updateQueue)
{
if (EntityManager.Deleted(toUpdate))
{
if (Deleted(toUpdate))
continue;
}
UpdateEntityRecursively(toUpdate);
UpdateEntityRecursively(toUpdate, xformQuery, pointQuery, spriteQuery);
}
_updateQueue.Clear();
}
private void UpdateEntityRecursively(EntityUid entity)
private void UpdateEntityRecursively(
EntityUid entity,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<PointLightComponent> pointQuery,
EntityQuery<SpriteComponent> spriteQuery)
{
// TODO: Since we are recursing down,
// we could cache ShowContents data here to speed it up for children.
// Am lazy though.
UpdateEntity(entity);
// Recursively go up parents and containers to see whether both sprites and lights need to be occluded
// Could maybe optimise this more by checking nearest parent that has sprite / light and whether it's container
// occluded but this probably isn't a big perf issue.
var xform = xformQuery.GetComponent(entity);
var parent = xform.ParentUid;
var child = entity;
var spriteOccluded = false;
var lightOccluded = false;
foreach (var child in EntityManager.GetComponent<TransformComponent>(entity).Children)
while (parent.IsValid() && !spriteOccluded && !lightOccluded)
{
UpdateEntityRecursively(child.Owner);
var parentXform = xformQuery.GetComponent(parent);
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
{
spriteOccluded = spriteOccluded || !container.ShowContents;
lightOccluded = lightOccluded || container.OccludesLight;
}
child = parent;
parent = parentXform.ParentUid;
}
// Alright so
// This is the CBT bit.
// The issue is we need to go through the children and re-check whether they are or are not contained.
// if they are contained then the occlusion values may need updating for all those children
UpdateEntity(entity, xform, xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
}
private void UpdateEntity(EntityUid entity)
private void UpdateEntity(
EntityUid entity,
TransformComponent xform,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<PointLightComponent> pointQuery,
EntityQuery<SpriteComponent> spriteQuery,
bool spriteOccluded,
bool lightOccluded)
{
if (EntityManager.TryGetComponent(entity, out SpriteComponent? sprite))
if (spriteQuery.TryGetComponent(entity, out var sprite))
{
sprite.ContainerOccluded = false;
// We have to recursively scan for containers upwards in case of nested containers.
var tempParent = entity;
while (tempParent.TryGetContainer(out var container))
{
if (!container.ShowContents)
{
sprite.ContainerOccluded = true;
break;
}
tempParent = container.Owner;
}
sprite.ContainerOccluded = spriteOccluded;
}
if (EntityManager.TryGetComponent(entity, out PointLightComponent? light))
if (pointQuery.TryGetComponent(entity, out var light))
{
light.ContainerOccluded = false;
light.ContainerOccluded = lightOccluded;
}
// We have to recursively scan for containers upwards in case of nested containers.
var tempParent = entity;
while (tempParent.TryGetContainer(out var container))
var childEnumerator = xform.ChildEnumerator;
// Try to avoid TryComp if we already know stuff is occluded.
if ((!spriteOccluded || !lightOccluded) && TryComp<ContainerManagerComponent>(entity, out var manager))
{
while (childEnumerator.MoveNext(out var child))
{
if (container.OccludesLight)
// Thank god it's by value and not by ref.
var childSpriteOccluded = spriteOccluded;
var childLightOccluded = lightOccluded;
// We already know either sprite or light is not occluding so need to check container.
if (manager.TryGetContainer(child.Value, out var container))
{
light.ContainerOccluded = true;
break;
childSpriteOccluded = childSpriteOccluded || !container.ShowContents;
childLightOccluded = childLightOccluded || container.OccludesLight;
}
tempParent = container.Owner;
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, childSpriteOccluded, childLightOccluded);
}
}
else
{
while (childEnumerator.MoveNext(out var child))
{
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
}
}
}

View File

@@ -25,7 +25,7 @@ namespace Robust.Client.GameObjects
if (_enabled)
{
_lightOverlay = new DebugLightOverlay(
IoCManager.Resolve<IEntityLookup>(),
EntitySystem.Get<EntityLookupSystem>(),
IoCManager.Resolve<IEyeManager>(),
IoCManager.Resolve<IMapManager>(),
Get<RenderingTreeSystem>());
@@ -44,7 +44,7 @@ namespace Robust.Client.GameObjects
private sealed class DebugLightOverlay : Overlay
{
private IEntityLookup _lookup;
private EntityLookupSystem _lookup;
private IEyeManager _eyeManager;
private IMapManager _mapManager;
@@ -52,7 +52,7 @@ namespace Robust.Client.GameObjects
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public DebugLightOverlay(IEntityLookup lookup, IEyeManager eyeManager, IMapManager mapManager, RenderingTreeSystem tree)
public DebugLightOverlay(EntityLookupSystem lookup, IEyeManager eyeManager, IMapManager mapManager, RenderingTreeSystem tree)
{
_lookup = lookup;
_eyeManager = eyeManager;
@@ -72,7 +72,7 @@ namespace Robust.Client.GameObjects
{
foreach (var light in tree.LightTree)
{
var aabb = _lookup.GetWorldAabbFromEntity(light.Owner);
var aabb = _lookup.GetWorldAABB(light.Owner);
if (!aabb.Intersects(worldBounds)) continue;
args.WorldHandle.DrawRect(aabb, Color.Green.WithAlpha(0.1f));

View File

@@ -323,7 +323,7 @@ namespace Robust.Client.GameObjects
{
private readonly IPlayerManager _playerManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
private readonly ShaderInstance _unshadedShader;
private readonly EffectSystem _owner;
@@ -342,7 +342,6 @@ namespace Robust.Client.GameObjects
var map = _owner.eyeManager.CurrentMap;
var worldHandle = args.WorldHandle;
ShaderInstance? currentShader = null;
if (_playerManager.LocalPlayer?.ControlledEntity is not {} playerEnt)
return;
@@ -362,13 +361,8 @@ namespace Robust.Client.GameObjects
continue;
}
var newShader = effect.Shaded ? null : _unshadedShader;
if (newShader != currentShader)
{
worldHandle.UseShader(newShader);
currentShader = newShader;
}
if (!effect.Shaded)
worldHandle.UseShader(_unshadedShader);
// TODO: Should be doing matrix transformations
var effectSprite = effect.EffectSprite;
@@ -377,6 +371,9 @@ namespace Robust.Client.GameObjects
(attachedXform?.Coordinates ?? effect.Coordinates)
.Offset(effect.AttachedOffset);
// If we've never seen the entity before then can't resolve coordinates.
if (!coordinates.IsValid(_entityManager)) continue;
// ???
var rotation = attachedXform?.WorldRotation ?? _entityManager.GetComponent<TransformComponent>(coordinates.EntityId).WorldRotation;
@@ -386,6 +383,9 @@ namespace Robust.Client.GameObjects
var rotatedBox = new Box2Rotated(effectArea, effect.Rotation + rotation, effectOrigin);
worldHandle.DrawTextureRect(effectSprite, rotatedBox, ToColor(effect.Color));
if (!effect.Shaded)
worldHandle.UseShader(null);
}
}
}

View File

@@ -0,0 +1,19 @@
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
namespace Robust.Client.GameObjects;
internal class GridRenderingSystem : EntitySystem
{
private readonly IClydeInternal _clyde;
public GridRenderingSystem(IClydeInternal clyde)
{
_clyde = clyde;
}
public override void Initialize()
{
_clyde.RegisterGridEcsEvents();
}
}

View File

@@ -4,11 +4,15 @@ using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
@@ -21,6 +25,8 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IClientGameStateManager _stateManager = default!;
[Dependency] private readonly IConsoleHost _conHost = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private readonly IPlayerCommandStates _cmdStates = new PlayerCommandStates();
@@ -108,6 +114,43 @@ namespace Robust.Client.GameObjects
public override void Initialize()
{
SubscribeLocalEvent<PlayerAttachSysMessage>(OnAttachedEntityChanged);
_conHost.RegisterCommand("incmd",
"Inserts an input command into the simulation",
"incmd <KeyFunction> <d|u KeyState> <wxPos> <wyPos>",
GenerateInputCommand);
}
public override void Shutdown()
{
base.Shutdown();
_conHost.UnregisterCommand("incmd");
}
private void GenerateInputCommand(IConsoleShell shell, string argstr, string[] args)
{
var localPlayer = _playerManager.LocalPlayer;
if(localPlayer is null)
return;
var pent = localPlayer.ControlledEntity;
if(pent is null)
return;
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
var pxform = Transform(pent.Value);
var wPos = pxform.WorldPosition + new Vector2(float.Parse(args[2]), float.Parse(args[3]));
var coords = EntityCoordinates.FromMap(EntityManager, pent.Value, new MapCoordinates(wPos, pxform.MapID));
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
coords, new ScreenCoordinates(0, 0, default), EntityUid.Invalid);
HandleInputCommand(localPlayer.Session, keyFunction, message);
}
private void OnAttachedEntityChanged(PlayerAttachSysMessage message)

View File

@@ -70,18 +70,17 @@ namespace Robust.Client.GameObjects
UpdatesAfter.Add(typeof(TransformSystem));
UpdatesAfter.Add(typeof(PhysicsSystem));
_mapManager.MapCreated += MapManagerOnMapCreated;
_mapManager.OnGridCreated += MapManagerOnGridCreated;
SubscribeLocalEvent<MapChangedEvent>(MapManagerOnMapCreated);
SubscribeLocalEvent<GridInitializeEvent>(MapManagerOnGridCreated);
// Due to how recursion works, this must be done.
SubscribeLocalEvent<MoveEvent>(AnythingMoved);
SubscribeLocalEvent<SpriteComponent, EntMapIdChangedMessage>(SpriteMapChanged);
SubscribeLocalEvent<SpriteComponent, EntParentChangedMessage>(SpriteParentChanged);
SubscribeLocalEvent<SpriteComponent, ComponentRemove>(RemoveSprite);
SubscribeLocalEvent<SpriteComponent, SpriteUpdateEvent>(HandleSpriteUpdate);
SubscribeLocalEvent<PointLightComponent, EntMapIdChangedMessage>(LightMapChanged);
SubscribeLocalEvent<PointLightComponent, EntParentChangedMessage>(LightParentChanged);
SubscribeLocalEvent<PointLightComponent, PointLightRadiusChangedEvent>(PointLightRadiusChanged);
SubscribeLocalEvent<PointLightComponent, PointLightUpdateEvent>(HandleLightUpdate);
@@ -113,12 +112,18 @@ namespace Robust.Client.GameObjects
private void AnythingMoved(ref MoveEvent args)
{
var xforms = EntityManager.GetEntityQuery<TransformComponent>();
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
AnythingMovedSubHandler(args.Sender, xforms);
AnythingMovedSubHandler(args.Sender, xformQuery, pointQuery, spriteQuery);
}
private void AnythingMovedSubHandler(EntityUid uid, EntityQuery<TransformComponent> xforms)
private void AnythingMovedSubHandler(
EntityUid uid,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<PointLightComponent> pointQuery,
EntityQuery<SpriteComponent> spriteQuery)
{
// To avoid doing redundant updates (and we don't need to update a grid's children ever)
if (!_checkedChildren.Add(uid) || EntityManager.HasComponent<RenderingTreeComponent>(uid)) return;
@@ -127,17 +132,19 @@ namespace Robust.Client.GameObjects
// WHATEVER YOU DO, DON'T REPLACE THIS WITH SPAMMING EVENTS UNLESS YOU HAVE A GUARANTEE IT WON'T LAG THE GC.
// (Struct-based events ok though)
// Ironically this was lagging the GC lolz
if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
if (spriteQuery.TryGetComponent(uid, out var sprite))
QueueSpriteUpdate(sprite);
if (EntityManager.TryGetComponent(uid, out PointLightComponent? light))
if (pointQuery.TryGetComponent(uid, out var light))
QueueLightUpdate(light);
if (!xforms.TryGetComponent(uid, out var xform)) return;
if (!xformQuery.TryGetComponent(uid, out var xform)) return;
foreach (var child in xform.ChildEntities)
var childEnumerator = xform.ChildEnumerator;
while (childEnumerator.MoveNext(out var child))
{
AnythingMovedSubHandler(child, xforms);
AnythingMovedSubHandler(child.Value, xformQuery, pointQuery, spriteQuery);
}
}
@@ -147,10 +154,6 @@ namespace Robust.Client.GameObjects
// Otherwise these will still have their past MapId and that's all we need..
#region SpriteHandlers
private void SpriteMapChanged(EntityUid uid, SpriteComponent component, EntMapIdChangedMessage args)
{
QueueSpriteUpdate(component);
}
private void SpriteParentChanged(EntityUid uid, SpriteComponent component, ref EntParentChangedMessage args)
{
@@ -180,10 +183,6 @@ namespace Robust.Client.GameObjects
#endregion
#region LightHandlers
private void LightMapChanged(EntityUid uid, PointLightComponent component, EntMapIdChangedMessage args)
{
QueueLightUpdate(component);
}
private void LightParentChanged(EntityUid uid, PointLightComponent component, ref EntParentChangedMessage args)
{
@@ -212,13 +211,6 @@ namespace Robust.Client.GameObjects
}
#endregion
public override void Shutdown()
{
base.Shutdown();
_mapManager.MapCreated -= MapManagerOnMapCreated;
_mapManager.OnGridCreated -= MapManagerOnGridCreated;
}
private void OnTreeRemove(EntityUid uid, RenderingTreeComponent component, ComponentRemove args)
{
foreach (var sprite in component.SpriteTree)
@@ -235,9 +227,9 @@ namespace Robust.Client.GameObjects
component.LightTree.Clear();
}
private void MapManagerOnMapCreated(object? sender, MapEventArgs e)
private void MapManagerOnMapCreated(MapChangedEvent e)
{
if (e.Map == MapId.Nullspace)
if (e.Destroyed || e.Map == MapId.Nullspace)
{
return;
}
@@ -245,9 +237,9 @@ namespace Robust.Client.GameObjects
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetMapEntityId(e.Map));
}
private void MapManagerOnGridCreated(MapId mapId, GridId gridId)
private void MapManagerOnGridCreated(GridInitializeEvent ev)
{
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetGrid(gridId).GridEntityId);
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetGrid(ev.GridId).GridEntityId);
}
private RenderingTreeComponent? GetRenderTree(EntityUid entity, EntityQuery<TransformComponent> xforms)
@@ -369,13 +361,10 @@ namespace Robust.Client.GameObjects
private Box2 SpriteAabbFunc(in SpriteComponent value)
{
var xforms = EntityManager.GetEntityQuery<TransformComponent>();
var xform = xforms.GetComponent(value.Owner);
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
var tree = GetRenderTree(value.Owner, xforms);
return tree == null ? bounds.CalcBoundingBox() : xforms.GetComponent(tree.Owner).InvWorldMatrix.TransformBox(bounds);
return SpriteAabbFunc(value, worldPos, worldRot, xforms);
}
private Box2 LightAabbFunc(in PointLightComponent value)
@@ -392,7 +381,7 @@ namespace Robust.Client.GameObjects
private Box2 SpriteAabbFunc(SpriteComponent value, Vector2 worldPos, Angle worldRot, EntityQuery<TransformComponent> xforms)
{
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
var bounds = value.CalculateRotatedBoundingBox(worldPos, worldRot);
var tree = GetRenderTree(value.Owner, xforms);
return tree == null ? bounds.CalcBoundingBox() : xforms.GetComponent(tree.Owner).InvWorldMatrix.TransformBox(bounds);

View File

@@ -0,0 +1,84 @@
using System;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects;
public sealed partial class SpriteSystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Pure]
public Texture Frame0(SpriteSpecifier specifier)
{
return RsiStateLike(specifier).Default;
}
[Pure]
public IRsiStateLike RsiStateLike(SpriteSpecifier specifier)
{
switch (specifier)
{
case SpriteSpecifier.Texture tex:
return tex.GetTexture(_resourceCache);
case SpriteSpecifier.Rsi rsi:
return GetState(rsi);
case SpriteSpecifier.EntityPrototype prototypeIcon:
if (!_proto.TryIndex<EntityPrototype>(prototypeIcon.EntityPrototypeId, out var prototype))
{
Logger.Error("Failed to load PrototypeIcon {0}", prototypeIcon.EntityPrototypeId);
return SpriteComponent.GetFallbackState(_resourceCache);
}
return SpriteComponent.GetPrototypeIcon(prototype, _resourceCache);
default:
throw new NotSupportedException();
}
}
[Pure]
public IRsiStateLike GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
{
var icon = IconComponent.GetPrototypeIcon(prototype, _resourceCache);
if (icon != null) return icon;
if (!prototype.Components.ContainsKey("Sprite"))
{
return SpriteComponent.GetFallbackState(resourceCache);
}
var dummy = Spawn(prototype.ID, MapCoordinates.Nullspace);
var spriteComponent = EnsureComp<SpriteComponent>(dummy);
var result = spriteComponent.Icon ?? SpriteComponent.GetFallbackState(resourceCache);
Del(dummy);
return result;
}
[Pure]
public RSI.State GetState(SpriteSpecifier.Rsi rsiSpecifier)
{
if (_resourceCache.TryGetResource<RSIResource>(
SharedSpriteComponent.TextureRoot / rsiSpecifier.RsiPath,
out var theRsi) &&
theRsi.RSI.TryGetState(rsiSpecifier.RsiState, out var state))
{
return state;
}
Logger.Error("Failed to load RSI {0}", rsiSpecifier.RsiPath);
return SpriteComponent.GetFallbackState(_resourceCache);
}
}

View File

@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects
/// Updates the layer animation for every visible sprite.
/// </summary>
[UsedImplicitly]
public sealed class SpriteSystem : EntitySystem
public sealed partial class SpriteSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly RenderingTreeSystem _treeSystem = default!;

View File

@@ -45,7 +45,6 @@ namespace Robust.Client.GameStates
[Dependency] private readonly IComponentFactory _compFactory = default!;
[Dependency] private readonly IClientEntityManagerInternal _entities = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IClientNetManager _network = default!;
[Dependency] private readonly IBaseClient _client = default!;
@@ -332,8 +331,6 @@ namespace Robust.Client.GameStates
}
_entities.TickUpdate((float) _timing.TickPeriod.TotalSeconds, noPredictions: !IsPredictionEnabled);
_lookup.Update();
}
private void ResetPredictedEntities(GameTick curTick)
@@ -350,9 +347,7 @@ namespace Robust.Client.GameStates
// Check log level first to avoid the string alloc.
if (_sawmill.Level <= LogLevel.Debug)
{
_sawmill.Debug($"Entity {entity} was made dirty.");
}
if (!_processor.TryGetLastServerStates(entity, out var last))
{
@@ -370,7 +365,9 @@ namespace Robust.Client.GameStates
continue;
}
_sawmill.Debug($" And also its component {comp.GetType()}");
if (_sawmill.Level <= LogLevel.Debug)
_sawmill.Debug($" And also its component {comp.GetType()}");
// TODO: Handle interpolation.
var handleState = new ComponentHandleState(compState, null);
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
@@ -424,7 +421,6 @@ namespace Robust.Client.GameStates
var createdEntities = ApplyEntityStates(curState.EntityStates.Span, curState.EntityDeletions.Span,
nextState != null ? nextState.EntityStates.Span : default);
_players.ApplyPlayerStates(curState.PlayerStates.Value ?? Array.Empty<PlayerState>());
_mapManager.ApplyGameStatePost(curState.MapData);
GameStateApplied?.Invoke(new GameStateAppliedArgs(curState));
return createdEntities;

View File

@@ -15,6 +15,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Audio;
using Robust.Shared.Log;
using Vector2 = Robust.Shared.Maths.Vector2;
using Robust.Shared.Utility;
namespace Robust.Client.Graphics.Audio
{
@@ -193,8 +194,10 @@ namespace Robust.Client.Graphics.Audio
{
_didPositionWarning = true;
Logger.WarningS("clyde.oal",
"Attempting to set position on audio source with multiple audio channels! Stream: '{0}'",
"Attempting to set position on audio source with multiple audio channels! Stream: '{0}'. Make sure the audio is MONO, not stereo.",
_sourceStream.Name);
// warning isn't enough, people just ignore it :(
DebugTools.Assert(false, $"Attempting to set position on audio source with multiple audio channels! Stream: '{_sourceStream.Name}'. Make sure the audio is MONO, not stereo.");
}
#endif

View File

@@ -1,7 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using OpenToolkit.Graphics.OpenGL4;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -18,8 +16,8 @@ namespace Robust.Client.Graphics.Clyde
private readonly Dictionary<GridId, Dictionary<Vector2i, MapChunkData>> _mapChunkData =
new();
private int _verticesPerChunk(IMapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * 4;
private int _indicesPerChunk(IMapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
private int _verticesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * 4;
private int _indicesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
private void _drawGrids(Viewport viewport, Box2Rotated worldBounds, IEye eye)
{
@@ -78,7 +76,7 @@ namespace Robust.Client.Graphics.Clyde
}
}
private void _updateChunkMesh(IMapGrid grid, IMapChunk chunk)
private void _updateChunkMesh(IMapGrid grid, MapChunk chunk)
{
var data = _mapChunkData[grid.Index];
@@ -91,25 +89,41 @@ namespace Robust.Client.Graphics.Clyde
Span<Vertex2D> vertexBuffer = stackalloc Vertex2D[_verticesPerChunk(chunk)];
var i = 0;
foreach (var tile in chunk)
var cSz = grid.ChunkSize;
var cScaled = chunk.Indices * cSz;
for (ushort x = 0; x < cSz; x++)
{
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile.Tile);
if (regionMaybe == null)
for (ushort y = 0; y < cSz; y++)
{
continue;
var tile = chunk.GetTile(x, y);
if (tile.IsEmpty)
continue;
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile);
Box2 region;
if (regionMaybe == null || regionMaybe.Length <= tile.Variant)
{
region = _tileDefinitionManager.ErrorTileRegion;
}
else
{
region = regionMaybe[tile.Variant];
}
var gx = x + cScaled.X;
var gy = y + cScaled.Y;
var vIdx = i * 4;
vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom);
vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom);
vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top);
vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top);
var nIdx = i * GetQuadBatchIndexCount();
var tIdx = (ushort)(i * 4);
QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx);
i += 1;
}
var region = regionMaybe.Value;
var vIdx = i * 4;
vertexBuffer[vIdx + 0] = new Vertex2D(tile.X, tile.Y, region.Left, region.Bottom);
vertexBuffer[vIdx + 1] = new Vertex2D(tile.X + 1, tile.Y, region.Right, region.Bottom);
vertexBuffer[vIdx + 2] = new Vertex2D(tile.X + 1, tile.Y + 1, region.Right, region.Top);
vertexBuffer[vIdx + 3] = new Vertex2D(tile.X, tile.Y + 1, region.Left, region.Top);
var nIdx = i * GetQuadBatchIndexCount();
var tIdx = (ushort) (i * 4);
QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx);
i += 1;
}
GL.BindVertexArray(datum.VAO);
@@ -122,7 +136,7 @@ namespace Robust.Client.Graphics.Clyde
datum.TileCount = i;
}
private MapChunkData _initChunkBuffers(IMapGrid grid, IMapChunk chunk)
private MapChunkData _initChunkBuffers(IMapGrid grid, MapChunk chunk)
{
var vao = GenVertexArray();
BindVertexArray(vao);
@@ -159,7 +173,7 @@ namespace Robust.Client.Graphics.Clyde
return datum;
}
private bool _isChunkDirty(IMapGrid grid, IMapChunk chunk)
private bool _isChunkDirty(IMapGrid grid, MapChunk chunk)
{
var data = _mapChunkData[grid.Index];
return !data.TryGetValue(chunk.Indices, out var datum) || datum.Dirty;
@@ -175,7 +189,7 @@ namespace Robust.Client.Graphics.Clyde
// Don't need to set it if we don't have an entry since lack of an entry is treated as dirty.
}
private void _updateOnGridModified(object? sender, GridChangedEventArgs args)
private void _updateOnGridModified(GridModifiedEvent args)
{
foreach (var (pos, _) in args.Modified)
{
@@ -185,21 +199,23 @@ namespace Robust.Client.Graphics.Clyde
}
}
private void _updateTileMapOnUpdate(object? sender, TileChangedEventArgs args)
private void _updateTileMapOnUpdate(TileChangedEvent args)
{
var grid = _mapManager.GetGrid(args.NewTile.GridIndex);
var chunk = grid.GridTileToChunkIndices(new Vector2i(args.NewTile.X, args.NewTile.Y));
_setChunkDirty(grid, chunk);
}
private void _updateOnGridCreated(MapId mapId, GridId gridId)
private void _updateOnGridCreated(GridStartupEvent ev)
{
var gridId = ev.GridId;
Logger.DebugS("grid", $"Adding {gridId} to grid renderer");
_mapChunkData.Add(gridId, new Dictionary<Vector2i, MapChunkData>());
}
private void _updateOnGridRemoved(MapId mapId, GridId gridId)
private void _updateOnGridRemoved(GridRemovalEvent ev)
{
var gridId = ev.GridId;
Logger.DebugS("grid", $"Removing {gridId} from grid renderer");
var data = _mapChunkData[gridId];

View File

@@ -24,11 +24,19 @@ namespace Robust.Client.Graphics.Clyde
{
public ClydeDebugLayers DebugLayers { get; set; }
private readonly RefList<(SpriteComponent sprite, Matrix3 worldMatrix, Angle worldRotation, float yWorldPos)>
private readonly RefList<(SpriteComponent sprite, Vector2 worldPos, Angle worldRotation, Box2 spriteScreenBB)>
_drawingSpriteList
=
new();
// TODO allow this scale to be passed with PostShader as variable
/// <summary>
/// Some shaders that enlarge the final sprite, like emission or highlight effects, need to use a slightly larger render target.
/// </summary>
public static float PostShadeScale = 1.25f;
private List<Overlay> _overlays = new();
public void Render()
{
CheckTransferringScreenshots();
@@ -146,19 +154,19 @@ namespace Robust.Client.Graphics.Clyde
private List<Overlay> GetOverlaysForSpace(OverlaySpace space)
{
var list = new List<Overlay>();
_overlays.Clear();
foreach (var overlay in _overlayManager.AllOverlays)
{
if ((overlay.Space & space) != 0)
{
list.Add(overlay);
_overlays.Add(overlay);
}
}
list.Sort(OverlayComparer.Instance);
_overlays.Sort(OverlayComparer.Instance);
return list;
return _overlays;
}
private ClydeTexture? ScreenBufferTexture;
@@ -217,9 +225,8 @@ namespace Robust.Client.Graphics.Clyde
RenderOverlays(viewport, OverlaySpace.WorldSpaceBelowEntities, worldAABB, worldBounds);
var screenSize = viewport.Size;
eye.GetViewMatrix(out var eyeMatrix, eye.Scale);
ProcessSpriteEntities(mapId, eyeMatrix, worldBounds, _drawingSpriteList);
ProcessSpriteEntities(mapId, viewport, eye, worldBounds, _drawingSpriteList);
var worldOverlays = new List<Overlay>();
@@ -276,30 +283,12 @@ namespace Robust.Client.Graphics.Clyde
break;
}
var matrix = entry.worldMatrix;
var worldPosition = new Vector2(matrix.R0C2, matrix.R1C2);
RenderTexture? entityPostRenderTarget = null;
Vector2i roundedPos = default;
if (entry.sprite.PostShader != null)
{
// calculate world bounding box
var spriteBB = entry.sprite.CalculateBoundingBox(worldPosition);
var spriteLB = spriteBB.BottomLeft;
var spriteRT = spriteBB.TopRight;
// finally we can calculate screen bounding in pixels
var screenLB = viewport.WorldToLocal(spriteLB);
var screenRT = viewport.WorldToLocal(spriteRT);
// we need to scale RT a for effects like emission or highlight
// scale can be passed with PostShader as variable in future
var postShadeScale = 1.25f;
var screenSpriteSize = (Vector2i) ((screenRT - screenLB) * postShadeScale).Rounded();
// Rotate the vector by the eye angle, otherwise the bounding box will be incorrect
screenSpriteSize = (Vector2i) eye.Rotation.RotateVec(screenSpriteSize).Rounded();
screenSpriteSize.Y = -screenSpriteSize.Y;
// get the size of the sprite on screen, scaled slightly to allow for shaders that increase the final sprite size.
var screenSpriteSize = (Vector2i) (entry.spriteScreenBB.Size * PostShadeScale).Rounded();
// I'm not 100% sure why it works, but without it post-shader
// can be lower or upper by 1px than original sprite depending on sprite rotation or scale
@@ -322,16 +311,14 @@ namespace Robust.Client.Graphics.Clyde
// Calculate viewport so that the entity thinks it's drawing to the same position,
// which is necessary for light application,
// but it's ACTUALLY drawing into the center of the render target.
var spritePos = spriteBB.Center;
var screenPos = viewport.WorldToLocal(spritePos);
var (roundedX, roundedY) = roundedPos = (Vector2i) screenPos;
var flippedPos = new Vector2i(roundedX, screenSize.Y - roundedY);
roundedPos = (Vector2i) entry.spriteScreenBB.Center;
var flippedPos = new Vector2i(roundedPos.X, screenSize.Y - roundedPos.Y);
flippedPos -= entityPostRenderTarget.Size / 2;
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
}
}
entry.sprite.Render(_renderHandle.DrawingHandleWorld, eye.Rotation, in entry.worldRotation, in worldPosition);
entry.sprite.Render(_renderHandle.DrawingHandleWorld, eye.Rotation, in entry.worldRotation, in entry.worldPos);
if (entry.sprite.PostShader != null && entityPostRenderTarget != null)
{
@@ -369,17 +356,26 @@ namespace Robust.Client.Graphics.Clyde
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void ProcessSpriteEntities(MapId map, Matrix3 eyeMatrix, Box2Rotated worldBounds,
RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> list)
private void ProcessSpriteEntities(MapId map, Viewport view, IEye eye, Box2Rotated worldBounds,
RefList<(SpriteComponent sprite, Vector2 worldPos, Angle worldRot, Box2 spriteScreenBB)> list)
{
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
// Construct a matrix equivalent for Viewport.WorldToLocal()
eye.GetViewMatrix(out var viewMatrix, view.RenderScale);
var uiProjmatrix = Matrix3.Identity;
uiProjmatrix.R0C0 = EyeManager.PixelsPerMeter;
uiProjmatrix.R1C1 = -EyeManager.PixelsPerMeter;
uiProjmatrix.R0C2 = view.Size.X / 2f;
uiProjmatrix.R1C2 = view.Size.Y / 2f;
var worldToLocal = viewMatrix * uiProjmatrix;
foreach (var comp in _entitySystemManager.GetEntitySystem<RenderingTreeSystem>().GetRenderTrees(map, worldBounds))
{
var bounds = xforms.GetComponent(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
comp.SpriteTree.QueryAabb(ref list, (
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
ref RefList<(SpriteComponent sprite, Vector2 worldPos, Angle worldRot, Box2 spriteScreenBB)> state,
in SpriteComponent value) =>
{
var entity = value.Owner;
@@ -387,12 +383,10 @@ namespace Robust.Client.Graphics.Clyde
ref var entry = ref state.AllocAdd();
entry.sprite = value;
Vector2 worldPos;
(worldPos, entry.worldRot, entry.matrix) = transform.GetWorldPositionRotationMatrix();
var eyePos = eyeMatrix.Transform(worldPos);
// Didn't use the bounds from the query as that has to be re-calculated (and is probably more expensive than this).
var bounds = value.CalculateBoundingBox(eyePos);
entry.yWorldPos = eyePos.Y - bounds.Extents.Y;
(entry.worldPos, entry.worldRot) = transform.GetWorldPositionRotation();
var spriteWorldBB = value.CalculateRotatedBoundingBox(entry.worldPos, entry.worldRot, eye);
entry.spriteScreenBB = worldToLocal.TransformBox(spriteWorldBB);
return true;
}, bounds, true);

View File

@@ -514,14 +514,14 @@ namespace Robust.Client.Graphics.Clyde
var enlargedBounds = worldAABB.Enlarged(renderingTreeSystem.MaxLightRadius);
// Use worldbounds for this one as we only care if the light intersects our actual bounds
var state = (this, worldAABB, count: 0);
var state = (this, worldAABB, count: 0, shadowCastingCount: 0);
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
foreach (var comp in renderingTreeSystem.GetRenderTrees(map, enlargedBounds))
{
var bounds = xforms.GetComponent(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
comp.LightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldAABB, int count) state, in PointLightComponent light) =>
comp.LightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldAABB, int count, int shadowCastingCount) state, in PointLightComponent light) =>
{
if (state.count >= LightsToRenderListSize)
{
@@ -543,6 +543,9 @@ namespace Robust.Client.Graphics.Clyde
return true;
}
// If the light is a shadow casting light, keep a separate track of that
if (light.CastShadows) state.shadowCastingCount++;
float distanceSquared = (state.worldAABB.Center - lightPos).LengthSquared;
state.clyde._lightsToRenderList[state.count++] = (light, lightPos, distanceSquared);
@@ -550,17 +553,29 @@ namespace Robust.Client.Graphics.Clyde
}, bounds);
}
if (state.count > _maxLightsPerScene)
if (state.shadowCastingCount > _maxLightsPerScene)
{
// There are too many lights to fit in the scene.
// There are too many lights casting shadows to fit in the scene.
// This check must occur before occluder expansion, or else bad things happen.
// Sort lights by distance.
// First, partition the array based on whether the lights are shadow casting or not
// (non shadow casting lights should be the first partition, shadow casting lights the second)
Array.Sort(_lightsToRenderList, 0, state.count, Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
{
if (x.light.CastShadows && !y.light.CastShadows) return 1;
else if (!x.light.CastShadows && y.light.CastShadows) return -1;
else return 0;
}));
// Next, sort just the shadow casting lights by distance.
Array.Sort(_lightsToRenderList, state.count - state.shadowCastingCount, state.shadowCastingCount, Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
{
return x.distanceSquared.CompareTo(y.distanceSquared);
}));
// Then effectively delete the furthest lights.
state.count = _maxLightsPerScene;
// Then effectively delete the furthest lights, by setting the end of the array to exclude N
// number of shadow casting lights (where N is the number above the max number per scene.)
state.count -= state.shadowCastingCount - _maxLightsPerScene;
}
// When culling occluders later, we can't just remove any occluders outside the worldBounds.
@@ -846,7 +861,6 @@ namespace Robust.Client.Graphics.Clyde
// 3D geometry used during depth projection.
// 2D mask geometry used to apply wall bleed.
// TODO: This code probably does not work correctly with rotated camera.
// TODO: Yes this function throws and index exception if you reach maxOccluders.
const int maxOccluders = 2048;

View File

@@ -994,9 +994,9 @@ namespace Robust.Client.Graphics.Clyde
private sealed class SpriteDrawingOrderComparer : IComparer<int>
{
private readonly RefList<(SpriteComponent, Matrix3, Angle, float)> _drawList;
private readonly RefList<(SpriteComponent, Vector2, Angle, Box2)> _drawList;
public SpriteDrawingOrderComparer(RefList<(SpriteComponent, Matrix3, Angle, float)> drawList)
public SpriteDrawingOrderComparer(RefList<(SpriteComponent, Vector2, Angle, Box2)> drawList)
{
_drawList = drawList;
}
@@ -1019,7 +1019,8 @@ namespace Robust.Client.Graphics.Clyde
return cmp;
}
cmp = _drawList[y].Item4.CompareTo(_drawList[x].Item4);
// compare the top of the sprite's BB for y-sorting. Because screen coordinates are flipped, the "top" of the BB is actually the "bottom".
cmp = _drawList[x].Item4.Top.CompareTo(_drawList[y].Item4.Top);
if (cmp != 0)
{

View File

@@ -437,6 +437,13 @@ namespace Robust.Client.Graphics.Clyde
_windowing!.WindowSetVisible(reg, visible);
}
public void RunOnWindowThread(Action a)
{
DebugTools.AssertNotNull(_windowing);
_windowing!.RunOnWindowThread(a);
}
private abstract class WindowReg
{
public bool IsDisposed;

View File

@@ -22,7 +22,7 @@ namespace Robust.Client.Graphics.Clyde
/// <summary>
/// Responsible for most things rendering on OpenGL mode.
/// </summary>
internal sealed partial class Clyde : IClydeInternal, IPostInjectInit
internal sealed partial class Clyde : IClydeInternal, IPostInjectInit, IEntityEventSubscriber
{
[Dependency] private readonly IClydeTileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
@@ -134,16 +134,19 @@ namespace Robust.Client.Graphics.Clyde
public void PostInject()
{
_mapManager.TileChanged += _updateTileMapOnUpdate;
_mapManager.OnGridCreated += _updateOnGridCreated;
_mapManager.OnGridRemoved += _updateOnGridRemoved;
_mapManager.GridChanged += _updateOnGridModified;
// This cvar does not modify the actual GL version requested or anything,
// it overrides the version we detect to detect GL features.
RegisterBlockCVars();
}
public void RegisterGridEcsEvents()
{
_entityManager.EventBus.SubscribeEvent<TileChangedEvent>(EventSource.Local, this, _updateTileMapOnUpdate);
_entityManager.EventBus.SubscribeEvent<GridStartupEvent>(EventSource.Local, this, _updateOnGridCreated);
_entityManager.EventBus.SubscribeEvent<GridRemovalEvent>(EventSource.Local, this, _updateOnGridRemoved);
_entityManager.EventBus.SubscribeEvent<GridModifiedEvent>(EventSource.Local, this, _updateOnGridModified);
}
private void GLInitBindings(bool gles)
{
_glBindingsContext = _glContext!.BindingsContext;

View File

@@ -76,6 +76,11 @@ namespace Robust.Client.Graphics.Clyde
return null;
}
public void RegisterGridEcsEvents()
{
// Nada.
}
public void SetWindowTitle(string title)
{
// Nada.
@@ -246,6 +251,11 @@ namespace Robust.Client.Graphics.Clyde
// Nada.
}
public void RunOnWindowThread(Action action)
{
action();
}
private sealed class DummyCursor : ICursor
{
public void Dispose()

View File

@@ -118,6 +118,10 @@ namespace Robust.Client.Graphics.Clyde
case CmdWinCursorSet cmd:
WinThreadWinCursorSet(cmd);
break;
case CmdRunAction cmd:
cmd.Action();
break;
}
}
@@ -169,6 +173,11 @@ namespace Robust.Client.Graphics.Clyde
}
}
public void RunOnWindowThread(Action action)
{
SendCmd(new CmdRunAction(action));
}
private abstract record CmdBase;
private sealed record CmdTerminate : CmdBase;
@@ -245,6 +254,10 @@ namespace Robust.Client.Graphics.Clyde
private sealed record CmdCursorDestroy(
ClydeHandle Cursor
) : CmdBase;
private sealed record CmdRunAction(
Action Action
) : CmdBase;
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Robust.Client.Input;
using Robust.Shared.Maths;
using SixLabors.ImageSharp;
@@ -58,6 +59,9 @@ namespace Robust.Client.Graphics.Clyde
void GLMakeContextCurrent(WindowReg? reg);
void GLSwapInterval(int interval);
unsafe void* GLGetProcAddress(string procName);
// Misc
void RunOnWindowThread(Action a);
}
}
}

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Utility;
using SharpFont;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using TerraFX.Interop.Windows;
namespace Robust.Client.Graphics
{
@@ -58,6 +59,14 @@ namespace Robust.Client.Graphics
return instance;
}
public void ClearFontCache()
{
foreach (var fontInstance in _loadedInstances)
{
fontInstance.Value.ClearSizeData();
}
}
private ScaledFontData _generateScaledDatum(FontInstanceHandle instance, float scale)
{
var ftFace = instance.FaceHandle.Face;
@@ -246,6 +255,18 @@ namespace Robust.Client.Graphics
FaceHandle = faceHandle;
}
public void ClearSizeData()
{
foreach (var scaleData in _scaledData)
{
foreach (var ownedTexture in scaleData.Value.AtlasTextures)
{
ownedTexture.Dispose();
}
}
_scaledData.Clear();
}
public Texture? GetCharTexture(Rune codePoint, float scale)
{
var glyph = GetGlyph(codePoint);

View File

@@ -61,5 +61,9 @@ namespace Robust.Client.Graphics
/// <returns>Null if not running on X11.</returns>
uint? GetX11WindowId();
void RegisterGridEcsEvents();
void RunOnWindowThread(Action action);
}
}

View File

@@ -5,9 +5,8 @@ namespace Robust.Client.Graphics
{
public interface IFontManager
{
public void ClearFontCache();
}
internal interface IFontManagerInternal : IFontManager
{
IFontFaceHandle Load(Stream stream);

View File

@@ -152,9 +152,12 @@ namespace Robust.Client.Graphics
/// <summary>
/// Specifies a direction in an RSI state.
/// </summary>
/// <remarks>
/// Value of the enum here matches the index used to store it in the icons array. If this ever changes, then
/// <see cref="GameObjects.SpriteComponent.Layer._rsiDirectionMatrices"/> also needs to be updated.
/// </remarks>
public enum Direction : byte
{
// Value of the enum here matches the index used to store it in the icons array.
South = 0,
North = 1,
East = 2,

View File

@@ -19,7 +19,7 @@ namespace Robust.Client.Graphics
[Dependency] private readonly IResourceCache _resourceCache = default!;
[ViewVariables]
[DataField("id", required: true)]
[IdDataFieldAttribute]
public string ID { get; } = default!;
private ShaderKind Kind;

View File

@@ -497,7 +497,7 @@ namespace Robust.Client.Input
if (robustMapping.TryGet("binds", out var BaseKeyRegsNode))
{
var baseKeyRegs = serializationManager.ReadValueOrThrow<KeyBindingRegistration[]>(BaseKeyRegsNode);
var baseKeyRegs = serializationManager.Read<KeyBindingRegistration[]>(BaseKeyRegsNode);
foreach (var reg in baseKeyRegs)
{
@@ -526,7 +526,7 @@ namespace Robust.Client.Input
if (userData && robustMapping.TryGet("leaveEmpty", out var node))
{
var leaveEmpty = serializationManager.ReadValueOrThrow<BoundKeyFunction[]>(node);
var leaveEmpty = serializationManager.Read<BoundKeyFunction[]>(node);
if (leaveEmpty.Length > 0)
{

View File

@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -22,11 +22,20 @@ namespace Robust.Client.Map
public Texture TileTextureAtlas => _tileTextureAtlas ?? Texture.Transparent;
private readonly Dictionary<ushort, Box2> _tileRegions = new();
private readonly Dictionary<ushort, Box2[]> _tileRegions = new();
public Box2? TileAtlasRegion(Tile tile)
public Box2 ErrorTileRegion { get; private set; }
/// <inheritdoc />
public Box2[]? TileAtlasRegion(Tile tile)
{
if (_tileRegions.TryGetValue(tile.TypeId, out var region))
return TileAtlasRegion(tile.TypeId);
}
/// <inheritdoc />
public Box2[]? TileAtlasRegion(ushort tileType)
{
if (_tileRegions.TryGetValue(tileType, out var region))
{
return region;
}
@@ -51,39 +60,79 @@ namespace Robust.Client.Map
const int tileSize = EyeManager.PixelsPerMeter;
var dimensionX = (int) Math.Ceiling(Math.Sqrt(defList.Count));
var dimensionY = (int) Math.Ceiling((float) defList.Count / dimensionX);
var tileCount = defList.Select(x => (int)x.Variants).Sum() + 1;
var sheet = new Image<Rgba32>(dimensionX * tileSize, dimensionY * tileSize);
var dimensionX = (int) Math.Ceiling(Math.Sqrt(tileCount));
var dimensionY = (int) Math.Ceiling((float) tileCount / dimensionX);
for (var i = 0; i < defList.Count; i++)
var imgWidth = dimensionX * tileSize;
var imgHeight = dimensionY * tileSize;
var sheet = new Image<Rgba32>(imgWidth, imgHeight);
// Add in the missing tile texture sprite as tile texture 0.
{
var def = defList[i];
var column = i % dimensionX;
var row = i / dimensionX;
var w = (float) sheet.Width;
var h = (float) sheet.Height;
ErrorTileRegion = Box2.FromDimensions(
0, (h - EyeManager.PixelsPerMeter) / h,
tileSize / w, tileSize / h);
Image<Rgba32> image;
using (var stream = _resourceCache.ContentFileRead("/Textures/noTile.png"))
{
image = Image.Load<Rgba32>(stream);
}
image.Blit(new UIBox2i(0, 0, tileSize, tileSize), sheet, Vector2i.Zero);
}
if (imgWidth >= 2048 || imgHeight >= 2048)
{
// Sanity warning, some machines don't have textures larger than this and need multiple atlases.
Logger.WarningS("clyde",
$"Tile texture atlas is ({imgWidth} x {imgHeight}), larger than 2048 x 2048. If you really need {tileCount} tiles, file an issue on RobustToolbox.");
}
var column = 1;
var row = 0;
foreach (var def in defList)
{
Image<Rgba32> image;
using (var stream = _resourceCache.ContentFileRead(new ResourcePath(def.Path) / $"{def.SpriteName}.png"))
{
image = Image.Load<Rgba32>(stream);
}
if (image.Width != tileSize || image.Height != tileSize)
if (image.Width != (tileSize * def.Variants) || image.Height != tileSize)
{
throw new NotSupportedException("Unable to use tiles with a dimension other than 32x32.");
throw new NotSupportedException(
$"Unable to load {new ResourcePath(def.Path) / $"{def.SpriteName}.png"}, due to being unable to use tile texture with a dimension other than {tileSize}x({tileSize} * Variants).");
}
var point = new Vector2i(column * tileSize, row * tileSize);
var regionList = new Box2[def.Variants];
image.Blit(new UIBox2i(0, 0, image.Width, image.Height), sheet, point);
for (var j = 0; j < def.Variants; j++)
{
var point = new Vector2i(column * tileSize, row * tileSize);
var w = (float) sheet.Width;
var h = (float) sheet.Height;
var box = new UIBox2i(0, 0, tileSize, tileSize).Translated(new Vector2i(j * tileSize, 0));
image.Blit(box, sheet, point);
_tileRegions.Add(def.TileId,
Box2.FromDimensions(
var w = (float) sheet.Width;
var h = (float) sheet.Height;
regionList[j] = Box2.FromDimensions(
point.X / w, (h - point.Y - EyeManager.PixelsPerMeter) / h,
tileSize / w, tileSize / h));
tileSize / w, tileSize / h);
column++;
if (column >= dimensionX)
{
column = 0;
row++;
}
}
_tileRegions.Add(def.TileId, regionList);
}
_tileTextureAtlas = Texture.LoadFromImage(sheet, "Tile Atlas");

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -9,6 +10,8 @@ namespace Robust.Client.Map
/// </summary>
internal interface IClydeTileDefinitionManager : ITileDefinitionManager
{
Box2 ErrorTileRegion { get; }
/// <summary>
/// The texture atlas containing all the tiles.
/// </summary>
@@ -18,6 +21,12 @@ namespace Robust.Client.Map
/// Gets the region inside the texture atlas to use to draw a tile.
/// </summary>
/// <returns>If null, do not draw the tile at all.</returns>
Box2? TileAtlasRegion(Tile tile);
Box2[]? TileAtlasRegion(Tile tile);
/// <summary>
/// Gets the region inside the texture atlas to use to draw a tile type.
/// </summary>
/// <returns>If null, do not draw the tile at all.</returns>
Box2[]? TileAtlasRegion(ushort tileType);
}
}

View File

@@ -37,7 +37,7 @@ namespace Robust.Client.Physics
SimulateWorld((float) diff.TotalSeconds, true);
}
protected override void HandleMapCreated(object? sender, MapEventArgs eventArgs)
protected override void HandleMapCreated(MapChangedEvent eventArgs)
{
if (eventArgs.Map == MapId.Nullspace) return;
var mapUid = MapManager.GetMapEntityId(eventArgs.Map);

View File

@@ -31,7 +31,7 @@ namespace Robust.Client.Placement.Modes
var mapId = MouseCoords.GetMapId(pManager.EntityManager);
var snapToEntities = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(MouseCoords, SnapToRange)
var snapToEntities = EntitySystem.Get<EntityLookupSystem>().GetEntitiesInRange(MouseCoords, SnapToRange)
.Where(entity => pManager.EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype == pManager.CurrentPrototype && pManager.EntityManager.GetComponent<TransformComponent>(entity).MapID == mapId)
.OrderBy(entity => (pManager.EntityManager.GetComponent<TransformComponent>(entity).WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager)).LengthSquared)
.ToList();

View File

@@ -53,7 +53,7 @@ namespace Robust.Client.Placement.Modes
var topRight = new Vector2(CurrentTile.X + 0.99f, CurrentTile.Y + 0.99f);
var box = new Box2(bottomLeft, topRight);
return !IoCManager.Resolve<IEntityLookup>().AnyEntitiesIntersecting(map, box);
return !EntitySystem.Get<EntityLookupSystem>().AnyEntitiesIntersecting(map, box);
}
}
}

View File

@@ -22,7 +22,7 @@ using Robust.Shared.Utility;
namespace Robust.Client.Placement
{
public sealed partial class PlacementManager : IPlacementManager, IDisposable
public sealed partial class PlacementManager : IPlacementManager, IDisposable, IEntityEventSubscriber
{
[Dependency] private readonly IClientNetManager NetworkManager = default!;
[Dependency] public readonly IPlayerManager PlayerManager = default!;
@@ -185,7 +185,7 @@ namespace Robust.Client.Placement
_modeDictionary.Add(type.Name, type);
}
MapManager.TileChanged += HandleTileChanged;
EntityManager.EventBus.SubscribeEvent<TileChangedEvent>(EventSource.Local, this, HandleTileChanged);
_drawOverlay = new PlacementOverlay(this);
_overlayManager.AddOverlay(_drawOverlay);
@@ -328,7 +328,7 @@ namespace Robust.Client.Placement
}
}
private void HandleTileChanged(object? sender, TileChangedEventArgs args)
private void HandleTileChanged(TileChangedEvent args)
{
var coords = MapManager.GetGrid(args.NewTile.GridIndex).GridTileToLocal(args.NewTile.GridIndices);
_pendingTileChanges.RemoveAll(c => c.Item1 == coords);

View File

@@ -230,7 +230,7 @@ namespace Robust.Client.ResourceManagement
using (var manifestFile = cache.ContentFileRead(manifestPath))
{
if (manifestFile.Length <= 4096)
if (manifestFile.CanSeek && manifestFile.Length <= 4096)
{
// Most RSIs are actually tiny so if that's the case just load them into a stackalloc buffer.
// Avoids a ton of allocations with stream reader etc

View File

@@ -13,8 +13,8 @@
<Import Project="..\MSBuild\Robust.DefineConstants.targets" />
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.3" />
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.2" />
<PackageReference Include="nfluidsynth" Version="0.3.1" />
<PackageReference Include="NVorbis" Version="0.10.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
@@ -25,9 +25,9 @@
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" />
</ItemGroup>
<ItemGroup Condition="'$(EnableClientScripting)' == 'True'">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="3.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
</ItemGroup>
@@ -53,6 +53,7 @@
<Compile Update="UserInterface\CustomControls\DefaultWindow.xaml.cs">
<DependentUpon>DefaultWindow.xaml</DependentUpon>
</Compile>
<Compile Remove="Debugging\IDebugDrawing.cs" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Engine.targets" />
<PropertyGroup>

View File

@@ -1,10 +1,10 @@
using System;
using Robust.Client.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.Manager.Result;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Validation;
@@ -16,26 +16,40 @@ namespace Robust.Client.Serialization
[TypeSerializer]
public sealed class AppearanceVisualizerSerializer : ITypeSerializer<AppearanceVisualizer, MappingDataNode>
{
public DeserializationResult Read(ISerializationManager serializationManager, MappingDataNode node,
public AppearanceVisualizer Read(ISerializationManager serializationManager, MappingDataNode node,
IDependencyCollection dependencies,
bool skipHook,
ISerializationContext? context = null)
ISerializationContext? context = null, AppearanceVisualizer? value = null)
{
Type? type = null;
if (!node.TryGet("type", out var typeNode))
throw new InvalidMappingException("No type specified for AppearanceVisualizer!");
{
if (value == null)
throw new InvalidMappingException("No type specified for AppearanceVisualizer!");
if (typeNode is not ValueDataNode typeValueDataNode)
throw new InvalidMappingException("Type node not a value node for AppearanceVisualizer!");
type = value.GetType();
}
else
{
if (typeNode is not ValueDataNode typeValueDataNode)
throw new InvalidMappingException("Type node not a value node for AppearanceVisualizer!");
var type = IoCManager.Resolve<IReflectionManager>()
.YamlTypeTagLookup(typeof(AppearanceVisualizer), typeValueDataNode.Value);
if (type == null)
throw new InvalidMappingException(
$"Invalid type {typeValueDataNode.Value} specified for AppearanceVisualizer!");
type = IoCManager.Resolve<IReflectionManager>()
.YamlTypeTagLookup(typeof(AppearanceVisualizer), typeValueDataNode.Value);
if (type == null)
throw new InvalidMappingException(
$"Invalid type {typeValueDataNode.Value} specified for AppearanceVisualizer!");
if(value != null && !type.IsInstanceOfType(value))
{
throw new InvalidMappingException(
$"Specified Type does not match type of provided Value for AppearanceVisualizer! (TypeOfValue: {value.GetType()}, ProvidedValue: {type})");
}
}
var newNode = node.Copy();
newNode.Remove("type");
return serializationManager.Read(type, newNode, context, skipHook);
return (AppearanceVisualizer) serializationManager.Read(type, newNode, context, skipHook, value)!;
}
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,

View File

@@ -1,4 +1,6 @@
using Robust.Client.Graphics;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Robust.Client.UserInterface.Controls
{
@@ -9,6 +11,30 @@ namespace Robust.Client.UserInterface.Controls
Window = window;
}
/// <summary>
/// Enable the UI autoscale system, this will scale down the UI for lower resolutions
/// </summary>
[ViewVariables]
public bool AutoScale { get; set; } = false;
/// <summary>
/// Minimum resolution to start clamping autoscale to 1
/// </summary>
[ViewVariables]
public Vector2i AutoScaleUpperCutoff { get; set; } = new Vector2i(1080, 720);
/// <summary>
/// Maximum resolution to start clamping autos scale to autoscale minimum
/// </summary>
[ViewVariables]
public Vector2i AutoScaleLowerCutoff { get; set; } = new Vector2i(520, 520);
/// <summary>
/// The minimum ui scale value that autoscale will scale to
/// </summary>
[ViewVariables]
public float AutoScaleMinimum { get; set; } = 0.5f;
public override float UIScale => UIScaleSet;
internal float UIScaleSet { get; set; }
public override IClydeWindow Window { get; }

View File

@@ -102,13 +102,18 @@ Mouse Pos:
var playerScreen = _eyeManager.WorldToScreen(playerWorldOffset.Position);
var playerCoordinates = entityTransform.Coordinates;
var playerRotation = entityTransform.WorldRotation;
Angle gridRotation = _mapManager.TryGetGrid(entityTransform.GridID, out var grid) ? grid.WorldRotation : Angle.Zero;
stringBuilder.AppendFormat(@" Screen: {0}
{1}
{2}
EntId: {3}
GridID: {4}", playerScreen, playerWorldOffset, playerCoordinates, entityTransform.Owner,
entityTransform.GridID);
Rotation: {3:F2}°
EntId: {4}
GridID: {5}
Grid Rotation: {6:F2}°", playerScreen, playerWorldOffset, playerCoordinates, playerRotation.Degrees, entityTransform.Owner,
entityTransform.GridID, gridRotation.Degrees);
}
if (controlHovered != null)

View File

@@ -228,7 +228,7 @@ namespace Robust.Client.UserInterface.CustomControls
foreach (var prototype in prototypeManager.EnumeratePrototypes<EntityPrototype>())
{
if (prototype.Abstract)
if (prototype.NoSpawn || prototype.Abstract)
{
continue;
}

View File

@@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using Robust.Client.Graphics;
using Robust.Shared;
using Robust.Shared.Console;
using Robust.Shared.Asynchronous;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -164,12 +165,11 @@ namespace Robust.Client.UserInterface
{
if (OperatingSystem.IsMacOS())
{
// macOS seems pretty annoying about having the file dialog opened from the main thread.
// So we are forced to execute this synchronously on the main thread.
// Also I'm calling RunOnMainThread here to provide safety in case this is ran from a different thread.
// nativefiledialog doesn't provide any form of async API, so this WILL lock up the client.
// macOS seems pretty annoying about having the file dialog opened from the main windowing thread.
// So we are forced to execute this synchronously on the main windowing thread.
// nativefiledialog doesn't provide any form of async API, so this WILL lock up half the client.
var tcs = new TaskCompletionSource<string?>();
_taskManager.RunOnMainThread(() => tcs.SetResult(action()));
_clyde.RunOnWindowThread(() => tcs.SetResult(action()));
return tcs.Task;
}
@@ -388,4 +388,17 @@ namespace Robust.Client.UserInterface
SW_NFD_CANCEL,
}
}
public sealed class OpenFileCommand : IConsoleCommand
{
public string Command => "testopenfile";
public string Description => "";
public string Help => "";
public async void Execute(IConsoleShell shell, string argStr, string[] args)
{
var stream = await IoCManager.Resolve<IFileDialogManager>().OpenFile();
stream?.Dispose();
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Client.Graphics;
@@ -23,6 +23,7 @@ namespace Robust.Client.UserInterface
internal sealed class UserInterfaceManager : IUserInterfaceManagerInternal
{
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IFontManager _fontManager = default!;
[Dependency] private readonly IClydeInternal _clyde = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -371,16 +372,21 @@ namespace Robust.Client.UserInterface
}
}
ReleaseKeyboardFocus();
if (hit == null)
{
ReleaseKeyboardFocus();
hitData = null;
return false;
}
var (control, rel) = hit.Value;
if (control != KeyboardFocused)
{
ReleaseKeyboardFocus();
}
ControlFocused = control;
if (ControlFocused.CanKeyboardFocus && ControlFocused.KeyboardFocusOnClick)
@@ -951,14 +957,37 @@ namespace Robust.Client.UserInterface
private void WindowContentScaleChanged(WindowContentScaleEventArgs args)
{
if (_windowsToRoot.TryGetValue(args.Window.Id, out var root))
{
UpdateUIScale(root);
_fontManager.ClearFontCache();
}
}
private float CalculateAutoScale(WindowRoot root)
{
//Grab the OS UIScale or the value set through CVAR debug
var osScale = _configurationManager.GetCVar(CVars.DisplayUIScale);
osScale = osScale == 0f ? root.Window.ContentScale.X : osScale;
var windowSize = root.Window.RenderTarget.Size;
//Only run autoscale if it is enabled, otherwise default to just use OS UIScale
if (!root.AutoScale && (windowSize.X <= 0 || windowSize.Y <= 0)) return osScale;
var maxScaleRes = root.AutoScaleUpperCutoff;
var minScaleRes = root.AutoScaleLowerCutoff;
var autoScaleMin = root.AutoScaleMinimum;
float scaleRatioX;
float scaleRatioY;
//Calculate the scale ratios and clamp it between the maximums and minimums
scaleRatioX = Math.Clamp(((float) windowSize.X - minScaleRes.X) / (maxScaleRes.X - minScaleRes.X) * osScale, autoScaleMin, osScale);
scaleRatioY = Math.Clamp(((float) windowSize.Y - minScaleRes.Y) / (maxScaleRes.Y - minScaleRes.Y) * osScale, autoScaleMin, osScale);
//Take the smallest UIScale value and use it for UI scaling
return Math.Min(scaleRatioX, scaleRatioY);
}
private void UpdateUIScale(WindowRoot root)
{
var newVal = _configurationManager.GetCVar(CVars.DisplayUIScale);
root.UIScaleSet = newVal == 0f ? root.Window.ContentScale.X : newVal;
root.UIScaleSet = CalculateAutoScale(root);
_propagateUIScaleChanged(root);
root.InvalidateMeasure();
}
@@ -977,7 +1006,7 @@ namespace Robust.Client.UserInterface
{
if (!_windowsToRoot.TryGetValue(windowResizedEventArgs.Window.Id, out var root))
return;
UpdateUIScale(root);
root.InvalidateMeasure();
}

View File

@@ -22,6 +22,7 @@ namespace Robust.Client.Utility
.Texture;
}
[Obsolete("Use SpriteSystem")]
public static RSI.State GetState(this SpriteSpecifier.Rsi rsiSpecifier, IResourceCache cache)
{
if (cache.TryGetResource<RSIResource>(
@@ -36,6 +37,7 @@ namespace Robust.Client.Utility
return SpriteComponent.GetFallbackState(cache);
}
[Obsolete("Use SpriteSystem")]
public static Texture Frame0(this SpriteSpecifier specifier)
{
return specifier.RsiStateLike().Default;
@@ -80,6 +82,7 @@ namespace Robust.Client.Utility
return specifier.RsiStateLike();
}
[Obsolete("Use SpriteSystem")]
public static IRsiStateLike RsiStateLike(this SpriteSpecifier specifier)
{
var resC = IoCManager.Resolve<IResourceCache>();

View File

@@ -0,0 +1 @@
# Add your introductions here!

View File

@@ -0,0 +1,2 @@
- name: Example-article
href: example-article.md

214
Robust.Docfx/docfx.json Normal file
View File

@@ -0,0 +1,214 @@
{
"metadata": [
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.UnitTesting"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.UnitTesting"
},
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.Shared"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.Shared"
},
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.Shared.Maths"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.Shared.Maths"
},
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.Shared.Scripting"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.Shared.Scripting"
},
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.Client"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.Client"
},
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.Client.WebView"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.Client.WebView"
},
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.Server"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.Server"
},
{
"src":
[
{
"files": [
"**.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**",
"_site/**",
"**.xaml"
],
"src": "../Robust.LoaderApi"
}
],
"disableGitFeatures": false,
"disableDefaultFilter": false,
"dest": "api/Robust.LoaderApi/Robust.LoaderApi"
}
],
"build": {
"content": [
{
"files": [
"api/**/**.yml"
]
},
{
"files": [
"articles/**.md",
"articles/**/toc.yml",
"toc.yml",
"*.md"
]
}
],
"resource": [
{
"files": [
"images/**",
"favicon.ico",
"icon.svg"
]
}
],
"overwrite": [
{
"files": [
"apidoc/**.md"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
"dest": "_robust-site",
"globalMetadataFiles": [],
"fileMetadataFiles": [],
"template": [
"default",
"templates/darkfx"
],
"postProcessors": [],
"markdownEngineName": "markdig",
"noLangKeyword": false,
"keepFileLink": false,
"cleanupCacheHistory": false,
"disableGitFeatures": false
}
}

6
Robust.Docfx/index.md Normal file
View File

@@ -0,0 +1,6 @@
# RobustToolbox DocFX
![](https://i.imgur.com/Sv3Swxg.png)
## Welcome to the RobustToolbox DocFX instance.
### Click one of the tabs above to see documentation for that particular project/namespace

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Steffen Wilke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,40 @@
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
<div class="hidden-sm col-md-2" role="complementary">
<div class="sideaffix">
{{^_disableContribution}}
<div class="contribution">
<ul class="nav">
{{#docurl}}
<li>
<a href="{{docurl}}" class="contribution-link">{{__global.improveThisDoc}}</a>
</li>
{{/docurl}}
{{#sourceurl}}
<li>
<a href="{{sourceurl}}" class="contribution-link">{{__global.viewSource}}</a>
</li>
{{/sourceurl}}
</ul>
</div>
{{/_disableContribution}}
<div class="toggle-mode">
<div class="icon">
<i aria-hidden="true">☀</i>
</div>
<label class="switch">
<input type="checkbox" id="switch-style">
<span class="slider round"></span>
</label>
<div class="icon">
<i aria-hidden="true">☾</i>
</div>
</div>
<nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
<h5>{{__global.inThisArticle}}</h5>
<div></div>
<!-- <p><a class="back-to-top" href="#top">Back to top</a><p> -->
</nav>
</div>
</div>

View File

@@ -0,0 +1,108 @@
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
<h1 id="{{id}}" data-uid="{{uid}}" class="text-break">{{>partials/title}}</h1>
<div class="markdown level0 summary">{{{summary}}}</div>
<div class="markdown level0 conceptual">{{{conceptual}}}</div>
{{#inClass}}
<div class="inheritance">
<h5>{{__global.inheritance}}</h5>
{{#inheritance}}
<div class="level{{index}}">{{{specName.0.value}}}</div>
{{/inheritance}}
<div class="level{{level}}"><span class="xref">{{name.0.value}}</span></div>
{{#derivedClasses}}
<div class="level{{index}}">{{{specName.0.value}}}</div>
{{/derivedClasses}}
</div>
{{/inClass}}
{{#implements.0}}
<div classs="implements">
<h5>{{__global.implements}}</h5>
{{/implements.0}}
{{#implements}}
<div>{{{specName.0.value}}}</div>
{{/implements}}
{{#implements.0}}
</div>
{{/implements.0}}
{{#inheritedMembers.0}}
<div class="inheritedMembers">
</div>
{{/inheritedMembers.0}}
<h6><strong>{{__global.namespace}}</strong>: {{{namespace.specName.0.value}}}</h6>
<h6><strong>{{__global.assembly}}</strong>: {{assemblies.0}}.dll</h6>
<h5 id="{{id}}_syntax">{{__global.syntax}}</h5>
<div class="codewrapper">
<pre><code class="lang-{{_lang}} hljs">{{syntax.content.0.value}}</code></pre>
</div>
{{#syntax.parameters.0}}
<h5 class="parameters">{{__global.parameters}}</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>{{__global.type}}</th>
<th>{{__global.name}}</th>
<th>{{__global.description}}</th>
</tr>
</thead>
<tbody>
{{/syntax.parameters.0}}
{{#syntax.parameters}}
<tr>
<td>{{{type.specName.0.value}}}</td>
<td><span class="parametername">{{{id}}}</span></td>
<td>{{{description}}}</td>
</tr>
{{/syntax.parameters}}
{{#syntax.parameters.0}}
</tbody>
</table>
{{/syntax.parameters.0}}
{{#syntax.return}}
<h5 class="returns">{{__global.returns}}</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>{{__global.type}}</th>
<th>{{__global.description}}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{{type.specName.0.value}}}</td>
<td>{{{description}}}</td>
</tr>
</tbody>
</table>
{{/syntax.return}}
{{#syntax.typeParameters.0}}
<h5 class="typeParameters">{{__global.typeParameters}}</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>{{__global.name}}</th>
<th>{{__global.description}}</th>
</tr>
</thead>
<tbody>
{{/syntax.typeParameters.0}}
{{#syntax.typeParameters}}
<tr>
<td><span class="parametername">{{{id}}}</span></td>
<td>{{{description}}}</td>
</tr>
{{/syntax.typeParameters}}
{{#syntax.typeParameters.0}}
</tbody>
</table>
{{/syntax.typeParameters.0}}
{{#remarks}}
<h5 id="{{id}}_remarks"><strong>{{__global.remarks}}</strong></h5>
<div class="markdown level0 remarks">{{{remarks}}}</div>
{{/remarks}}
{{#example.0}}
<h5 id="{{id}}_examples"><strong>{{__global.examples}}</strong></h5>
{{/example.0}}
{{#example}}
{{{.}}}
{{/example}}

View File

@@ -0,0 +1,29 @@
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
<footer>
<div class="grad-bottom"></div>
<div class="footer">
<div class="container">
<span class="pull-right">
<a href="#top">Back to top</a>
</span>
<div class="pull-left">
{{{_appFooter}}}
{{^_appFooter}}<span>Generated by <strong>DocFX</strong></span>{{/_appFooter}}
</div>
<div class="toggle-mode pull-right visible-sm visible-xs">
<div class="icon">
<i aria-hidden="true">☀</i>
</div>
<label class="switch">
<input type="checkbox" id="switch-style-m">
<span class="slider round"></span>
</label>
<div class="icon">
<i aria-hidden="true">☾</i>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="{{_rel}}styles/toggle-theme.js"></script>
</footer>

View File

@@ -0,0 +1,20 @@
{{!Copyright (c) Oscar Vasquez. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
<meta name="viewport" content="width=device-width">
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
<meta name="generator" content="docfx {{_docfxVersion}}">
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
<link rel="shortcut icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
<link rel="stylesheet" href="{{_rel}}styles/main.css">
<meta property="docfx:navrel" content="{{_navRel}}">
<meta property="docfx:tocrel" content="{{_tocRel}}">
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
{{#_enableSearch}}<meta property="docfx:rel" content="{{_rel}}">{{/_enableSearch}}
{{#_enableNewTab}}<meta property="docfx:newtab" content="true">{{/_enableNewTab}}
</head>

View File

@@ -0,0 +1,470 @@
:root, body.dark-theme {
--color-foreground: #ccd5dc;
--color-navbar: #66666d;
--color-breadcrumb: #999;
--color-underline: #ddd;
--color-toc-hover: #fff;
--color-background: #2d2d30;
--color-background-subnav: #333337;
--color-background-dark: #1e1e1e;
--color-background-table-alt: #212123;
--color-background-quote: #69696e;
}
body.light-theme {
--color-foreground: #171717;
--color-breadcrumb: #4a4a4a;
--color-toc-hover: #4c4c4c;
--color-background: #ffffff;
--color-background-subnav: #f5f5f5;
--color-background-dark: #ddd;
--color-background-table-alt: #f9f9f9;
}
body {
color: var(--color-foreground);
line-height: 1.5;
font-size: 14px;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
word-wrap: break-word;
background-color: var(--color-background);
}
.btn.focus, .btn:focus, .btn:hover {
color: var(--color-foreground);
}
h1 {
font-weight: 600;
font-size: 32px;
}
h2 {
font-weight: 600;
font-size: 24px;
line-height: 1.8;
}
h3 {
font-weight: 600;
font-size: 20px;
line-height: 1.8;
}
h5 {
font-size: 14px;
padding: 10px 0px;
}
article h1, article h2, article h3, article h4 {
margin-top: 35px;
margin-bottom: 15px;
}
article h4 {
padding-bottom: 8px;
border-bottom: 2px solid var(--color-underline);
}
.navbar-brand>img {
color: var(--color-background);
}
.navbar {
border: none;
}
.subnav {
border-top: 1px solid var(--color-underline);
background-color: var(--color-background-subnav);
}
.sidenav, .fixed_header, .toc {
background-color: var(--color-background);
}
.navbar-inverse {
background-color: var(--color-background-dark);
z-index: 100;
}
.navbar-inverse .navbar-nav>li>a, .navbar-inverse .navbar-text {
color: var(--color-navbar);
background-color: var(--color-background-dark);
border-bottom: 3px solid transparent;
padding-bottom: 12px;
}
.navbar-inverse .navbar-nav>li>a:focus, .navbar-inverse .navbar-nav>li>a:hover {
color: var(--color-foreground);
background-color: var(--color-background-dark);
border-bottom: 3px solid var(--color-background-subnav);
transition: all ease 0.25s;
}
.navbar-inverse .navbar-nav>.active>a, .navbar-inverse .navbar-nav>.active>a:focus, .navbar-inverse .navbar-nav>.active>a:hover {
color: var(--color-foreground);
background-color: var(--color-background-dark);
border-bottom: 3px solid var(--color-foreground);
transition: all ease 0.25s;
}
.navbar-form .form-control {
border: none;
border-radius: 0;
}
.light-theme .navbar-brand svg {
filter: brightness(20%);
}
.toc .level1>li {
font-weight: 400;
}
.toc .nav>li>a {
color: var(--color-foreground);
}
.sidefilter {
background-color: var(--color-background);
border-left: none;
border-right: none;
}
.sidefilter {
background-color: var(--color-background);
border-left: none;
border-right: none;
}
.toc-filter {
padding: 10px;
margin: 0;
background-color: var(--color-background);
}
.toc-filter>input {
border: none;
border-radius: unset;
background-color: var(--color-background-subnav);
padding: 5px 0 5px 20px;
font-size: 90%
}
.toc-filter>.clear-icon {
position: absolute;
top: 17px;
right: 15px;
}
.toc-filter>input:focus {
color: var(--color-foreground);
transition: all ease 0.25s;
}
.toc-filter>.filter-icon {
display: none;
}
.sidetoc>.toc {
background-color: var(--color-background);
overflow-x: hidden;
}
.sidetoc {
background-color: var(--color-background);
border: none;
}
.alert {
background-color: inherit;
border: none;
padding: 10px 0;
border-radius: 0;
}
.alert>p {
margin-bottom: 0;
padding: 5px 10px;
border-bottom: 1px solid;
background-color: var(--color-background-dark);
}
.alert>h5 {
padding: 10px 15px;
margin-top: 0;
margin-bottom: 0;
text-transform: uppercase;
font-weight: bold;
border-top: 2px solid;
background-color: var(--color-background-dark);
border-radius: none;
}
.alert>ul {
margin-bottom: 0;
padding: 5px 40px;
}
.alert-info {
color: #1976d2;
}
.alert-warning {
color: #f57f17;
}
.alert-danger {
color: #d32f2f;
}
pre {
padding: 9.5px;
margin: 0 0 10px;
font-size: 13px;
word-break: break-all;
word-wrap: break-word;
background-color: var(--color-background-dark);
border-radius: 0;
border: none;
}
code {
background: var(--color-background-dark) !important;
border-radius: 2px;
}
.hljs {
color: var(--color-foreground);
}
.toc .nav>li.active>.expand-stub::before, .toc .nav>li.in>.expand-stub::before, .toc .nav>li.in.active>.expand-stub::before, .toc .nav>li.filtered>.expand-stub::before {
content: "▾";
}
.toc .nav>li>.expand-stub::before, .toc .nav>li.active>.expand-stub::before {
content: "▸";
}
.affix ul ul>li>a:before {
content: "|";
}
.breadcrumb {
background-color: var(--color-background-subnav);
}
.breadcrumb .label.label-primary {
background: #444;
border-radius: 0;
font-weight: normal;
font-size: 100%;
}
#breadcrumb .breadcrumb>li a {
border-radius: 0;
font-weight: normal;
font-size: 85%;
display: inline;
padding: 0 .6em 0;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
color: var(--color-breadcrumb);
}
#breadcrumb .breadcrumb>li a:hover {
color: var(--color-foreground);
transition: all ease 0.25s;
}
.breadcrumb>li+li:before {
content: "⯈";
font-size: 75%;
color: var(--color-background-dark);
padding: 0;
}
.light-theme .breadcrumb>li+li:before {
color: var(--color-foreground)
}
.toc .level1>li {
font-weight: 600;
font-size: 130%;
padding-left: 5px;
}
.footer {
border-top: none;
background-color: var(--color-background-dark);
padding: 15px 0;
font-size: 90%;
}
.toc .nav>li>a:hover, .toc .nav>li>a:focus {
color: var(--color-toc-hover);
transition: all ease 0.1s;
}
.form-control {
background-color: var(--color-background-subnav);
border: none;
border-radius: 0;
-webkit-box-shadow: none;
box-shadow: none;
}
.form-control:focus {
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: none;
box-shadow: none;
}
input#search-query:focus {
color: var(--color-foreground);
}
.table-bordered, .table-bordered>tbody>tr>td, .table-bordered>tbody>tr>th, .table-bordered>tfoot>tr>td, .table-bordered>tfoot>tr>th, .table-bordered>thead>tr>td, .table-bordered>thead>tr>th {
border: 1px solid var(--color-background-dark);
}
.table-striped>tbody>tr:nth-of-type(odd) {
background-color: var(--color-background-table-alt);
}
blockquote {
padding: 10px 20px;
margin: 0 0 10px;
font-size: 110%;
border-left: 5px solid var(--color-background-quote);
color: var(--color-background-quote);
}
.pagination>.disabled>a, .pagination>.disabled>a:focus, .pagination>.disabled>a:hover, .pagination>.disabled>span, .pagination>.disabled>span:focus, .pagination>.disabled>span:hover {
background-color: var(--color-background-subnav);
border-color: var(--color-background-subnav);
}
.breadcrumb>li, .pagination {
display: inline;
}
.tabGroup a[role="tab"] {
border-bottom: 2px solid var(--color-background-dark);
}
.tabGroup a[role="tab"][aria-selected="true"] {
color: var(--color-foreground);
}
.tabGroup section[role="tabpanel"] {
border: 1px solid var(--color-background-dark);
}
.sideaffix > div.contribution > ul > li > a.contribution-link:hover {
background-color: var(--color-background);
}
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 4px;
bottom: 3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #337ab7;
}
input:focus + .slider {
box-shadow: 0 0 1px #337ab7;
}
input:checked + .slider:before {
-webkit-transform: translateX(19px);
-ms-transform: translateX(19px);
transform: translateX(19px);
}
/* Rounded sliders */
.slider.round {
border-radius: 20px;
}
.slider.round:before {
border-radius: 50%;
}
.toggle-mode .icon {
display: inline-block;
}
.toggle-mode .icon i {
font-style: normal;
font-size: 17px;
display: inline-block;
padding-right: 7px;
padding-left: 7px;
vertical-align: middle;
}
@media (min-width: 1600px) {
.container {
width: 100%;
}
.sidefilter {
width: 18%;
}
.sidetoc {
width: 18%;
}
.article.grid-right {
margin-left: 19%;
}
.sideaffix {
width: 11.5%;
}
.affix ul>li.active>a {
white-space: initial;
}
.affix ul>li>a {
width: 99%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}

View File

@@ -0,0 +1,35 @@
const sw = document.getElementById("switch-style"), sw_mobile = document.getElementById("switch-style-m"), b = document.body;
if (b) {
function toggleTheme(target, dark) {
target.classList.toggle("dark-theme", dark)
target.classList.toggle("light-theme", !dark)
}
function switchEventListener() {
toggleTheme(b, this.checked);
if (window.localStorage) {
this.checked ? localStorage.setItem("theme", "dark-theme") : localStorage.setItem("theme", "light-theme")
}
}
var isDarkTheme = !window.localStorage || !window.localStorage.getItem("theme") || window.localStorage && localStorage.getItem("theme") === "dark-theme";
if(sw && sw_mobile){
sw.checked = isDarkTheme;
sw_mobile.checked = isDarkTheme;
sw.addEventListener("change", switchEventListener);
sw_mobile.addEventListener("change", switchEventListener);
// sync state between switches
sw.addEventListener("change", function() {
sw_mobile.checked = this.checked;
});
sw_mobile.addEventListener("change", function() {
sw.checked = this.checked;
});
}
toggleTheme(b, isDarkTheme);
}

29
Robust.Docfx/toc.yml Normal file
View File

@@ -0,0 +1,29 @@
- name: Articles
href: articles/
# Client
- name: Robust.Client
href: api/Robust.Client/
homepage: api/Robust.Client/Robust.Client.html
- name: Robust.Client.WebView
href: api/Robust.Client.WebView/
#Loader Api
- name: Robust.LoaderApi
href: api/Robust.LoaderApi/Robust.LoaderApi/
# Server
- name: Robust.Server
href: api/Robust.Server/
# Shared
- name: Robust.Shared
href: api/Robust.Shared/
- name: Robust.Shared.Maths
href: api/Robust.Shared.Maths/
- name: Robust.Shared.Scripting
href: api/Robust.Shared.Scripting/
# Unit testing
- name: Robust.UnitTesting
href: api/Robust.UnitTesting/

View File

@@ -66,18 +66,17 @@ namespace Robust.Server
[Dependency] private readonly IConfigurationManagerInternal _config = default!;
[Dependency] private readonly IServerEntityManager _entityManager = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly ILogManager _log = default!;
[Dependency] private readonly IRobustSerializer _serializer = default!;
[Dependency] private readonly IGameTiming _time = default!;
[Dependency] private readonly IResourceManagerInternal _resources = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ITimerManager timerManager = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly IServerGameStateManager _stateManager = default!;
[Dependency] private readonly IServerNetManager _network = default!;
[Dependency] private readonly ISystemConsoleManager _systemConsole = default!;
[Dependency] private readonly ITaskManager _taskManager = default!;
[Dependency] private readonly IRuntimeLog runtimeLog = default!;
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
[Dependency] private readonly IModLoaderInternal _modLoader = default!;
[Dependency] private readonly IWatchdogApi _watchdogApi = default!;
[Dependency] private readonly HubManager _hubManager = default!;
@@ -127,7 +126,7 @@ namespace Robust.Server
else
Logger.InfoS("srv", $"{reason}, shutting down...");
_shutdownReason = reason;
_shutdownReason = reason ?? "Shutting down";
if (_mainLoop != null) _mainLoop.Running = false;
if (_logHandler != null)
@@ -339,12 +338,11 @@ namespace Robust.Server
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
prototypeManager.Initialize();
prototypeManager.LoadDirectory(Options.PrototypeDirectory);
prototypeManager.Resync();
prototypeManager.ResolveResults();
_consoleHost.Initialize();
_entityManager.Startup();
_mapManager.Startup();
IoCManager.Resolve<IEntityLookup>().Startup();
_stateManager.Initialize();
var reg = _entityManager.ComponentFactory.GetRegistration<TransformComponent>();
@@ -603,7 +601,6 @@ namespace Robust.Server
_network.Shutdown($"Server shutting down: {_shutdownReason}");
// shutdown entities
IoCManager.Resolve<IEntityLookup>().Shutdown();
_entityManager.Cleanup();
if (_config.GetCVar(CVars.LogRuntimeLog))
@@ -614,7 +611,7 @@ namespace Robust.Server
Directory.CreateDirectory(relPath);
var pathToWrite = Path.Combine(relPath,
"Runtime-" + DateTime.Now.ToString("yyyy-MM-dd-THH-mm-ss") + ".txt");
File.WriteAllText(pathToWrite, runtimeLog.Display(), EncodingHelpers.UTF8);
File.WriteAllText(pathToWrite, _runtimeLog.Display(), EncodingHelpers.UTF8);
}
AppDomain.CurrentDomain.ProcessExit -= ProcessExiting;
@@ -659,7 +656,8 @@ namespace Robust.Server
using (TickUsage.WithLabels("Timers").NewTimer())
{
timerManager.UpdateTimers(frameEventArgs);
_consoleHost.CommandBufferExecute();
_timerManager.UpdateTimers(frameEventArgs);
}
using (TickUsage.WithLabels("AsyncTasks").NewTimer())
@@ -670,8 +668,6 @@ namespace Robust.Server
// Pass Histogram into the IEntityManager.Update so it can do more granular measuring.
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds, noPredictions: false, TickUsage);
_lookup.Update();
using (TickUsage.WithLabels("PostEngine").NewTimer())
{
_modLoader.BroadcastUpdate(ModUpdateLevel.PostEngine, frameEventArgs);

View File

@@ -311,7 +311,7 @@ namespace Robust.Server.Bql
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var radius = (float)(double)arguments[0];
var entityLookup = IoCManager.Resolve<IEntityLookup>();
var entityLookup = EntitySystem.Get<EntityLookupSystem>();
// TODO: Make this a foreach and reduce LINQ chain because it'll allocate a LOT
//BUG: GetEntitiesInRange effectively uses manhattan distance. This is not intended, near is supposed to be circular.

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