mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 20:20:46 +01:00
Compare commits
155 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd18454358 | ||
|
|
70f92ebd87 | ||
|
|
2694dce076 | ||
|
|
8960d1d995 | ||
|
|
0a4683d33e | ||
|
|
379bcfabe0 | ||
|
|
1d91838166 | ||
|
|
a5d4b8096f | ||
|
|
a77eee5658 | ||
|
|
156187a0dd | ||
|
|
852f002f59 | ||
|
|
9dc49c1904 | ||
|
|
1995b13e5d | ||
|
|
f985d10ed9 | ||
|
|
ae6cebbfbb | ||
|
|
ef0bc1a2e4 | ||
|
|
72ba484f5b | ||
|
|
a70e511fcb | ||
|
|
e7f9e95525 | ||
|
|
bd908f9db6 | ||
|
|
f8cb1729a3 | ||
|
|
fd9d5c8aa8 | ||
|
|
4677296934 | ||
|
|
708f5dd376 | ||
|
|
4a06acda32 | ||
|
|
e7beb2032b | ||
|
|
c7bd75f800 | ||
|
|
b4165e8661 | ||
|
|
ad339b5bfd | ||
|
|
e1197af8ce | ||
|
|
102cadf3a6 | ||
|
|
e7723b61bc | ||
|
|
a9d17337a3 | ||
|
|
74622bac83 | ||
|
|
a3047b1687 | ||
|
|
3a55118143 | ||
|
|
3c5fbc648a | ||
|
|
f9c39bce0b | ||
|
|
0e8c803c0f | ||
|
|
6bb7b88c69 | ||
|
|
9e0fc7017c | ||
|
|
76317b7ab3 | ||
|
|
d5f4d4bf2f | ||
|
|
156d1a6b14 | ||
|
|
3fa456fd44 | ||
|
|
bf9bb46154 | ||
|
|
fb3da0b53c | ||
|
|
5c635c09b4 | ||
|
|
fad539212d | ||
|
|
7275302639 | ||
|
|
5057ff97a3 | ||
|
|
754d5a1fbb | ||
|
|
7cee5b67a7 | ||
|
|
21729e7e48 | ||
|
|
394d1e6cc2 | ||
|
|
f73d7f7285 | ||
|
|
e505cfffd8 | ||
|
|
d0fe3591ef | ||
|
|
1868f32457 | ||
|
|
ca82767b07 | ||
|
|
76024330a7 | ||
|
|
21e8107eb1 | ||
|
|
bcaa97a79b | ||
|
|
19727f6a25 | ||
|
|
2102b96323 | ||
|
|
59b3ffda4f | ||
|
|
aae929966c | ||
|
|
0cf842cacc | ||
|
|
96885c5b53 | ||
|
|
d45ce7742e | ||
|
|
50e27fd204 | ||
|
|
af98933173 | ||
|
|
e357dada65 | ||
|
|
19c48862e2 | ||
|
|
448ce94b35 | ||
|
|
3681b7f0d5 | ||
|
|
7b3c883653 | ||
|
|
c81004ddb4 | ||
|
|
f844011348 | ||
|
|
0094040d68 | ||
|
|
dfb5369664 | ||
|
|
2462c906b3 | ||
|
|
8cbc05840f | ||
|
|
510846321d | ||
|
|
ac60567583 | ||
|
|
7592997f4e | ||
|
|
c68b3dccb7 | ||
|
|
da2a2ce4ff | ||
|
|
538418ea93 | ||
|
|
2bf284bce8 | ||
|
|
b51cb06d53 | ||
|
|
bab6c29fbe | ||
|
|
9502c86a65 | ||
|
|
359811f71e | ||
|
|
9de5840017 | ||
|
|
45af00096f | ||
|
|
2f0283edb7 | ||
|
|
d142393221 | ||
|
|
ac147dc2a1 | ||
|
|
16af5cff9c | ||
|
|
7725dbff78 | ||
|
|
f2f8824678 | ||
|
|
f2e9ed0b73 | ||
|
|
9e6d0aa44a | ||
|
|
1758fced75 | ||
|
|
31e2a3efe4 | ||
|
|
371db7db2f | ||
|
|
bb23bc6acc | ||
|
|
270326e188 | ||
|
|
7d27835543 | ||
|
|
cfb88b2e8e | ||
|
|
8a83787d58 | ||
|
|
6dd9f9e0f5 | ||
|
|
7f3445b1c6 | ||
|
|
5c15f26f09 | ||
|
|
b8fbe5e465 | ||
|
|
32049e34f2 | ||
|
|
26b09283f3 | ||
|
|
6c6360e50a | ||
|
|
9877323195 | ||
|
|
b6f52f4c27 | ||
|
|
c1789cbbaf | ||
|
|
f44f5b5a98 | ||
|
|
f4faa1ad3d | ||
|
|
97d03c6954 | ||
|
|
8accbc700a | ||
|
|
9f013534b3 | ||
|
|
0b8febf6a6 | ||
|
|
188985ecc2 | ||
|
|
42434d1f49 | ||
|
|
87492cb0c3 | ||
|
|
517ae7f1bd | ||
|
|
73357f022b | ||
|
|
012faa0a16 | ||
|
|
7a8abb3db7 | ||
|
|
d6ce7e950b | ||
|
|
e39b249070 | ||
|
|
5b889936be | ||
|
|
712809195d | ||
|
|
64b5d6e323 | ||
|
|
baa607532d | ||
|
|
ff064dd859 | ||
|
|
8305bffcac | ||
|
|
17a8972052 | ||
|
|
56e03eae3e | ||
|
|
1f948e17c4 | ||
|
|
efaed42b24 | ||
|
|
be2e31ff9d | ||
|
|
a891cacae5 | ||
|
|
e9e0117402 | ||
|
|
24114d87e6 | ||
|
|
7cd78f3f4e | ||
|
|
ca64aae7f0 | ||
|
|
be0fb4250c | ||
|
|
eba58cb893 |
@@ -10,5 +10,8 @@ charset = utf-8
|
||||
[*.{csproj,xml,yml,dll.config,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
[nuget.config]
|
||||
indent_size = 2
|
||||
|
||||
[*.gdsl]
|
||||
indent_style = tab
|
||||
|
||||
64
Directory.Packages.props
Normal file
64
Directory.Packages.props
Normal file
@@ -0,0 +1,64 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="ILReader.Core" Version="1.0.0.4" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.0" />
|
||||
<PackageVersion Include="Linguini.Bundle" Version="0.1.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeCoverage" Version="17.8.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.DotNet.RemoteExecutor" Version="8.0.0-beta.24059.4" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.ILVerification" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.70" />
|
||||
<PackageVersion Include="NUnit" Version="4.0.1" />
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="3.10.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageVersion Include="Nett" Version="0.15.0" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
|
||||
<PackageVersion Include="Pidgin" Version="3.2.2" />
|
||||
<PackageVersion Include="Robust.Natives" Version="0.1.1" />
|
||||
<PackageVersion Include="Robust.Natives.Cef" Version="120.1.9" />
|
||||
<PackageVersion Include="Robust.Shared.AuthLib" Version="0.1.2" />
|
||||
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.7" />
|
||||
<PackageVersion Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.7" />
|
||||
<PackageVersion Include="Serilog" Version="3.1.1" />
|
||||
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.3" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
|
||||
<PackageVersion Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
<PackageVersion Include="System.Memory" Version="4.5.5" />
|
||||
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.22621.5" />
|
||||
<PackageVersion Include="TerraFX.Interop.Xlib" Version="6.4.0" />
|
||||
<PackageVersion Include="VorbisPizza" Version="1.3.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="13.7.1" />
|
||||
<PackageVersion Include="prometheus-net" Version="8.2.1" />
|
||||
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
|
||||
<PackageVersion Include="PolySharp" Version="1.14.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// So I wanted to mess with NetIncomingMessage and NetOutgoingMessage from tests.
|
||||
// Now.. the instructors are internal...
|
||||
// Unless...
|
||||
// I mean we have this project here from the weird way we're compiling Lidgren.
|
||||
// I could just put this in here... it wouldn't touch the main Lidgren repo at all...
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="CursedHorrorsBeyondOurWildestImagination.cs" />
|
||||
|
||||
<Compile Include="Lidgren.Network\Lidgren.Network\**\*.cs">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="Robust.Custom.targets" Condition="Exists('Robust.Custom.targets')"/>
|
||||
@@ -27,5 +28,5 @@
|
||||
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- serialization generator -->
|
||||
<Import Project="Robust.Serialization.Generator.targets" />
|
||||
<Import Project="Robust.Serialization.Generator.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
</Project>
|
||||
|
||||
Submodule NetSerializer updated: 7224829e87...7f51deaeca
434
RELEASE-NOTES.md
434
RELEASE-NOTES.md
@@ -54,10 +54,444 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 211.0.3
|
||||
|
||||
|
||||
## 211.0.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix TextureRect scaling not handling UIScale correctly.
|
||||
|
||||
|
||||
## 211.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix GridChunkEnumerator on maps.
|
||||
|
||||
|
||||
## 211.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Moved ChunkIndicesEnumerator to engine and to a re-useable namespace at Robust.Shared/Maps.
|
||||
|
||||
### New features
|
||||
|
||||
* Added an Enlarged method for Box2Rotated.
|
||||
|
||||
### Internal
|
||||
|
||||
* Significantly optimise ChunkEnumerator / FindGridsIntersecting in certain use cases by intersecting the grid's AABB with the local AABB to avoid iterating dummy chunks.
|
||||
|
||||
|
||||
## 210.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed multiple recent bugs with key binding storage.
|
||||
|
||||
### Other
|
||||
|
||||
* Change default of `ButtonGroup.IsNoneSetAllowed` to `true`. This makes it default again to the previous (unintentional) behavior.
|
||||
|
||||
|
||||
## 210.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* `NetUserId` implements `ISelfSerialize` so can be used in data fields.
|
||||
* `ButtonGroup.IsNoneSetAllowed` to allow a button group to have no buttons pressed by default.
|
||||
|
||||
|
||||
## 210.0.3
|
||||
|
||||
|
||||
## 210.0.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Revert changes to `TextureRect` too.
|
||||
|
||||
|
||||
## 210.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Revert changes to `TextureButton` that broke style property handling.
|
||||
|
||||
|
||||
## 210.0.0
|
||||
|
||||
### New features
|
||||
|
||||
* Controls can now hook before, after, and during rendering of their children.
|
||||
* IRenderHandle is now a public API, with the caveat that it's properties and methods are unstable.
|
||||
* ButtonGroup now exposes what buttons it contains, alongside which is currently pressed.
|
||||
* OptionButton has additional styleclasses, and has a hook for modifying it's internal buttons.
|
||||
* PanelContainer.GetStyleBox() is now protected rather than private.
|
||||
* TextureButton now uses a TextureRect instead of custom drawing code.
|
||||
* TextureRect has additional style properties exposed.
|
||||
* A new property, TextureSizeTarget, was added, which allows specifying a size in virtual pixels that the control should attempt to draw at.
|
||||
* Stretch mode is now a style property.
|
||||
* Scale is now a style property.
|
||||
* Avalonia.Metadata.XmlnsDefinitionAttribute is now permitted by the sandbox.
|
||||
* Add MaxDimension property to Box2 to return the higher of the Width or Height.
|
||||
* Add GetLocalPosition to convert ScreenCoordinates to coordinates relative to the control. Ignores window.
|
||||
* Add GlobalRect and GlobalPixelRect for controls to get their UIBox2i in screen terms.
|
||||
* Add dotted line drawing to DrawingHandleScreen.
|
||||
* You can use `Subs.CVar()` from an entity systems to subscribe to CVar changes. This is more convenient than `IConfigurationManager.OnValueChanged` as it automatically unsubscribes on system shutdown.
|
||||
* There is now a built-in type serializer for `DateTime`, so you can put `DateTime`s in your data fields.
|
||||
* `System.Text.Unicode.UnicodeRange` and `UnicodeRanges` are now available in the sandbox.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* UI drawing now properly accounts for a control's draw routine potentially mangling the current matrix.
|
||||
* UI roots now properly update when the global stylesheet is changed. They previously only did so if they had a dedicated stylesheet (which is the one case where they would be unaffected by a global sheet update.
|
||||
|
||||
|
||||
## 209.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix missed import from 209.0.0.
|
||||
|
||||
|
||||
## 209.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `replay.max_compressed_size` and `replay.max_uncompressed_size` CVars are now `long`.
|
||||
* Remove obsolete CoordinatesExtension for ToEntityCoordinates from GridUid / Vector2i.
|
||||
|
||||
### New features
|
||||
|
||||
* Add GetEntitiesOnMap / GetChildEntities to EntityLookupSystem to return components on the specified map and components with the specified parent respectively.
|
||||
* Add MaxDimension property to Box2 to return the higher of the Width or Height.
|
||||
* Add GetLocalPosition to convert ScreenCoordinates to coordinates relative to the control. Ignores window.
|
||||
* Add GlobalRect and GlobalPixelRect for controls to get their UIBox2i in screen terms.
|
||||
* Add dotted line drawing to DrawingHandleScreen.
|
||||
* `IConfigurationManager.LoadDefaultsFromTomlStream` properly does type conversions. This fixes scenarios like loading of `long` CVars.
|
||||
* Add helper methods for TileRef / Vector2i to SharedMapSystem for ToCenterCoordinates (tile center EntityCoordinates) and ToCoordinates (tile origin to EntityCoordinates).
|
||||
* Copy some of the coordinates extensions to SharedTransformSystem.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed integer overflows in replay max size calculation.
|
||||
* Explicitly capped `replay.replay_tick_batchSize` internally to avoid high values causing allocation failures.
|
||||
|
||||
### Other
|
||||
|
||||
* Important MIDI performance improvements.
|
||||
|
||||
|
||||
## 208.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Metadata flags are no longer serialized as they get rebuilt on entity startup.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Log failing to load user keybinds and handle the exception.
|
||||
|
||||
|
||||
## 207.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add the ability to merge grids via GridFixtureSystem.
|
||||
|
||||
|
||||
## 207.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Update EntityLookup internally so non-approximate queries use the GJK solver and are much more accurate. This also means the approximate flag matters much more if you don't need narrowphase checks.
|
||||
* Add shape versions of queries for both EntityLookup and MapManager.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PVS full state updates not clearing session entities and causing exceptions.
|
||||
|
||||
### Other
|
||||
|
||||
* Integration tests now run `NetMessage`s through serialization rather than passing the objects between client and server. This causes tests that missed `[NetSerializer]` attributes on any objects that need them to fail.
|
||||
|
||||
### Internal
|
||||
|
||||
* Remove a lot of duplicate code internally from EntityLookup and MapManager.
|
||||
|
||||
|
||||
## 206.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* tpto will teleport you to physics-center instead of transform center instead.
|
||||
* Rename local EntityLookup methods to reflect they take local AABBs and not world AABBs.
|
||||
|
||||
### New features
|
||||
|
||||
* Add some additional EntityLookup methods for local queries.
|
||||
* Add support to PrototypeManager for parsing specific files / directories as abstract.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix tpto short-circuiting if one of the listed entities isn't found.
|
||||
* Fix tpto not allowing grids as targets.
|
||||
|
||||
### Other
|
||||
|
||||
* Reduce MIDI source update rate from 10hz to 4hz.
|
||||
|
||||
### Internal
|
||||
|
||||
* Remove some duplicate internal code in EntityLookupSystem.
|
||||
* Skip serialization sourcegen in GLFW and Lidgren.
|
||||
|
||||
|
||||
## 205.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The unused `Robust.Physics` project has been deleted.
|
||||
* The project now uses [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management).
|
||||
* (Almost) all the NuGet packages have been updated. This causes many problems. I am so sorry.
|
||||
* Cleaned up some unused packages as well.
|
||||
|
||||
|
||||
## 204.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* New `EntitySystem` subscription helper for working with Bound User Interface events. You can find them by doing `Subs.BuiEvents<>()` in a system.
|
||||
* The `EntityManager.Subscriptions` type (for building helper extension methods) now uses
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Avoid loading assemblies from content `/Assemblies` if Robust ships its own copy. This avoid duplicate or weird mismatching version issues.
|
||||
|
||||
### Other
|
||||
|
||||
* Removed glibc version check warning.
|
||||
|
||||
|
||||
## 204.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Make EntityManager abstract and make IEntityManager.EntityNetManager not nullable.
|
||||
* Make VVAccess.ReadWrite default for all Datafields instead of VVAccess.ReadOnly
|
||||
|
||||
### New features
|
||||
|
||||
* `TextEdit.OnTextChanged`
|
||||
* Add Pick and PickAndTake versions for System.Random for ICollections.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix `IClipboardManager.GetText()` returning null in some cases.
|
||||
* Fix possible NRE in server-side console command completion code.
|
||||
* Fix possible NRE on DebugConsole logs.
|
||||
* Fix exception when VVing non-networked components.
|
||||
|
||||
### Other
|
||||
|
||||
* Remove "Do not use from content" from IComponent.
|
||||
|
||||
|
||||
## 203.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered.
|
||||
* Event bus subscriptions are now locked after `IEntityManager` has started, instead of after the first component gets added. Any event subscriptions now need to happen before startup (but after init).
|
||||
* Event bus subscriptions must now be locked before raising any events.
|
||||
* Delete FodyWeavers.xsd as it hasn't been used for a long time.
|
||||
* Remove physics sleep cancelling as it was, in hindsight, a bad idea.
|
||||
|
||||
### New features
|
||||
|
||||
* `RobustUnitTest` now has a `ExtraComponents` field for automatically registering additional components.
|
||||
* `IComponentFactory.RegisterIgnore()` now accepts more than one string.
|
||||
* Added `IComponentFactory.RegisterTypes` for simultaneously registering multiple components.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Clamp volume calculations for audio rather than throwing.
|
||||
|
||||
|
||||
## 202.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Reverted some map/grid initialisation changes that might've been causing broadphase/physics errors.
|
||||
* Fixed PVS sometimes sending entities without first sending their children.
|
||||
* Fixed a container state handling bug caused by containers not removing expected entities when shutting down.
|
||||
* Fixed a `EnsureEntity<T>` state handling bug caused by improper handling of entity deletions.
|
||||
* Fixed a bad NetSyncEnabled debug assert.
|
||||
|
||||
|
||||
## 202.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add GetLocalEntitiesIntersecting overload that takes in a griduid and a Vector2i tile.
|
||||
|
||||
|
||||
## 202.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Various entity manager methods now have a new `where T : IComponent` constraint.
|
||||
* The `IComponentFactory.ComponentAdded` event has been renamed to `ComponentsAdded` and now provides an array of component registrations.
|
||||
* `IComponentFactory.RegisterIgnore()` no longer supports overwriting existing registrations, components should get ignored before they are registered.
|
||||
|
||||
### New features
|
||||
|
||||
* Added `IComponentFactory.GetAllRegistrations()`
|
||||
* Add IComponentState interface support for component states so structs can be used in lieu of classes.
|
||||
|
||||
|
||||
## 201.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The `zCircleGradient` shader function arguments have changed. It now requires a pixel-size to ensure that the gradient is properly entered.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed some PVS null reference errors.
|
||||
|
||||
|
||||
## 200.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* MappingDataNode is now ordered.
|
||||
* Make invalid AutoNetworkedFields compiler errors.
|
||||
|
||||
### New features
|
||||
|
||||
* `OSWindowStyles.NoTitleBar` (supported only on Linux X11 for now).
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Avoid calling DirtyEntity when a component's last modified tick is not current.
|
||||
* Fix `tpgrid` allowing moving grids to nullspace.
|
||||
|
||||
### Other
|
||||
|
||||
* `OSWindowStyles.NoTitleOptions` is now supported on Linux X11.
|
||||
|
||||
|
||||
## 199.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Various `IEntityManager` C# events now use `Entity<MetadataComponent>` instead of `EntityUid`
|
||||
* Entity visibility masks now use a ushort instead of an integer.
|
||||
* Run grid traversal on entity spawn.
|
||||
|
||||
### New features
|
||||
|
||||
* Added two new `IEntityManager` C# events that get raiseed before and after deleting ("flushing") all entities.
|
||||
* Added a new `DeleteEntity()` override that takes in the entity's metadata and transform components.
|
||||
* Add better audio logs.
|
||||
* Expand z-library shader.
|
||||
* Add a Box2i union for Vector2i and add a Contains variant that assumes the Vector2i is a tile and not a point.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Try to prevent some NREs in PVS.
|
||||
* More PVS fixes and cleanup.
|
||||
|
||||
|
||||
## 198.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* `IClydeViewport` now provides access to the light render target.
|
||||
* Added a style-class to the `MenuBar` popup control.
|
||||
* Added `NextGaussian()` extension method for `System.Random`.
|
||||
* Added per-session variant of `PvsOverrideSystem.AddForceSend()`.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Stopped the client from logging errors when attempting to delete invalid entities.
|
||||
|
||||
### Other
|
||||
|
||||
* The `DevWindow` UI inspector has been improved a bit and it now groups properties by their defining type.
|
||||
|
||||
|
||||
## 198.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix preprocessor flag for FULL_RELEASE preventing building.
|
||||
|
||||
|
||||
## 198.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Disable DefaultMagicAczProvider for FULL_RELEASE as it's only meant for debugging.
|
||||
|
||||
### New features
|
||||
|
||||
* Automatic UI scale is disabled by default for non-main windows. If desired, it can be re-enabled per window by changing `WindowRoot.DisableAutoScaling`.
|
||||
* Add UI click and hover sound support via IUserInterfaceManager.SetClickSound / .SetHoverSound
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix GetEntitiesIntersecting for map entities without grids.
|
||||
|
||||
### Other
|
||||
|
||||
* Print more diagnostics on server startup.
|
||||
|
||||
|
||||
## 197.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* ACZ improvements: `IStatusHost.InvalidateAcz()` and `IStatusHost.SetFullHybridAczProvider()`.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixes a PVS bug that happens when grids moved across maps.
|
||||
* Fixes sprite animations not working properly
|
||||
|
||||
|
||||
## 197.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* PvsOverrideSystem has been reworked:
|
||||
* Session and global overrides now default to always being recursive (i.e., sending all children).
|
||||
* Session & global overrides will always respect a client's PVS budgets.
|
||||
* Entities with an override will now still be sent in the same way as other entities if they are within a player's view. If you want to prevent them from being sent, you need to use visibility masks.
|
||||
* Entities can have more than one kind of override (i.e., multiple sessions).
|
||||
|
||||
### New features
|
||||
|
||||
* Added a `PvsSize ` field to `EyeComponent`, which can be used to modify the PVS range of an eye.
|
||||
* Added a new `NetLowLodRange` cvar for reducing the number of distant entities that get sent to a player. If a PVS chunk is beyond this range (but still within PVS range), then only high-priority entities on that chunk will get sent.
|
||||
* Added a new metadata flag for tagging an entity as a "high prority" entity that should get sent even on distant chunks. This only works for entities that are directly attached to a grid or map. This is currently used by lights & occluders.
|
||||
|
||||
### Other
|
||||
|
||||
* PVS has been reworked again, and should hopefully be noticeable faster.
|
||||
* PVS now prioritizes sending chunks that are closer to a player's eyes.
|
||||
|
||||
|
||||
## 196.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Dirtying a non-networked component will now fail a debug assert.
|
||||
* The `IInvocationContext` interface for toolshed commands now requires a UserId field. The session field should be cleared if a player disconnects.
|
||||
|
||||
### New features
|
||||
|
||||
@@ -9,6 +9,7 @@ cmd-parse-failure-float = {$arg} is not a valid float.
|
||||
cmd-parse-failure-bool = {$arg} is not a valid bool.
|
||||
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
|
||||
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
|
||||
cmd-parse-failure-grid = {$arg} is not a valid grid.
|
||||
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
|
||||
|
||||
cmd-error-file-not-found = Could not find file: {$file}.
|
||||
|
||||
8
Resources/Locale/en-US/physics/grid_merging.ftl
Normal file
8
Resources/Locale/en-US/physics/grid_merging.ftl
Normal file
@@ -0,0 +1,8 @@
|
||||
cmd-merge_grids-desc = Combines 2 grids into 1 grid
|
||||
cmd-merge_grids-help = merge_grids <gridUid1> <gridUid2> <offsetX> <offsetY> [angle]
|
||||
|
||||
cmd-merge_grids-hintA = Grid A
|
||||
cmd-merge_grids-hintB = Grid B
|
||||
cmd-merge_grids-xOffset = X offset
|
||||
cmd-merge_grids-yOffset = Y offset
|
||||
cmd-merge_grids-angle = [Angle]
|
||||
8
Robust.Analyzers.Tests/Aliases.cs
Normal file
8
Robust.Analyzers.Tests/Aliases.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
// OH BOY. TURNS OUT IT GETS EVEN MORE CURSED.
|
||||
//
|
||||
// So because we're compiling a copy of Robust.Roslyn.Shared into every analyzer project,
|
||||
// the test project sees multiple copies of it. This would make it impossible to use.
|
||||
// UNLESS you use this obscure C# feature called "extern alias"
|
||||
// that I guarantee you you've never heard of before, and are now concerned about.
|
||||
|
||||
extern alias SerializationGenerator;
|
||||
340
Robust.Analyzers.Tests/ComponentPauseGeneratorTest.cs
Normal file
340
Robust.Analyzers.Tests/ComponentPauseGeneratorTest.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
extern alias SerializationGenerator;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using NUnit.Framework;
|
||||
using SerializationGenerator::Robust.Roslyn.Shared;
|
||||
using SerializationGenerator::Robust.Serialization.Generator;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ComponentPauseGenerator))]
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public sealed class ComponentPauseGeneratorTest
|
||||
{
|
||||
private const string TypesCode = """
|
||||
global using System;
|
||||
global using Robust.Shared.Analyzers;
|
||||
global using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Analyzers
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class AutoGenerateComponentPauseAttribute : Attribute
|
||||
{
|
||||
public bool Dirty = false;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class AutoPausedFieldAttribute : Attribute;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class AutoNetworkedFieldAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public interface IComponent;
|
||||
}
|
||||
""";
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
[AutoPausedField]
|
||||
public TimeSpan Foo;
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoDiagnostics(result);
|
||||
ExpectSource(
|
||||
result,
|
||||
"""
|
||||
// <auto-generated />
|
||||
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
public partial class FooComponent
|
||||
{
|
||||
[RobustAutoGenerated]
|
||||
public sealed class FooComponent_AutoPauseSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
||||
}
|
||||
|
||||
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.Foo += args.PausedTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNullable()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
[AutoPausedField]
|
||||
public TimeSpan? Foo;
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoDiagnostics(result);
|
||||
ExpectSource(
|
||||
result,
|
||||
"""
|
||||
// <auto-generated />
|
||||
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
public partial class FooComponent
|
||||
{
|
||||
[RobustAutoGenerated]
|
||||
public sealed class FooComponent_AutoPauseSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
||||
}
|
||||
|
||||
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
if (component.Foo.HasValue)
|
||||
component.Foo = component.Foo.Value + args.PausedTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoState()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
[AutoPausedField, AutoNetworkedField]
|
||||
public TimeSpan Foo;
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoDiagnostics(result);
|
||||
ExpectSource(
|
||||
result,
|
||||
"""
|
||||
// <auto-generated />
|
||||
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
public partial class FooComponent
|
||||
{
|
||||
[RobustAutoGenerated]
|
||||
public sealed class FooComponent_AutoPauseSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
||||
}
|
||||
|
||||
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.Foo += args.PausedTime;
|
||||
Dirty(uid, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExplicitDirty()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause(Dirty = true)]
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
[AutoPausedField]
|
||||
public TimeSpan Foo;
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoDiagnostics(result);
|
||||
ExpectSource(
|
||||
result,
|
||||
"""
|
||||
// <auto-generated />
|
||||
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
public partial class FooComponent
|
||||
{
|
||||
[RobustAutoGenerated]
|
||||
public sealed class FooComponent_AutoPauseSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
||||
}
|
||||
|
||||
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.Foo += args.PausedTime;
|
||||
Dirty(uid, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDiagnosticNotIComponent()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class FooComponent
|
||||
{
|
||||
[AutoPausedField]
|
||||
public TimeSpan Foo;
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoSource(result);
|
||||
ExpectDiagnostics(result, [
|
||||
(Diagnostics.IdComponentPauseNotComponent, new LinePositionSpan(new LinePosition(1, 28), new LinePosition(1, 40)))
|
||||
]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDiagnosticNoFields()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
public TimeSpan Foo;
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoSource(result);
|
||||
ExpectDiagnostics(result, [
|
||||
(Diagnostics.IdComponentPauseNoFields, new LinePositionSpan(new LinePosition(1, 28), new LinePosition(1, 40)))
|
||||
]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDiagnosticNoParentAttribute()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
[AutoPausedField]
|
||||
public TimeSpan Foo, Fooz;
|
||||
|
||||
[AutoPausedField]
|
||||
public TimeSpan Bar { get; set; }
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoSource(result);
|
||||
ExpectDiagnostics(result, [
|
||||
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(3, 20), new LinePosition(3, 23))),
|
||||
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(3, 25), new LinePosition(3, 29))),
|
||||
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(6, 20), new LinePosition(6, 23)))
|
||||
]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDiagnosticWrongType()
|
||||
{
|
||||
var result = RunGenerator("""
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class FooComponent : IComponent
|
||||
{
|
||||
[AutoPausedField]
|
||||
public int Foo, Fooz;
|
||||
|
||||
[AutoPausedField]
|
||||
public int Bar { get; set; }
|
||||
}
|
||||
""");
|
||||
|
||||
ExpectNoSource(result);
|
||||
ExpectDiagnostics(result, [
|
||||
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(4, 15), new LinePosition(4, 18))),
|
||||
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(4, 20), new LinePosition(4, 24))),
|
||||
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(7, 15), new LinePosition(7, 18)))
|
||||
]);
|
||||
}
|
||||
|
||||
private static void ExpectSource(GeneratorRunResult result, string expected)
|
||||
{
|
||||
Assert.That(result.GeneratedSources, Has.Length.EqualTo(1));
|
||||
|
||||
var source = result.GeneratedSources[0];
|
||||
|
||||
Assert.That(source.SourceText.ToString(), Is.EqualTo(expected));
|
||||
}
|
||||
|
||||
private static void ExpectNoSource(GeneratorRunResult result)
|
||||
{
|
||||
Assert.That(result.GeneratedSources, Is.Empty);
|
||||
}
|
||||
|
||||
private static void ExpectNoDiagnostics(GeneratorRunResult result)
|
||||
{
|
||||
Assert.That(result.Diagnostics, Is.Empty);
|
||||
}
|
||||
|
||||
private static void ExpectDiagnostics(GeneratorRunResult result, (string code, LinePositionSpan span)[] diagnostics)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result.Diagnostics, Has.Length.EqualTo(diagnostics.Length));
|
||||
foreach (var (code, span) in diagnostics)
|
||||
{
|
||||
Assert.That(
|
||||
result.Diagnostics.Any(x => x.Id == code && x.Location.GetLineSpan().Span == span),
|
||||
$"Expected diagnostic with code {code} and location {span}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static GeneratorRunResult RunGenerator(string source)
|
||||
{
|
||||
var compilation = (Compilation)CSharpCompilation.Create("compilation",
|
||||
new[]
|
||||
{
|
||||
CSharpSyntaxTree.ParseText(source, path: "Source.cs"),
|
||||
CSharpSyntaxTree.ParseText(TypesCode, path: "Types.cs")
|
||||
},
|
||||
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
|
||||
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
||||
|
||||
var generator = new ComponentPauseGenerator();
|
||||
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
|
||||
driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out _);
|
||||
var result = driver.GetRunResult();
|
||||
|
||||
return result.Results[0];
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<SkipRobustAnalyzer>true</SkipRobustAnalyzer>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets"/>
|
||||
<Import Project="..\MSBuild\Robust.Engine.props"/>
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1"/>
|
||||
<PackageReference Include="NUnit" Version="3.13.2"/>
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.0"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.3.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageVersion Update="NUnit" Version="3.14.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzer.Testing"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces"/>
|
||||
<PackageReference Include="NUnit"/>
|
||||
<PackageReference Include="NUnit3TestAdapter"/>
|
||||
<PackageReference Include="NUnit.Analyzers"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Analyzers\Robust.Analyzers.csproj"/>
|
||||
<ProjectReference Include="..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" Aliases="SerializationGenerator" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
using Robust.Shared.Analyzers.Implementation;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
using static Microsoft.CodeAnalysis.SymbolEqualityComparer;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
@@ -16,7 +17,7 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor ByRefEventSubscribedByValueRule = new(
|
||||
Diagnostics.IdByRefEventSubscribedByValue,
|
||||
"By-ref event subscribed to by value",
|
||||
"Tried to subscribe to a by-ref event '{0}' by value.",
|
||||
"Tried to subscribe to a by-ref event '{0}' by value",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
@@ -26,7 +27,7 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor ByRefEventRaisedByValueRule = new(
|
||||
Diagnostics.IdByRefEventRaisedByValue,
|
||||
"By-ref event raised by value",
|
||||
"Tried to raise a by-ref event '{0}' by value.",
|
||||
"Tried to raise a by-ref event '{0}' by value",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
@@ -36,7 +37,7 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor ByValueEventRaisedByRefRule = new(
|
||||
Diagnostics.IdValueEventRaisedByRef,
|
||||
"Value event raised by-ref",
|
||||
"Tried to raise a value event '{0}' by-ref.",
|
||||
"Tried to raise a value event '{0}' by-ref",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
@@ -18,7 +19,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
|
||||
Diagnostics.IdDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} is a DataDefinition but is not partial.",
|
||||
"Type {0} is a DataDefinition but is not partial",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
@@ -28,7 +29,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
|
||||
Diagnostics.IdNestedDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} contains nested data definition {1} but is not partial.",
|
||||
"Type {0} contains nested data definition {1} but is not partial",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
@@ -38,7 +39,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
|
||||
Diagnostics.IdDataFieldWritable,
|
||||
"Data field must not be readonly",
|
||||
"Data field {0} in data definition {1} is readonly.",
|
||||
"Data field {0} in data definition {1} is readonly",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
@@ -48,7 +49,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
|
||||
Diagnostics.IdDataFieldPropertyWritable,
|
||||
"Data field property must have a setter",
|
||||
"Data field property {0} in data definition {1} does not have a setter.",
|
||||
"Data field property {0} in data definition {1} does not have a setter",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
|
||||
@@ -9,7 +9,7 @@ using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
||||
using static Robust.Analyzers.Diagnostics;
|
||||
using static Robust.Roslyn.Shared.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
using Document = Microsoft.CodeAnalysis.Document;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
@@ -31,7 +32,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
|
||||
|
||||
private static readonly DiagnosticDescriptor InvalidNotNullableImplementationRule = new (
|
||||
Diagnostics.IdInvalidNotNullableFlagImplementation,
|
||||
"Invalid NotNullable flag implementation.",
|
||||
"Invalid NotNullable flag implementation",
|
||||
"NotNullable flag is either not typed as bool, or does not have a default value equaling false",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
@@ -41,7 +42,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
|
||||
private static readonly DiagnosticDescriptor InvalidNotNullableTypeRule = new (
|
||||
Diagnostics.IdInvalidNotNullableFlagType,
|
||||
"Failed to resolve type parameter",
|
||||
"Failed to resolve type parameter \"{0}\".",
|
||||
"Failed to resolve type parameter \"{0}\"",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
@@ -49,7 +50,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
|
||||
|
||||
private static readonly DiagnosticDescriptor NotNullableFlagValueTypeRule = new (
|
||||
Diagnostics.IdNotNullableFlagValueType,
|
||||
"NotNullable flag not supported for value types.",
|
||||
"NotNullable flag not supported for value types",
|
||||
"Value types as generic arguments are not supported for NotNullable flags",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
|
||||
@@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for NotNullableFlagAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for FriendAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" />
|
||||
@@ -27,4 +16,10 @@
|
||||
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using BenchmarkDotNet.Analysers;
|
||||
using BenchmarkDotNet.Columns;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using BenchmarkDotNet.EventProcessors;
|
||||
using BenchmarkDotNet.Exporters;
|
||||
using BenchmarkDotNet.Filters;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using BenchmarkDotNet.Loggers;
|
||||
using BenchmarkDotNet.Order;
|
||||
using BenchmarkDotNet.Reports;
|
||||
using BenchmarkDotNet.Running;
|
||||
using BenchmarkDotNet.Validators;
|
||||
using Robust.Benchmarks.Exporters;
|
||||
|
||||
@@ -44,10 +47,16 @@ public sealed class DefaultSQLConfig : IConfig
|
||||
|
||||
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => DefaultConfig.Instance.GetLogicalGroupRules();
|
||||
|
||||
public IEnumerable<EventProcessor> GetEventProcessors() => DefaultConfig.Instance.GetEventProcessors();
|
||||
|
||||
public IEnumerable<IColumnHidingRule> GetColumnHidingRules() => DefaultConfig.Instance.GetColumnHidingRules();
|
||||
public IOrderer Orderer => DefaultConfig.Instance.Orderer!;
|
||||
public ICategoryDiscoverer? CategoryDiscoverer => DefaultConfig.Instance.CategoryDiscoverer;
|
||||
public SummaryStyle SummaryStyle => DefaultConfig.Instance.SummaryStyle;
|
||||
public ConfigUnionRule UnionRule => DefaultConfig.Instance.UnionRule;
|
||||
public string ArtifactsPath => DefaultConfig.Instance.ArtifactsPath;
|
||||
public CultureInfo CultureInfo => DefaultConfig.Instance.CultureInfo!;
|
||||
public ConfigOptions Options => DefaultConfig.Instance.Options;
|
||||
public TimeSpan BuildTimeout => DefaultConfig.Instance.BuildTimeout;
|
||||
public IReadOnlyList<Conclusion> ConfigAnalysisConclusion => DefaultConfig.Instance.ConfigAnalysisConclusion;
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Server.GameStates;
|
||||
@@ -11,7 +12,9 @@ using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.UnitTesting.Server;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.UnitTesting;
|
||||
|
||||
namespace Robust.Benchmarks.Transform;
|
||||
|
||||
@@ -19,114 +22,177 @@ namespace Robust.Benchmarks.Transform;
|
||||
/// This benchmark tests various transform/move related functions with an entity that has many children.
|
||||
/// </summary>
|
||||
[Virtual, MemoryDiagnoser]
|
||||
public class RecursiveMoveBenchmark
|
||||
public class RecursiveMoveBenchmark : RobustIntegrationTest
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private SharedTransformSystem _transform = default!;
|
||||
private ContainerSystem _container = default!;
|
||||
private PvsSystem _pvs = default!;
|
||||
private EntityCoordinates _mapCoords;
|
||||
private EntityCoordinates _gridCoords;
|
||||
private EntityCoordinates _gridCoords2;
|
||||
private EntityUid _ent;
|
||||
private EntityUid _child;
|
||||
private TransformComponent _childXform = default!;
|
||||
private EntityQuery<TransformComponent> _query;
|
||||
private ICommonSession[] _players = default!;
|
||||
private PvsSession _session = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.InitializeInstance();
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
if (!_simulation.Resolve<IConfigurationManager>().GetCVar(CVars.NetPVS))
|
||||
throw new InvalidOperationException("PVS must be enabled");
|
||||
Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()).Wait();
|
||||
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
_entMan = server.ResolveDependency<IEntityManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
|
||||
_entMan = _simulation.Resolve<IEntityManager>();
|
||||
_transform = _entMan.System<SharedTransformSystem>();
|
||||
_container = _entMan.System<ContainerSystem>();
|
||||
_pvs = _entMan.System<PvsSystem>();
|
||||
_query = _entMan.GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Create map & grid
|
||||
var mapMan = _simulation.Resolve<IMapManager>();
|
||||
var mapSys = _entMan.System<SharedMapSystem>();
|
||||
var mapId = mapMan.CreateMap();
|
||||
var map = mapMan.GetMapEntityId(mapId);
|
||||
var gridComp = mapMan.CreateGridEntity(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
_mapCoords = new EntityCoordinates(map, 100, 100);
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
|
||||
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
|
||||
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
|
||||
_ent = _entMan.Spawn();
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
client.SetConnectTarget(server);
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
||||
|
||||
// Quick check that SetCoordinates actually changes the parent as expected
|
||||
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
// Add 5 direct children in slots to represent clothing.
|
||||
for (var i = 0; i < 5; i++)
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var id = $"inventory{i}";
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, id);
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
server.WaitRunTicks(1).Wait();
|
||||
client.WaitRunTicks(1).Wait();
|
||||
}
|
||||
|
||||
// body parts
|
||||
_container.EnsureContainer<Container>(_ent, "body");
|
||||
for (var i = 0; i < 5; i++)
|
||||
// Ensure client & server ticks are synced.
|
||||
// Client runs 1 tick ahead
|
||||
{
|
||||
// Simple organ
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
var sTick = (int)server.Timing.CurTick.Value;
|
||||
var cTick = (int)client.Timing.CurTick.Value;
|
||||
var delta = cTick - sTick;
|
||||
|
||||
// body part that has another body part / limb
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
if (delta > 1)
|
||||
server.WaitRunTicks(delta - 1).Wait();
|
||||
else if (delta < 1)
|
||||
client.WaitRunTicks(1 - delta).Wait();
|
||||
|
||||
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
|
||||
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
sTick = (int)server.Timing.CurTick.Value;
|
||||
cTick = (int)client.Timing.CurTick.Value;
|
||||
delta = cTick - sTick;
|
||||
if (delta != 1)
|
||||
throw new Exception("Failed setup");
|
||||
}
|
||||
|
||||
// Backpack
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// Misc backpack contents.
|
||||
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
// Set up map and spawn player
|
||||
server.WaitPost(() =>
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
|
||||
var mapId = mapMan.CreateMap();
|
||||
var map = mapMan.GetMapEntityId(mapId);
|
||||
var gridComp = mapMan.CreateGridEntity(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
_gridCoords2 = new EntityCoordinates(grid, .5f, .6f);
|
||||
_mapCoords = new EntityCoordinates(map, 100, 100);
|
||||
|
||||
var playerUid = _entMan.SpawnEntity(null, _mapCoords);
|
||||
|
||||
// Attach player.
|
||||
var session = sPlayerMan.Sessions.First();
|
||||
server.PlayerMan.SetAttachedEntity(session, playerUid);
|
||||
sPlayerMan.JoinGame(session);
|
||||
|
||||
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
|
||||
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
|
||||
_ent = _entMan.Spawn();
|
||||
|
||||
// Quick check that SetCoordinates actually changes the parent as expected
|
||||
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
// Add 5 direct children in slots to represent clothing.
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var id = $"inventory{i}";
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, id);
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// body parts
|
||||
_container.EnsureContainer<Container>(_ent, "body");
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
// Simple organ
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// body part that has another body part / limb
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
|
||||
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Backpack
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// Misc backpack contents.
|
||||
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Emergency box inside of the backpack
|
||||
var box = backpackStorage.ContainedEntities.First();
|
||||
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Deepest child.
|
||||
_child = boxContainer.ContainedEntities.First();
|
||||
_childXform = _query.GetComponent(_child);
|
||||
|
||||
_players = new[] {session};
|
||||
_session = _pvs.PlayerData[session];
|
||||
}).Wait();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
server.WaitRunTicks(1).Wait();
|
||||
client.WaitRunTicks(1).Wait();
|
||||
}
|
||||
|
||||
// Emergency box inside of the backpack
|
||||
var box = backpackStorage.ContainedEntities.First();
|
||||
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
PvsTick();
|
||||
PvsTick();
|
||||
}
|
||||
|
||||
// Deepest child.
|
||||
_child = boxContainer.ContainedEntities.First();
|
||||
_childXform = _query.GetComponent(_child);
|
||||
|
||||
_pvs.ProcessCollections();
|
||||
private void PvsTick()
|
||||
{
|
||||
_session.ClearState();
|
||||
_pvs.CacheSessionData(_players);
|
||||
_pvs.GetVisibleChunks();
|
||||
_pvs.ProcessVisibleChunksSequential();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -140,6 +206,13 @@ public class RecursiveMoveBenchmark
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MoveEntityASmidge()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_transform.SetCoordinates(_ent, _gridCoords2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Like <see cref="MoveEntity"/>, but also processes queued PVS chunk updates.
|
||||
/// </summary>
|
||||
@@ -147,9 +220,18 @@ public class RecursiveMoveBenchmark
|
||||
public void MoveAndUpdateChunks()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_pvs.ProcessCollections();
|
||||
PvsTick();
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
_pvs.ProcessCollections();
|
||||
PvsTick();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MoveASmidgeAndUpdateChunk()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
PvsTick();
|
||||
_transform.SetCoordinates(_ent, _gridCoords2);
|
||||
PvsTick();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="17.0.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="17.8.3" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
|
||||
<PackageReference Include="Pidgin" Version="2.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Link="XamlX\filename" Include="../XamlX/src/XamlX/**/*.cs" />
|
||||
<Compile Remove="../XamlX/src/XamlX/**/SreTypeSystem.cs" />
|
||||
<Compile Remove="../XamlX/src/XamlX/obj/**" />
|
||||
<Compile Include="..\Robust.Client\UserInterface\ControlPropertyAccess.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- XamlX doesn't do NRTs. -->
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
||||
<PackageReference Include="Robust.Natives.Cef" Version="120.1.9" />
|
||||
<PackageReference Include="JetBrains.Annotations" />
|
||||
<PackageReference Include="Robust.Natives.Cef" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -281,7 +281,7 @@ internal partial class AudioManager
|
||||
_bufferedAudioSources.Remove(handle);
|
||||
}
|
||||
|
||||
IAudioSource? IAudioInternal.CreateAudioSource(AudioStream stream)
|
||||
public IAudioSource? CreateAudioSource(AudioStream stream)
|
||||
{
|
||||
var source = AL.GenSource();
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ internal sealed partial class AudioManager : IAudioInternal
|
||||
var error = AL.GetError();
|
||||
if (error != ALError.NoError)
|
||||
{
|
||||
OpenALSawmill.Error("[{0}:{1}] AL error: {2}, {3}", callerMember, callerLineNumber, error, message);
|
||||
OpenALSawmill.Error("[{0}:{1}] AL error: {2}, {3}. Stacktrace is {4}", callerMember, callerLineNumber, error, message, Environment.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed class AudioOverlay : Overlay
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var localPlayer = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
var localPlayer = _playerManager.LocalEntity;
|
||||
|
||||
if (args.ViewportControl == null || localPlayer == null)
|
||||
return;
|
||||
|
||||
@@ -106,8 +106,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
SubscribeNetworkEvent<PlayAudioEntityMessage>(OnEntityAudio);
|
||||
SubscribeNetworkEvent<PlayAudioPositionalMessage>(OnEntityCoordinates);
|
||||
|
||||
CfgManager.OnValueChanged(CVars.AudioAttenuation, OnAudioAttenuation, true);
|
||||
CfgManager.OnValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged, true);
|
||||
Subs.CVar(CfgManager, CVars.AudioAttenuation, OnAudioAttenuation, true);
|
||||
Subs.CVar(CfgManager, CVars.AudioRaycastLength, OnRaycastLengthChanged, true);
|
||||
}
|
||||
|
||||
private void OnAudioState(EntityUid uid, AudioComponent component, ref AfterAutoHandleStateEvent args)
|
||||
@@ -133,13 +133,6 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
_audio.SetMasterGain(value);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
CfgManager.UnsubValueChanged(CVars.AudioAttenuation, OnAudioAttenuation);
|
||||
CfgManager.UnsubValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged);
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void OnAudioPaused(EntityUid uid, AudioComponent component, ref EntityPausedEvent args)
|
||||
{
|
||||
component.Pause();
|
||||
@@ -326,7 +319,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var paramsGain = MathF.Pow(10, component.Params.Volume / 10);
|
||||
var paramsGain = VolumeToGain(component.Params.Volume);
|
||||
|
||||
// Thought I'd never have to manually calculate gain again but this is the least
|
||||
// unpleasant audio I could get at the moment.
|
||||
|
||||
@@ -21,8 +21,6 @@ internal interface IAudioInternal : IAudioManager
|
||||
/// </summary>
|
||||
void FlushALDisposeQueues();
|
||||
|
||||
IAudioSource? CreateAudioSource(AudioStream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a buffered audio source.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Robust.Client.Audio.Sources;
|
||||
using Robust.Shared.Audio.Sources;
|
||||
|
||||
namespace Robust.Client.Audio;
|
||||
|
||||
@@ -8,6 +10,8 @@ namespace Robust.Client.Audio;
|
||||
/// </summary>
|
||||
public interface IAudioManager
|
||||
{
|
||||
IAudioSource? CreateAudioSource(AudioStream stream);
|
||||
|
||||
AudioStream LoadAudioOggVorbis(Stream stream, string? name = null);
|
||||
|
||||
AudioStream LoadAudioWav(Stream stream, string? name = null);
|
||||
|
||||
@@ -72,7 +72,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
// To avoid lock contention until some kind of MIDI refactor.
|
||||
private TimeSpan _nextUpdate;
|
||||
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(0.1f);
|
||||
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(0.25f);
|
||||
|
||||
private SemaphoreSlim _updateSemaphore = new(1);
|
||||
|
||||
@@ -114,7 +114,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
"/usr/share/sounds/sf2/TimGM6mb.sf2",
|
||||
};
|
||||
|
||||
private const string WindowsSoundfont = @"C:\WINDOWS\system32\drivers\gm.dls";
|
||||
private static readonly string WindowsSoundfont = $@"{Environment.GetEnvironmentVariable("SystemRoot")}\system32\drivers\gm.dls";
|
||||
|
||||
private const string OsxSoundfont =
|
||||
"/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls";
|
||||
@@ -192,7 +192,12 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
_settings["synth.midi-bank-select"].StringValue = "gm";
|
||||
//_settings["synth.verbose"].IntValue = 1; // Useful for debugging.
|
||||
|
||||
_parallel.AddAndInvokeParallelCountChanged(UpdateParallelCount);
|
||||
var midiParallel = _cfgMan.GetCVar(CVars.MidiParallelism);
|
||||
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(midiParallel) * 2048), 1, 65535);
|
||||
_settings["synth.cpu-cores"].IntValue = Math.Clamp(midiParallel, 1, 256);
|
||||
|
||||
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
|
||||
_midiSawmill.Debug($"Synth Polyphony: {_settings["synth.polyphony"].IntValue}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -201,7 +206,10 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
return;
|
||||
}
|
||||
|
||||
_midiThread = new Thread(ThreadUpdate);
|
||||
_midiThread = new Thread(ThreadUpdate)
|
||||
{
|
||||
Name = "RobustToolbox MIDI Thread"
|
||||
};
|
||||
_midiThread.Start();
|
||||
|
||||
_updateJob = new MidiUpdateJob()
|
||||
@@ -219,18 +227,6 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
FluidsynthInitialized = true;
|
||||
}
|
||||
|
||||
private void UpdateParallelCount()
|
||||
{
|
||||
if (_settings == null)
|
||||
return;
|
||||
|
||||
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(_parallel.ParallelProcessCount) * 2048), 1, 65535);
|
||||
_settings["synth.cpu-cores"].IntValue = Math.Clamp(_parallel.ParallelProcessCount, 1, 256);
|
||||
|
||||
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
|
||||
_midiSawmill.Debug($"Synth Polyphony: {_settings["synth.polyphony"].IntValue}");
|
||||
}
|
||||
|
||||
private void LoggerDelegate(NFluidsynth.Logger.LogLevel level, string message, IntPtr data)
|
||||
{
|
||||
var rLevel = level switch
|
||||
|
||||
@@ -38,6 +38,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
private readonly Synth _synth;
|
||||
private readonly Sequencer _sequencer;
|
||||
private NFluidsynth.Player? _player;
|
||||
private int _playerTotalTicks;
|
||||
private MidiDriver? _driver;
|
||||
private byte _midiProgram = 1;
|
||||
private byte _midiBank = 1;
|
||||
@@ -144,7 +145,21 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
public bool DisableProgramChangeEvent { get; set; } = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int PlayerTotalTick => _player?.GetTotalTicks ?? 0;
|
||||
public int PlayerTotalTick
|
||||
{
|
||||
get
|
||||
{
|
||||
// GetTotalTicks is really expensive (has to iterate the entire file, not cached).
|
||||
// Slight problem with caching it ourselves: the value only becomes available when the player loads the MIDI file.
|
||||
// And that only happens after playback really starts, with the timer and synth and all that stuff.
|
||||
// So we cache it "as soon as it's available", i.e. not 0.
|
||||
// We don't care about playlists and such, so it shouldn't change anymore after.
|
||||
if (_playerTotalTicks != 0)
|
||||
return _playerTotalTicks;
|
||||
|
||||
return _playerTotalTicks = _player?.GetTotalTicks ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int PlayerTick
|
||||
@@ -339,6 +354,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
return false;
|
||||
}
|
||||
|
||||
_playerTotalTicks = 0;
|
||||
_player?.Dispose();
|
||||
_player = new NFluidsynth.Player(_synth);
|
||||
_player.SetPlaybackCallback(MidiPlayerEventHandler);
|
||||
@@ -377,6 +393,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
_player?.Join();
|
||||
_player?.Dispose();
|
||||
_player = null;
|
||||
_playerTotalTicks = 0;
|
||||
}
|
||||
|
||||
StopAllNotes();
|
||||
|
||||
@@ -10,7 +10,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Audio.Sources;
|
||||
|
||||
internal abstract class BaseAudioSource : IAudioSource
|
||||
public abstract class BaseAudioSource : IAudioSource
|
||||
{
|
||||
/*
|
||||
* This may look weird having all these methods here however
|
||||
@@ -27,7 +27,7 @@ internal abstract class BaseAudioSource : IAudioSource
|
||||
/// </summary>
|
||||
protected int FilterHandle;
|
||||
|
||||
protected readonly AudioManager Master;
|
||||
internal readonly AudioManager Master;
|
||||
|
||||
/// <summary>
|
||||
/// Prior gain that was set.
|
||||
@@ -38,13 +38,14 @@ internal abstract class BaseAudioSource : IAudioSource
|
||||
|
||||
private bool IsEfxSupported => Master.IsEfxSupported;
|
||||
|
||||
protected BaseAudioSource(AudioManager master, int sourceHandle)
|
||||
internal BaseAudioSource(AudioManager master, int sourceHandle)
|
||||
{
|
||||
Master = master;
|
||||
SourceHandle = sourceHandle;
|
||||
AL.GetSource(SourceHandle, ALSourcef.Gain, out _gain);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Pause()
|
||||
{
|
||||
AL.SourcePause(SourceHandle);
|
||||
@@ -68,6 +69,13 @@ internal abstract class BaseAudioSource : IAudioSource
|
||||
Playing = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Restart()
|
||||
{
|
||||
AL.SourceRewind(SourceHandle);
|
||||
StartPlaying();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool Playing
|
||||
{
|
||||
@@ -338,7 +346,7 @@ internal abstract class BaseAudioSource : IAudioSource
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAuxiliary(IAuxiliaryAudio? audio)
|
||||
void IAudioSource.SetAuxiliary(IAuxiliaryAudio? audio)
|
||||
{
|
||||
_checkDisposed();
|
||||
if (!IsEfxSupported)
|
||||
|
||||
@@ -229,7 +229,7 @@ namespace Robust.Client
|
||||
|
||||
// Don't invoke PlayerLeaveServer if PlayerJoinedServer & GameStartedSetup hasn't been called yet.
|
||||
if (RunLevel > ClientRunLevel.Connecting)
|
||||
PlayerLeaveServer?.Invoke(this, new PlayerEventArgs(_playMan.LocalPlayer?.Session));
|
||||
PlayerLeaveServer?.Invoke(this, new PlayerEventArgs(_playMan.LocalSession));
|
||||
|
||||
LastDisconnectReason = args.Reason;
|
||||
GameStoppedReset();
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace Robust.Client.Console
|
||||
}
|
||||
|
||||
args.RemoveAt(0);
|
||||
var shell = new ConsoleShell(this, session ?? _player.LocalPlayer?.Session, session == null);
|
||||
var shell = new ConsoleShell(this, session ?? _player.LocalSession, session == null);
|
||||
var cmdArgs = args.ToArray();
|
||||
|
||||
AnyCommandExecuted?.Invoke(shell, commandName, command, cmdArgs);
|
||||
@@ -200,8 +200,7 @@ namespace Robust.Client.Console
|
||||
// When not connected to a server, you can run all local commands.
|
||||
// When connected to a server, you can only run commands according to the con group controller.
|
||||
|
||||
return _player.LocalPlayer == null
|
||||
|| _player.LocalPlayer.Session.Status <= SessionStatus.Connecting
|
||||
return _player.LocalSession is not { Status: > SessionStatus.Connecting }
|
||||
|| _conGroup.CanCommand(cmdName);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var controlled = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (controlled == EntityUid.Invalid)
|
||||
if (_playerManager.LocalEntity is not { } controlled)
|
||||
{
|
||||
shell.WriteLine("You don't have an attached entity.");
|
||||
return;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -494,9 +495,9 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal static List<(string, string)> PropertyValuesFor(Control control)
|
||||
internal static List<MemberInfo> GetAllMembers(Control control)
|
||||
{
|
||||
var members = new List<(string, string)>();
|
||||
var members = new List<MemberInfo>();
|
||||
var type = control.GetType();
|
||||
|
||||
foreach (var fieldInfo in type.GetAllFields())
|
||||
@@ -506,7 +507,7 @@ namespace Robust.Client.Console.Commands
|
||||
continue;
|
||||
}
|
||||
|
||||
members.Add((fieldInfo.Name, fieldInfo.GetValue(control)?.ToString() ?? "null"));
|
||||
members.Add(fieldInfo);
|
||||
}
|
||||
|
||||
foreach (var propertyInfo in type.GetAllProperties())
|
||||
@@ -516,7 +517,19 @@ namespace Robust.Client.Console.Commands
|
||||
continue;
|
||||
}
|
||||
|
||||
members.Add((propertyInfo.Name, propertyInfo.GetValue(control)?.ToString() ?? "null"));
|
||||
members.Add(propertyInfo);
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
internal static List<(string, string)> PropertyValuesFor(Control control)
|
||||
{
|
||||
var members = new List<(string, string)>();
|
||||
|
||||
foreach (var fieldInfo in GetAllMembers(control))
|
||||
{
|
||||
members.Add((fieldInfo.Name, fieldInfo.GetValue(control)?.ToString() ?? "null"));
|
||||
}
|
||||
|
||||
foreach (var (attachedProperty, value) in control.AllAttachedProperties)
|
||||
@@ -528,6 +541,35 @@ namespace Robust.Client.Console.Commands
|
||||
members.Sort((a, b) => string.Compare(a.Item1, b.Item1, StringComparison.Ordinal));
|
||||
return members;
|
||||
}
|
||||
|
||||
internal static Dictionary<string, List<(string, string)>> PropertyValuesForInheritance(Control control)
|
||||
{
|
||||
var returnVal = new Dictionary<string, List<(string, string)>>();
|
||||
var engine = typeof(Control).Assembly;
|
||||
|
||||
foreach (var member in GetAllMembers(control))
|
||||
{
|
||||
var type = member.DeclaringType!;
|
||||
var cname = type.Assembly == engine ? type.Name : type.ToString();
|
||||
|
||||
if (type != typeof(Control))
|
||||
cname = $"Control > {cname}";
|
||||
|
||||
returnVal.GetOrNew(cname).Add((member.Name, member.GetValue(control)?.ToString() ?? "null"));
|
||||
}
|
||||
|
||||
foreach (var (attachedProperty, value) in control.AllAttachedProperties)
|
||||
{
|
||||
var cname = $"Attached > {attachedProperty.OwningType.Name}";
|
||||
returnVal.GetOrNew(cname).Add((attachedProperty.Name, value?.ToString() ?? "null"));
|
||||
}
|
||||
|
||||
foreach (var v in returnVal.Values)
|
||||
{
|
||||
v.Sort((a, b) => string.Compare(a.Item1, b.Item1, StringComparison.Ordinal));
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SetClipboardCommand : LocalizedCommands
|
||||
|
||||
@@ -43,6 +43,7 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
GC.Collect();
|
||||
|
||||
Span<EntityUid> ents = stackalloc EntityUid[amount];
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
@@ -50,12 +51,17 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
_entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
|
||||
ents[i] = _entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
MeasureProfiler.SaveData();
|
||||
|
||||
shell.WriteLine($"Client: Profiled spawning {amount} entities in {stopwatch.Elapsed.TotalMilliseconds:N3} ms");
|
||||
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
_entities.DeleteEntity(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -420,7 +420,7 @@ namespace Robust.Client.Debugging
|
||||
if (mapPos.MapId != args.MapId)
|
||||
return;
|
||||
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
var player = _playerManager.LocalEntity;
|
||||
|
||||
if (!_entityManager.TryGetComponent<TransformComponent>(player, out var playerXform) ||
|
||||
playerXform.MapID != args.MapId)
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
|
||||
<xs:element name="Weavers">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="InlineIL" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="SequencePoints">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Defines if sequence points should be generated for each emitted IL instruction. Default value: Debug</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Debug">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Insert sequence points in Debug builds only (this is the default).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Release">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Insert sequence points in Release builds only.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="True">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Always insert sequence points.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="False">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Never insert sequence points.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Warnings">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Defines how warnings should be handled. Default value: Warnings</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Warnings">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Emit build warnings (this is the default).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Ignore">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Do not emit warnings.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Errors">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Treat warnings as errors.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="GenerateXsd" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -35,8 +35,6 @@ namespace Robust.Client
|
||||
throw new InvalidOperationException("Cannot start twice!");
|
||||
}
|
||||
|
||||
GlibcBug.Check();
|
||||
|
||||
_hasStarted = true;
|
||||
|
||||
if (CommandLineArgs.TryParse(args, out var parsed))
|
||||
|
||||
@@ -42,6 +42,8 @@ public sealed partial class ClientEntityManager
|
||||
var pending = PendingNetEntityStates.GetOrNew(nEntity);
|
||||
pending.Add((typeof(T), callerEntity));
|
||||
|
||||
|
||||
|
||||
return entity.Item1;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void QueueDeleteEntity(EntityUid? uid)
|
||||
{
|
||||
if (uid == null)
|
||||
if (uid == null || uid == EntityUid.Invalid)
|
||||
return;
|
||||
|
||||
if (IsClientSide(uid.Value))
|
||||
@@ -119,25 +119,17 @@ namespace Robust.Client.GameObjects
|
||||
base.Dirty(ent, meta);
|
||||
}
|
||||
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metaDataComponent = null)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == uid)
|
||||
return base.ToPrettyString(uid) with { Session = _playerManager.LocalPlayer.Session };
|
||||
|
||||
return base.ToPrettyString(uid);
|
||||
}
|
||||
|
||||
public override void RaisePredictiveEvent<T>(T msg)
|
||||
{
|
||||
var localPlayer = _playerManager.LocalPlayer;
|
||||
DebugTools.AssertNotNull(localPlayer);
|
||||
var session = _playerManager.LocalSession;
|
||||
DebugTools.AssertNotNull(session);
|
||||
|
||||
var sequence = _stateMan.SystemMessageDispatched(msg);
|
||||
EntityNetManager?.SendSystemNetworkMessage(msg, sequence);
|
||||
|
||||
DebugTools.Assert(!_stateMan.IsPredictionEnabled || _gameTiming.InPrediction && _gameTiming.IsFirstTimePredicted || _client.RunLevel != ClientRunLevel.Connected);
|
||||
|
||||
var eventArgs = new EntitySessionEventArgs(localPlayer!.Session);
|
||||
var eventArgs = new EntitySessionEventArgs(session!);
|
||||
EventBus.RaiseEvent(EventSource.Local, msg);
|
||||
EventBus.RaiseEvent(EventSource.Local, new EntitySessionMessage<T>(eventArgs, msg));
|
||||
}
|
||||
@@ -229,7 +221,7 @@ namespace Robust.Client.GameObjects
|
||||
public void DispatchReceivedNetworkMsg(EntityEventArgs msg)
|
||||
{
|
||||
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
|
||||
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(_playerManager.LocalPlayer!.Session), msg)!;
|
||||
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(_playerManager.LocalSession!), msg)!;
|
||||
ReceivedSystemMessage?.Invoke(this, msg);
|
||||
ReceivedSystemMessage?.Invoke(this, sessionMsg);
|
||||
}
|
||||
|
||||
@@ -2068,6 +2068,8 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
AnimationFrame = delayCount - 1;
|
||||
}
|
||||
|
||||
AnimationTime = -AnimationTimeLeft;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2086,10 +2088,11 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
AnimationFrame = 0;
|
||||
}
|
||||
|
||||
AnimationTime = -AnimationTimeLeft;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationTime = -AnimationTimeLeft;
|
||||
AnimationTimeLeft += state.GetDelay(AnimationFrame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Enumerators;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Robust.Shared.GameObjects.OccluderComponent;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
@@ -30,17 +27,15 @@ internal sealed class ClientOccluderSystem : OccluderSystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<OccluderComponent, AnchorStateChangedEvent>(OnAnchorChanged);
|
||||
SubscribeLocalEvent<OccluderComponent, ReAnchorEvent>(OnReAnchor);
|
||||
SubscribeLocalEvent<OccluderComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
public override void SetEnabled(EntityUid uid, bool enabled, OccluderComponent? comp = null)
|
||||
public override void SetEnabled(EntityUid uid, bool enabled, OccluderComponent? comp = null, MetaDataComponent? meta = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false) || enabled == comp.Enabled)
|
||||
return;
|
||||
|
||||
comp.Enabled = enabled;
|
||||
Dirty(uid, comp);
|
||||
base.SetEnabled(uid, enabled, comp, meta);
|
||||
|
||||
var xform = Transform(uid);
|
||||
QueueTreeUpdate(uid, comp, xform);
|
||||
@@ -94,11 +89,6 @@ internal sealed class ClientOccluderSystem : OccluderSystem
|
||||
AnchorStateChanged(uid, comp, args.Transform);
|
||||
}
|
||||
|
||||
private void OnReAnchor(EntityUid uid, OccluderComponent comp, ref ReAnchorEvent args)
|
||||
{
|
||||
AnchorStateChanged(uid, comp, args.Xform);
|
||||
}
|
||||
|
||||
private void QueueOccludedDirectionUpdate(EntityUid sender, OccluderComponent occluder, TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(sender, ref xform))
|
||||
@@ -178,8 +168,9 @@ internal sealed class ClientOccluderSystem : OccluderSystem
|
||||
|
||||
var tile = grid.TileIndicesFor(xform.Coordinates);
|
||||
|
||||
DebugTools.Assert(occluder.LastPosition == null
|
||||
|| occluder.LastPosition.Value.Grid == xform.GridUid && occluder.LastPosition.Value.Tile == tile);
|
||||
// TODO: Sub to parent changes instead or something.
|
||||
// DebugTools.Assert(occluder.LastPosition == null
|
||||
// || occluder.LastPosition.Value.Grid == xform.GridUid && occluder.LastPosition.Value.Tile == tile);
|
||||
occluder.LastPosition = (xform.GridUid.Value, tile);
|
||||
|
||||
// dir starts at the relative effective south direction;
|
||||
|
||||
@@ -54,14 +54,26 @@ namespace Robust.Client.GameObjects
|
||||
DebugTools.Assert(ExpectedEntities.TryGetValue(netEntity, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(netEntity));
|
||||
}
|
||||
|
||||
private void HandleEntityInitialized(EntityUid uid)
|
||||
private void HandleEntityInitialized(Entity<MetaDataComponent> ent)
|
||||
{
|
||||
if (!RemoveExpectedEntity(GetNetEntity(uid), out var container))
|
||||
var (uid, meta) = ent;
|
||||
if (!RemoveExpectedEntity(meta.NetEntity, out var container))
|
||||
return;
|
||||
|
||||
Insert((uid, TransformQuery.GetComponent(uid), MetaQuery.GetComponent(uid), null), container);
|
||||
}
|
||||
|
||||
public override void ShutdownContainer(BaseContainer container)
|
||||
{
|
||||
foreach (var ent in container.ExpectedEntities)
|
||||
{
|
||||
if (ExpectedEntities.Remove(ent, out var c))
|
||||
DebugTools.Assert(c == container);
|
||||
}
|
||||
|
||||
base.ShutdownContainer(container);
|
||||
}
|
||||
|
||||
private void HandleComponentState(EntityUid uid, ContainerManagerComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not ContainerManagerComponentState cast)
|
||||
|
||||
@@ -105,12 +105,10 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="inputCmd">Input command to handle as predicted.</param>
|
||||
public void PredictInputCommand(IFullInputCmdMessage inputCmd)
|
||||
{
|
||||
DebugTools.AssertNotNull(_playerManager.LocalPlayer);
|
||||
|
||||
var keyFunc = _inputManager.NetworkBindMap.KeyFunctionName(inputCmd.InputFunctionId);
|
||||
|
||||
Predicted = true;
|
||||
var session = _playerManager.LocalPlayer!.Session;
|
||||
var session = _playerManager.LocalSession;
|
||||
foreach (var handler in BindRegistry.GetHandlers(keyFunc))
|
||||
{
|
||||
if (handler.HandleCmdMessage(EntityManager, session, inputCmd))
|
||||
@@ -145,27 +143,22 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void GenerateInputCommand(IConsoleShell shell, string argstr, string[] args)
|
||||
{
|
||||
var localPlayer = _playerManager.LocalPlayer;
|
||||
if(localPlayer is null)
|
||||
return;
|
||||
|
||||
var pent = localPlayer.ControlledEntity;
|
||||
if(pent is null)
|
||||
if (_playerManager.LocalEntity is not { } pent)
|
||||
return;
|
||||
|
||||
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
|
||||
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
|
||||
|
||||
var pxform = Transform(pent.Value);
|
||||
var pxform = Transform(pent);
|
||||
var wPos = pxform.WorldPosition + new Vector2(float.Parse(args[2]), float.Parse(args[3]));
|
||||
var coords = EntityCoordinates.FromMap(EntityManager, pent.Value, new MapCoordinates(wPos, pxform.MapID));
|
||||
var coords = EntityCoordinates.FromMap(EntityManager, pent, new MapCoordinates(wPos, pxform.MapID));
|
||||
|
||||
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
|
||||
|
||||
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
|
||||
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
|
||||
|
||||
HandleInputCommand(localPlayer.Session, keyFunction, message);
|
||||
HandleInputCommand(_playerManager.LocalSession, keyFunction, message);
|
||||
}
|
||||
|
||||
private void OnAttachedEntityChanged(LocalPlayerAttachedEvent message)
|
||||
@@ -208,11 +201,8 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void SetEntityContextActive()
|
||||
{
|
||||
var controlled = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (controlled == EntityUid.Invalid)
|
||||
{
|
||||
if (_playerManager.LocalEntity is not { } controlled)
|
||||
return;
|
||||
}
|
||||
|
||||
SetEntityContextActive(_inputManager, controlled);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,10 @@ namespace Robust.Client.GameObjects
|
||||
return RemCompDeferred<PointLightComponent>(uid);
|
||||
}
|
||||
|
||||
protected override void UpdatePriority(EntityUid uid, SharedPointLightComponent comp, MetaDataComponent meta)
|
||||
{
|
||||
}
|
||||
|
||||
private void HandleInit(EntityUid uid, PointLightComponent component, ComponentInit args)
|
||||
{
|
||||
SetMask(component.MaskPath, component);
|
||||
@@ -95,28 +99,23 @@ namespace Robust.Client.GameObjects
|
||||
_lightTree.QueueTreeUpdate(uid, clientComp);
|
||||
}
|
||||
|
||||
public override void SetEnabled(EntityUid uid, bool enabled, SharedPointLightComponent? comp = null)
|
||||
public override void SetEnabled(EntityUid uid, bool enabled, SharedPointLightComponent? comp = null, MetaDataComponent? meta = null)
|
||||
{
|
||||
if (!ResolveLight(uid, ref comp) || enabled == comp.Enabled || comp is not PointLightComponent clientComp)
|
||||
return;
|
||||
|
||||
comp.Enabled = enabled;
|
||||
RaiseLocalEvent(uid, new PointLightToggleEvent(comp.Enabled));
|
||||
Dirty(uid, comp);
|
||||
|
||||
base.SetEnabled(uid, enabled, comp, meta);
|
||||
if (!comp.ContainerOccluded)
|
||||
_lightTree.QueueTreeUpdate(uid, clientComp);
|
||||
}
|
||||
|
||||
public override void SetRadius(EntityUid uid, float radius, SharedPointLightComponent? comp = null)
|
||||
public override void SetRadius(EntityUid uid, float radius, SharedPointLightComponent? comp = null, MetaDataComponent? meta = null)
|
||||
{
|
||||
if (!ResolveLight(uid, ref comp) || MathHelper.CloseToPercent(radius, comp.Radius) ||
|
||||
comp is not PointLightComponent clientComp)
|
||||
return;
|
||||
|
||||
comp.Radius = radius;
|
||||
Dirty(uid, comp);
|
||||
|
||||
base.SetRadius(uid, radius, comp, meta);
|
||||
if (clientComp.TreeUid != null)
|
||||
_lightTree.QueueTreeUpdate(uid, clientComp);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Robust.Client.GameObjects
|
||||
SubscribeLocalEvent<SpriteComponent, SpriteUpdateInertEvent>(QueueUpdateInert);
|
||||
SubscribeLocalEvent<SpriteComponent, ComponentInit>(OnInit);
|
||||
|
||||
_cfg.OnValueChanged(CVars.RenderSpriteDirectionBias, OnBiasChanged, true);
|
||||
Subs.CVar(_cfg, CVars.RenderSpriteDirectionBias, OnBiasChanged, true);
|
||||
_sawmill = _logManager.GetSawmill("sprite");
|
||||
}
|
||||
|
||||
@@ -72,12 +72,6 @@ namespace Robust.Client.GameObjects
|
||||
QueueUpdateInert(uid, component);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_cfg.UnsubValueChanged(CVars.RenderSpriteDirectionBias, OnBiasChanged);
|
||||
}
|
||||
|
||||
private void OnBiasChanged(double value)
|
||||
{
|
||||
SpriteComponent.DirectionBias = value;
|
||||
|
||||
@@ -29,10 +29,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var uiKey = ev.UiKey;
|
||||
var message = ev.Message;
|
||||
// This should probably not happen at this point, but better make extra sure!
|
||||
if (_playerManager.LocalPlayer != null)
|
||||
message.Session = _playerManager.LocalPlayer.Session;
|
||||
|
||||
message.Session = _playerManager.LocalSession!;
|
||||
message.Entity = GetNetEntity(uid);
|
||||
message.UiKey = uiKey;
|
||||
|
||||
@@ -75,8 +72,7 @@ namespace Robust.Client.GameObjects
|
||||
boundInterface.Open();
|
||||
uiComp.OpenInterfaces[uiKey] = boundInterface;
|
||||
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if (playerSession != null)
|
||||
if (_playerManager.LocalSession is { } playerSession)
|
||||
{
|
||||
uiComp.Interfaces[uiKey]._subscribedSessions.Add(playerSession);
|
||||
RaiseLocalEvent(uid, new BoundUIOpenedEvent(uiKey, uid, playerSession), true);
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
var player = _playerManager.LocalEntity;
|
||||
|
||||
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
return;
|
||||
|
||||
// Client-side entity deletion is not supported and will cause errors.
|
||||
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
|
||||
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity.Owner, ev.Entity.Comp)}. Trace: {Environment.StackTrace}");
|
||||
}
|
||||
|
||||
private void OnCompRemoved(RemovedComponentEventArgs args)
|
||||
@@ -71,9 +71,9 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
RemovedComponents.Clear();
|
||||
}
|
||||
|
||||
private void OnEntityDirty(EntityUid e)
|
||||
private void OnEntityDirty(Entity<MetaDataComponent> e)
|
||||
{
|
||||
if (_timing.InPrediction && !IsClientSide(e))
|
||||
if (_timing.InPrediction && !IsClientSide(e, e))
|
||||
DirtyEntities.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,16 +49,16 @@ namespace Robust.Client.GameStates
|
||||
// Game state dictionaries that get used every tick.
|
||||
private readonly Dictionary<EntityUid, (NetEntity NetEntity, MetaDataComponent Meta, bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
|
||||
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
|
||||
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
|
||||
private readonly Dictionary<ushort, (IComponent Component, IComponentState? curState, IComponentState? nextState)> _compStateWork = new();
|
||||
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
|
||||
private readonly HashSet<NetEntity> _stateEnts = new();
|
||||
private readonly List<EntityUid> _toDelete = new();
|
||||
private readonly List<IComponent> _toRemove = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _outputData = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _outputData = new();
|
||||
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
|
||||
|
||||
private readonly ObjectPool<Dictionary<ushort, ComponentState>> _compDataPool =
|
||||
new DefaultObjectPool<Dictionary<ushort, ComponentState>>(new DictPolicy<ushort, ComponentState>(), 256);
|
||||
private readonly ObjectPool<Dictionary<ushort, IComponentState>> _compDataPool =
|
||||
new DefaultObjectPool<Dictionary<ushort, IComponentState>>(new DictPolicy<ushort, IComponentState>(), 256);
|
||||
|
||||
private uint _metaCompNetId;
|
||||
|
||||
@@ -232,9 +232,9 @@ namespace Robust.Client.GameStates
|
||||
return default;
|
||||
}
|
||||
|
||||
DebugTools.AssertNotNull(_players.LocalPlayer);
|
||||
DebugTools.Assert(_players.LocalSession != null);
|
||||
|
||||
var evArgs = new EntitySessionEventArgs(_players.LocalPlayer!.Session);
|
||||
var evArgs = new EntitySessionEventArgs(_players.LocalSession);
|
||||
_pendingSystemMessages.Enqueue((_nextInputCmdSeq, _timing.CurTick, message,
|
||||
new EntitySessionMessage<T>(evArgs, message)));
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace Robust.Client.GameStates
|
||||
public void UpdateFullRep(GameState state, bool cloneDelta = false)
|
||||
=> _processor.UpdateFullRep(state, cloneDelta);
|
||||
|
||||
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
|
||||
=> _processor.GetFullRep();
|
||||
|
||||
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
|
||||
@@ -343,7 +343,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
// If we were waiting for a new state, we are now applying it.
|
||||
if (_processor.WaitingForFull)
|
||||
if (curState.FromSequence == GameTick.Zero)
|
||||
{
|
||||
_processor.OnFullStateReceived();
|
||||
_timing.LastProcessedTick = curState.ToSequence;
|
||||
@@ -351,7 +351,10 @@ namespace Robust.Client.GameStates
|
||||
PartialStateReset(curState, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTools.Assert(!_processor.WaitingForFull);
|
||||
_timing.LastProcessedTick += 1;
|
||||
}
|
||||
|
||||
_timing.CurTick = _timing.LastRealTick = _timing.LastProcessedTick;
|
||||
|
||||
@@ -776,15 +779,13 @@ namespace Robust.Client.GameStates
|
||||
newMeta.LastStateApplied = curState.ToSequence;
|
||||
|
||||
// Check if there's any component states awaiting this entity.
|
||||
if (_entityManager.PendingNetEntityStates.TryGetValue(es.NetEntity, out var value))
|
||||
if (_entityManager.PendingNetEntityStates.Remove(es.NetEntity, out var value))
|
||||
{
|
||||
foreach (var (type, owner) in value)
|
||||
{
|
||||
var pending = _pendingReapplyNetStates.GetOrNew(owner);
|
||||
pending.Add(type);
|
||||
}
|
||||
|
||||
_entityManager.PendingNetEntityStates.Remove(es.NetEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,6 +849,17 @@ namespace Robust.Client.GameStates
|
||||
if (!metas.TryGetComponent(uid, out var meta))
|
||||
continue;
|
||||
|
||||
// It may also have been queued for deletion, in which case its last server state entry has already been removed.
|
||||
// I love me some spaghetti order-of-operation dependent code
|
||||
|
||||
if (!_processor._lastStateFullRep.ContainsKey(meta.NetEntity))
|
||||
{
|
||||
DebugTools.Assert(curState.EntityDeletions.Value.Contains(meta.NetEntity));
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(!curState.EntityDeletions.Value.Contains(meta.NetEntity));
|
||||
|
||||
// State already being re-applied so don't bulldoze it.
|
||||
ref var state = ref CollectionsMarshal.GetValueRefOrAddDefault(_toApply, uid, out var exists);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
|
||||
/// </summary>
|
||||
internal readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _lastStateFullRep
|
||||
internal readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _lastStateFullRep
|
||||
= new();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -212,7 +212,7 @@ Had full state: {LastFullState != null}"
|
||||
{
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
|
||||
{
|
||||
compData = new Dictionary<ushort, ComponentState>();
|
||||
compData = new Dictionary<ushort, IComponentState>();
|
||||
_lastStateFullRep.Add(entityState.NetEntity, compData);
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ Had full state: {LastFullState != null}"
|
||||
LastFullStateRequested = null;
|
||||
}
|
||||
|
||||
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> implicitData)
|
||||
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> implicitData)
|
||||
{
|
||||
foreach (var (netEntity, implicitEntState) in implicitData)
|
||||
{
|
||||
@@ -425,18 +425,18 @@ Had full state: {LastFullState != null}"
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity netEntity)
|
||||
public Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity netEntity)
|
||||
{
|
||||
return _lastStateFullRep[netEntity];
|
||||
}
|
||||
|
||||
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
|
||||
{
|
||||
return _lastStateFullRep;
|
||||
}
|
||||
|
||||
public bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary)
|
||||
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary)
|
||||
{
|
||||
return _lastStateFullRep.TryGetValue(entity, out dictionary);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Returns the full collection of cached game states that are used to reset predicted entities.
|
||||
/// </summary>
|
||||
Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep();
|
||||
Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep();
|
||||
|
||||
/// <summary>
|
||||
/// This will perform some setup in order to reset the game to an earlier state. To fully reset the state
|
||||
|
||||
@@ -83,13 +83,13 @@ namespace Robust.Client.GameStates
|
||||
/// The data to merge.
|
||||
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
|
||||
/// </param>
|
||||
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> data);
|
||||
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> data);
|
||||
|
||||
/// <summary>
|
||||
/// Get the last state data from the server for an entity.
|
||||
/// </summary>
|
||||
/// <returns>Dictionary (net ID -> ComponentState)</returns>
|
||||
Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity entity);
|
||||
Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the number of applicable states in the game state buffer from a given tick.
|
||||
@@ -99,6 +99,6 @@ namespace Robust.Client.GameStates
|
||||
int GetApplicableStateCount(GameTick? fromTick);
|
||||
|
||||
bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary);
|
||||
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
entity = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
entity = _playerManager.LocalEntity ?? EntityUid.Invalid;
|
||||
}
|
||||
else if (!NetEntity.TryParse(args[0], out var netEntity) || !_entManager.TryGetEntity(netEntity, out entity))
|
||||
{
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private int _verticesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * 4;
|
||||
private int _indicesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
private void _drawGrids(Viewport viewport, Box2 worldAABB, Box2Rotated worldBounds, IEye eye)
|
||||
{
|
||||
var mapId = eye.Position.MapId;
|
||||
@@ -37,14 +39,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
mapId = MapId.Nullspace;
|
||||
}
|
||||
|
||||
var grids = new List<Entity<MapGridComponent>>();
|
||||
_mapManager.FindGridsIntersecting(mapId, worldBounds, ref grids);
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(mapId, worldBounds, ref _grids);
|
||||
|
||||
var requiresFlush = true;
|
||||
GLShaderProgram gridProgram = default!;
|
||||
var gridOverlays = GetOverlaysForSpace(OverlaySpace.WorldSpaceGrids);
|
||||
var mapSystem = _entityManager.System<SharedMapSystem>();
|
||||
|
||||
foreach (var mapGrid in grids)
|
||||
foreach (var mapGrid in _grids)
|
||||
{
|
||||
if (!_mapChunkData.TryGetValue(mapGrid, out var data))
|
||||
{
|
||||
@@ -65,7 +68,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(mapGrid);
|
||||
gridProgram.SetUniform(UniIModelMatrix, transform.WorldMatrix);
|
||||
var enumerator = mapGrid.Comp.GetMapChunks(worldBounds);
|
||||
var enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
|
||||
|
||||
while (enumerator.MoveNext(out var chunk))
|
||||
{
|
||||
|
||||
@@ -198,6 +198,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
IRenderTexture IClydeViewport.RenderTarget => RenderTarget;
|
||||
IRenderTexture IClydeViewport.LightRenderTarget => LightRenderTarget;
|
||||
public IEye? Eye { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,8 +224,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
fixed (char* pCaption = "RobustToolbox: Failed to create window")
|
||||
{
|
||||
Windows.MessageBoxW(HWND.NULL,
|
||||
(ushort*) pText,
|
||||
(ushort*) pCaption,
|
||||
pText,
|
||||
pCaption,
|
||||
MB.MB_OK | MB.MB_ICONERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,6 +463,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public IRenderTexture RenderTarget { get; } =
|
||||
new DummyRenderTexture(Vector2i.One, new DummyTexture(Vector2i.One));
|
||||
|
||||
public IRenderTexture LightRenderTarget { get; } =
|
||||
new DummyRenderTexture(Vector2i.One, new DummyTexture(Vector2i.One));
|
||||
|
||||
public IEye? Eye { get; set; }
|
||||
public Vector2i Size { get; }
|
||||
public Color? ClearColor { get; set; } = Color.Black;
|
||||
|
||||
@@ -413,7 +413,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
ThrowIfFailed("GetDesc1", _adapter->GetDesc1(&desc));
|
||||
|
||||
var descName = new ReadOnlySpan<char>(desc.Description, 128).TrimEnd('\0');
|
||||
var descName = ((ReadOnlySpan<char>)desc.Description).TrimEnd('\0');
|
||||
|
||||
Logger.DebugS("clyde.ogl.angle", "Successfully created D3D11 device!");
|
||||
Logger.DebugS("clyde.ogl.angle", $"D3D11 Device Adapter: {descName.ToString()}");
|
||||
@@ -493,7 +493,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
ThrowIfFailed("GetDesc1", adapter->GetDesc1(&desc));
|
||||
|
||||
var descName = new ReadOnlySpan<char>(desc.Description, 128);
|
||||
var descName = ((ReadOnlySpan<char>)desc.Description);
|
||||
|
||||
if (descName.StartsWith(name))
|
||||
return adapter;
|
||||
|
||||
@@ -137,5 +137,70 @@ highp vec4 zTexture(highp vec2 uv)
|
||||
return zTextureSpec(TEXTURE, uv);
|
||||
}
|
||||
|
||||
// -- color --
|
||||
|
||||
// Grayscale function for the ITU's Rec BT-709. Primarily intended for HDTVs, but standard sRGB monitors are coincidentally extremely close.
|
||||
highp float zGrayscale_BT709(highp vec3 col) {
|
||||
return dot(col, vec3(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
// Grayscale function for the ITU's Rec BT-601, primarily intended for SDTV, but amazing for a handful of niche use-cases.
|
||||
highp float zGrayscale_BT601(highp vec3 col) {
|
||||
return dot(col, vec3(0.299, 0.587, 0.114));
|
||||
}
|
||||
|
||||
// If you don't have any reason to be specifically using the above grayscale functions, then you should default to this.
|
||||
highp float zGrayscale(highp vec3 col) {
|
||||
return zGrayscale_BT709(col);
|
||||
}
|
||||
|
||||
// -- noise --
|
||||
|
||||
//zRandom, zNoise, and zFBM are derived from https://godotshaders.com/snippet/2d-noise/ and https://godotshaders.com/snippet/fractal-brownian-motion-fbm/
|
||||
highp vec2 zRandom(highp vec2 uv){
|
||||
uv = vec2( dot(uv, vec2(127.1,311.7) ),
|
||||
dot(uv, vec2(269.5,183.3) ) );
|
||||
return -1.0 + 2.0 * fract(sin(uv) * 43758.5453123);
|
||||
}
|
||||
|
||||
highp float zNoise(highp vec2 uv) {
|
||||
highp vec2 uv_index = floor(uv);
|
||||
highp vec2 uv_fract = fract(uv);
|
||||
|
||||
highp vec2 blur = smoothstep(0.0, 1.0, uv_fract);
|
||||
|
||||
return mix( mix( dot( zRandom(uv_index + vec2(0.0,0.0) ), uv_fract - vec2(0.0,0.0) ),
|
||||
dot( zRandom(uv_index + vec2(1.0,0.0) ), uv_fract - vec2(1.0,0.0) ), blur.x),
|
||||
mix( dot( zRandom(uv_index + vec2(0.0,1.0) ), uv_fract - vec2(0.0,1.0) ),
|
||||
dot( zRandom(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
highp float zFBM(highp vec2 uv) {
|
||||
const int octaves = 6;
|
||||
highp float amplitude = 0.5;
|
||||
highp float frequency = 3.0;
|
||||
highp float value = 0.0;
|
||||
|
||||
for(int i = 0; i < octaves; i++) {
|
||||
value += amplitude * zNoise(frequency * uv);
|
||||
amplitude *= 0.5;
|
||||
frequency *= 2.0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// -- generative --
|
||||
|
||||
// Function that creates a circular gradient. Screenspace shader bread n butter.
|
||||
highp float zCircleGradient(highp vec2 ps, highp vec2 coord, highp float maxi, highp float radius, highp float dist, highp float power) {
|
||||
highp float rad = (radius * ps.y) * 0.001;
|
||||
highp float aspectratio = ps.x / ps.y;
|
||||
highp vec2 totaldistance = ((ps * 0.5) - coord) / (rad * ps);
|
||||
totaldistance.x *= aspectratio;
|
||||
highp float length = (length(totaldistance) * ps.y) - dist;
|
||||
return pow(clamp(length, 0.0, maxi), power);
|
||||
}
|
||||
|
||||
// -- Utilities End --
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
adapter->Release();
|
||||
ThrowIfFailed("GetDesc", adapter3->GetDesc2(&desc));
|
||||
|
||||
var descString = new ReadOnlySpan<char>(desc.Description, 128).TrimEnd('\0');
|
||||
var descString = ((ReadOnlySpan<char>)desc.Description).TrimEnd('\0');
|
||||
shell.WriteLine(descString.ToString());
|
||||
|
||||
DXGI_QUERY_VIDEO_MEMORY_INFO memInfo;
|
||||
|
||||
@@ -10,8 +10,11 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using TerraFX.Interop.Windows;
|
||||
using TerraFX.Interop.Xlib;
|
||||
using GlfwImage = OpenToolkit.GraphicsLibraryFramework.Image;
|
||||
using Monitor = OpenToolkit.GraphicsLibraryFramework.Monitor;
|
||||
using Window = OpenToolkit.GraphicsLibraryFramework.Window;
|
||||
using X11Window = TerraFX.Interop.Xlib.Window;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -468,6 +471,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GLFW.MaximizeWindow(window);
|
||||
}
|
||||
|
||||
if ((parameters.Styles & OSWindowStyles.NoTitleBar) != 0)
|
||||
{
|
||||
GLFW.WindowHint(WindowHintBool.Decorated, false);
|
||||
}
|
||||
|
||||
if ((parameters.Styles & OSWindowStyles.NoTitleOptions) != 0)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
@@ -481,6 +489,35 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Cast to long here to work around a bug in rider with nint bitwise operators.
|
||||
(nint)((long)Windows.GetWindowLongPtrW(hWnd, GWL.GWL_STYLE) & ~WS.WS_SYSMENU));
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
try
|
||||
{
|
||||
var x11Window = (X11Window)GLFW.GetX11Window(window);
|
||||
var x11Display = (Display*) GLFW.GetX11Display(window);
|
||||
DebugTools.Assert(x11Window != X11Window.NULL);
|
||||
// https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm46181547486832
|
||||
var newPropValString = Marshal.StringToCoTaskMemUTF8("_NET_WM_WINDOW_TYPE_DIALOG");
|
||||
var newPropVal = Xlib.XInternAtom(x11Display, (sbyte*)newPropValString, Xlib.False);
|
||||
DebugTools.Assert(newPropVal != Atom.NULL);
|
||||
|
||||
var propNameString = Marshal.StringToCoTaskMemUTF8("_NET_WM_WINDOW_TYPE");
|
||||
#pragma warning disable CA1806
|
||||
// [display] [window] [property] [type] [format (8, 16,32)] [mode] [data] [element count]
|
||||
Xlib.XChangeProperty(x11Display, x11Window,
|
||||
Xlib.XInternAtom(x11Display, (sbyte*)propNameString, Xlib.False), // should never be null; part of spec
|
||||
Xlib.XA_ATOM, 32, Xlib.PropModeReplace,
|
||||
(byte*)&newPropVal, 1);
|
||||
#pragma warning restore CA1806
|
||||
|
||||
Marshal.FreeCoTaskMem(newPropValString);
|
||||
Marshal.FreeCoTaskMem(propNameString);
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
_sawmill.Warning("OSWindowStyles.NoTitleOptions not implemented on this windowing manager");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sawmill.Warning("OSWindowStyles.NoTitleOptions not implemented on this platform");
|
||||
@@ -500,6 +537,25 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GWLP.GWLP_HWNDPARENT,
|
||||
ownerHWnd);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
try
|
||||
{
|
||||
var x11Display = (Display*) GLFW.GetX11Display(window);
|
||||
var thisWindow = (X11Window)GLFW.GetX11Window(window);
|
||||
var parentWindow = (X11Window)GLFW.GetX11Window(ownerWindow);
|
||||
DebugTools.Assert(thisWindow != X11Window.NULL);
|
||||
DebugTools.Assert(parentWindow != X11Window.NULL);
|
||||
|
||||
#pragma warning disable CA1806
|
||||
Xlib.XSetTransientForHint(x11Display, thisWindow, parentWindow);
|
||||
#pragma warning restore CA1806
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
_sawmill.Warning("owner windows not implemented on this windowing manager");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sawmill.Warning("owner windows not implemented on this platform");
|
||||
@@ -598,7 +654,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private static void WinThreadGetClipboard(CmdGetClipboard cmd)
|
||||
{
|
||||
var clipboard = GLFW.GetClipboardString((Window*) cmd.Window);
|
||||
var clipboard = GLFW.GetClipboardString((Window*) cmd.Window) ?? "";
|
||||
// Don't have to care about synchronization I don't think so just fire this immediately.
|
||||
cmd.Tcs.TrySetResult(clipboard);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Graphics;
|
||||
using System.Runtime.Intrinsics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
@@ -100,12 +100,43 @@ namespace Robust.Client.Graphics
|
||||
DrawPrimitives(primitiveTopology, White, indices, drawVertices);
|
||||
}
|
||||
|
||||
private void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> output, Color color)
|
||||
private static void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> output, Color color)
|
||||
{
|
||||
Color colorLinear = Color.FromSrgb(color);
|
||||
for (var i = 0; i < output.Length; i++)
|
||||
if (input.Length == 0)
|
||||
return;
|
||||
|
||||
if (input.Length != output.Length)
|
||||
{
|
||||
output[i] = new DrawVertexUV2DColor(input[i], new Vector2(0.5f, 0.5f), colorLinear);
|
||||
throw new InvalidOperationException("Invalid lengths!");
|
||||
}
|
||||
|
||||
var colorLinear = Color.FromSrgb(color);
|
||||
var colorVec = Unsafe.As<Color, Vector128<float>>(ref colorLinear);
|
||||
var uvVec = Vector128.Create(0, 0, 0.5f, 0.5f);
|
||||
var maskVec = Vector128.Create(0xFFFFFFFF, 0xFFFFFFFF, 0, 0).AsSingle();
|
||||
|
||||
var simdVectors = (nuint)(input.Length / 2);
|
||||
ref readonly var srcBase = ref Unsafe.As<Vector2, float>(ref Unsafe.AsRef(in input[0]));
|
||||
ref var dstBase = ref Unsafe.As<DrawVertexUV2DColor, float>(ref output[0]);
|
||||
|
||||
for (nuint i = 0; i < simdVectors; i++)
|
||||
{
|
||||
var positions = Vector128.LoadUnsafe(in srcBase, i * 4);
|
||||
|
||||
var posColorLower = (positions & maskVec) | uvVec;
|
||||
var posColorUpper = (Vector128.Shuffle(positions, Vector128.Create(2, 3, 0, 0)) & maskVec) | uvVec;
|
||||
|
||||
posColorLower.StoreUnsafe(ref dstBase, i * 16);
|
||||
colorVec.StoreUnsafe(ref dstBase, i * 16 + 4);
|
||||
posColorUpper.StoreUnsafe(ref dstBase, i * 16 + 8);
|
||||
colorVec.StoreUnsafe(ref dstBase, i * 16 + 12);
|
||||
}
|
||||
|
||||
var lastPos = (int)simdVectors * 2;
|
||||
if (lastPos != output.Length)
|
||||
{
|
||||
// Odd number of vertices. Handle the last manually.
|
||||
output[lastPos] = new DrawVertexUV2DColor(input[lastPos], new Vector2(0.5f, 0.5f), colorLinear);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -14,6 +15,79 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simialr to DrawLine but has dashes interspersed.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset from the start of the line.</param>
|
||||
/// <param name="dashSize">How long a dash is.</param>
|
||||
/// <param name="gapSize">How long the gap between dashes is.</param>
|
||||
public void DrawDottedLine(Vector2 from, Vector2 to, Color color, float offset = 0f, float dashSize = 8f, float gapSize = 2f)
|
||||
{
|
||||
var lineVector = to - from;
|
||||
|
||||
// No drawing for you.
|
||||
if (lineVector.LengthSquared() < 10f * float.Epsilon)
|
||||
return;
|
||||
|
||||
var lineAndGap = gapSize + dashSize;
|
||||
var lines = new ValueList<Vector2>();
|
||||
|
||||
// Minimum distance.
|
||||
if (lineVector.Length() < lineAndGap)
|
||||
{
|
||||
lines.Add(from);
|
||||
lines.Add(to);
|
||||
}
|
||||
else
|
||||
{
|
||||
var maxLength = lineVector.Length();
|
||||
var normalizedLine = lineVector.Normalized();
|
||||
var dashVector = normalizedLine * dashSize;
|
||||
var gapVector = normalizedLine * gapSize;
|
||||
|
||||
var position = from;
|
||||
offset %= (dashSize + gapSize);
|
||||
var length = offset;
|
||||
var dashLength = dashSize;
|
||||
|
||||
// If offset is less than gap size then start with a gap
|
||||
// otherwise start with a partial line
|
||||
if (offset > 0f)
|
||||
{
|
||||
if (offset < gapSize)
|
||||
{
|
||||
position += normalizedLine * offset;
|
||||
length += offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
dashLength = (offset - gapSize);
|
||||
}
|
||||
}
|
||||
|
||||
while (length < maxLength)
|
||||
{
|
||||
lines.Add(position);
|
||||
|
||||
position += normalizedLine * dashLength;
|
||||
var lengthFromStart = (position - from).Length();
|
||||
|
||||
// if over length then cap the thing.
|
||||
if (lengthFromStart > maxLength)
|
||||
{
|
||||
position = to;
|
||||
}
|
||||
|
||||
lines.Add(position);
|
||||
dashLength = dashVector.Length();
|
||||
position += gapVector;
|
||||
length = (position - from).Length();
|
||||
}
|
||||
}
|
||||
|
||||
DrawPrimitives(DrawPrimitiveTopology.LineList, lines.Span, color);
|
||||
}
|
||||
|
||||
public abstract void DrawRect(UIBox2 rect, Color color, bool filled = true);
|
||||
|
||||
public abstract void DrawTextureRectRegion(Texture texture, UIBox2 rect, UIBox2? subRegion = null, Color? modulate = null);
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Robust.Client.Graphics
|
||||
/// The render target that is rendered to when rendering this viewport.
|
||||
/// </summary>
|
||||
IRenderTexture RenderTarget { get; }
|
||||
IRenderTexture LightRenderTarget { get; }
|
||||
IEye? Eye { get; set; }
|
||||
Vector2i Size { get; }
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
internal interface IRenderHandle
|
||||
/// <remarks>
|
||||
/// Unstable API. Likely to break hard during renderer rewrite if you rely on it.
|
||||
/// </remarks>
|
||||
public interface IRenderHandle
|
||||
{
|
||||
DrawingHandleScreen DrawingHandleScreen { get; }
|
||||
DrawingHandleWorld DrawingHandleWorld { get; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
@@ -69,7 +69,7 @@ namespace Robust.Client.Graphics
|
||||
ShaderBlendMode? blend = null;
|
||||
if (_rawBlendMode != null)
|
||||
{
|
||||
if (!Enum.TryParse<ShaderBlendMode>(_rawBlendMode.ToUpper(), out var parsed))
|
||||
if (!Enum.TryParse<ShaderBlendMode>(_rawBlendMode, true, out var parsed))
|
||||
Logger.Error($"invalid mode: {_rawBlendMode}");
|
||||
else
|
||||
blend = parsed;
|
||||
|
||||
@@ -59,6 +59,11 @@ namespace Robust.Client.Graphics
|
||||
/// <summary>
|
||||
/// Hide title buttons such as close and minimize.
|
||||
/// </summary>
|
||||
NoTitleOptions = 1 << 0
|
||||
NoTitleOptions = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Completely hide the title bar
|
||||
/// </summary>
|
||||
NoTitleBar = 1 << 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Client.Input
|
||||
void KeyDown(KeyEventArgs e);
|
||||
void KeyUp(KeyEventArgs e);
|
||||
|
||||
IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified=true);
|
||||
IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified=true, bool invalid=false);
|
||||
|
||||
void RemoveBinding(IKeyBinding binding, bool markModified=true);
|
||||
|
||||
|
||||
@@ -124,12 +124,19 @@ namespace Robust.Client.Input
|
||||
var path = new ResPath(KeybindsPath);
|
||||
if (_resourceMan.UserData.Exists(path))
|
||||
{
|
||||
LoadKeyFile(path, true);
|
||||
try
|
||||
{
|
||||
LoadKeyFile(path, false, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("input", "Failed to load user keybindings: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
if (_resourceMan.ContentFileExists(path))
|
||||
{
|
||||
LoadKeyFile(path, false);
|
||||
LoadKeyFile(path, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +496,13 @@ namespace Robust.Client.Input
|
||||
return true;
|
||||
}
|
||||
|
||||
private void LoadKeyFile(ResPath file, bool userData)
|
||||
/// <summary>
|
||||
/// Loads a keybind file, configuring keybinds.
|
||||
/// </summary>
|
||||
/// <param name="file">File to load from the content package</param>
|
||||
/// <param name="defaultRegistration">Whether or not this is a "default" keybind set. If it is, then it won't override the current configuration, only the defaults.</param>
|
||||
/// <param name="userData">Whether or not to load from the user data directory instead of the content package.</param>
|
||||
public void LoadKeyFile(ResPath file, bool defaultRegistration, bool userData = false)
|
||||
{
|
||||
TextReader reader;
|
||||
if (userData)
|
||||
@@ -510,16 +523,19 @@ namespace Robust.Client.Input
|
||||
{
|
||||
var baseKeyRegs = _serialization.Read<KeyBindingRegistration[]>(BaseKeyRegsNode, notNullableOverride: true);
|
||||
|
||||
|
||||
foreach (var reg in baseKeyRegs)
|
||||
{
|
||||
var invalid = false;
|
||||
|
||||
if (reg.Type != KeyBindingType.Command && !NetworkBindMap.FunctionExists(reg.Function.FunctionName))
|
||||
{
|
||||
Logger.ErrorS("input", "Key function in {0} does not exist: '{1}'", file,
|
||||
Logger.DebugS("input", "Key function in {0} does not exist: '{1}'.", file,
|
||||
reg.Function);
|
||||
continue;
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
if (!userData)
|
||||
if (defaultRegistration)
|
||||
{
|
||||
_defaultRegistrations.Add(reg);
|
||||
|
||||
@@ -531,19 +547,24 @@ namespace Robust.Client.Input
|
||||
}
|
||||
}
|
||||
|
||||
RegisterBinding(reg, markModified: userData);
|
||||
RegisterBinding(reg, markModified: !defaultRegistration, invalid);
|
||||
}
|
||||
}
|
||||
|
||||
if (userData && mapping.TryGet("leaveEmpty", out var node))
|
||||
if (!defaultRegistration && mapping.TryGet("leaveEmpty", out var node))
|
||||
{
|
||||
var leaveEmpty = _serialization.Read<BoundKeyFunction[]>(node, notNullableOverride: true);
|
||||
|
||||
if (leaveEmpty.Length > 0)
|
||||
foreach (var bind in leaveEmpty)
|
||||
{
|
||||
// Adding to _modifiedKeyFunctions means that these keybinds won't be loaded from the base file.
|
||||
// Because they've been explicitly cleared.
|
||||
_modifiedKeyFunctions.UnionWith(leaveEmpty);
|
||||
_modifiedKeyFunctions.Add(bind);
|
||||
|
||||
// Adding to bindingsByFunction because if the keybind is not valid(For example if it's from another
|
||||
// server then we will have problems saving the file)
|
||||
_bindingsByFunction.GetOrNew(bind);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -571,7 +592,7 @@ namespace Robust.Client.Input
|
||||
return binding;
|
||||
}
|
||||
|
||||
public IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified = true)
|
||||
public IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified = true, bool invalid = false)
|
||||
{
|
||||
var binding = new KeyBinding(this, reg.Function.FunctionName, reg.Type, reg.BaseKey, reg.CanFocus, reg.CanRepeat,
|
||||
reg.AllowSubCombs, reg.Priority, reg.Mod1, reg.Mod2, reg.Mod3);
|
||||
@@ -602,7 +623,7 @@ namespace Robust.Client.Input
|
||||
|
||||
public void InputModeChanged() => OnInputModeChanged?.Invoke();
|
||||
|
||||
private void RegisterBinding(KeyBinding binding, bool markModified = true)
|
||||
private void RegisterBinding(KeyBinding binding, bool markModified = true, bool invalid = false)
|
||||
{
|
||||
// we sort larger combos first so they take priority over smaller (single key) combos,
|
||||
// so they get processed first in KeyDown and such.
|
||||
@@ -617,7 +638,8 @@ namespace Robust.Client.Input
|
||||
_modifiedKeyFunctions.Add(binding.Function);
|
||||
}
|
||||
|
||||
_bindings.Insert(pos, binding);
|
||||
if (!invalid)
|
||||
_bindings.Insert(pos, binding);
|
||||
_bindingsByFunction.GetOrNew(binding.Function).Add(binding);
|
||||
OnKeyBindingAdded?.Invoke(binding);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ public sealed class TileEdgeOverlay : GridOverlay
|
||||
var tileDimensions = new Vector2(tileSize, tileSize);
|
||||
var (_, _, worldMatrix, invMatrix) = xformSystem.GetWorldPositionRotationMatrixWithInv(Grid.Owner);
|
||||
args.WorldHandle.SetTransform(worldMatrix);
|
||||
var localAABB = invMatrix.TransformBox(args.WorldBounds);
|
||||
var bounds = args.WorldBounds;
|
||||
bounds = new Box2Rotated(bounds.Box.Enlarged(1), bounds.Rotation, bounds.Origin);
|
||||
var localAABB = invMatrix.TransformBox(bounds);
|
||||
|
||||
var enumerator = mapSystem.GetLocalTilesEnumerator(Grid.Owner, Grid, localAABB, false);
|
||||
|
||||
|
||||
@@ -207,8 +207,8 @@ public sealed partial class PhysicsSystem
|
||||
var contact = contacts[i];
|
||||
var uidA = contact.EntityA;
|
||||
var uidB = contact.EntityB;
|
||||
var bodyATransform = GetPhysicsTransform(uidA, xformQuery.GetComponent(uidA), xformQuery);
|
||||
var bodyBTransform = GetPhysicsTransform(uidB, xformQuery.GetComponent(uidB), xformQuery);
|
||||
var bodyATransform = GetPhysicsTransform(uidA, xformQuery.GetComponent(uidA));
|
||||
var bodyBTransform = GetPhysicsTransform(uidB, xformQuery.GetComponent(uidB));
|
||||
contact.UpdateIsTouching(bodyATransform, bodyBTransform);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.Placement.Modes
|
||||
|
||||
var closestEntity = snapToEntities[0];
|
||||
var closestTransform = pManager.EntityManager.GetComponent<TransformComponent>(closestEntity);
|
||||
if (!pManager.EntityManager.TryGetComponent<SpriteComponent?>(closestEntity, out var component) || component.BaseRSI == null)
|
||||
if (!pManager.EntityManager.TryGetComponent(closestEntity, out SpriteComponent? component) || component.BaseRSI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
// Try to get current map.
|
||||
var map = MapId.Nullspace;
|
||||
if (EntityManager.TryGetComponent(PlayerManager.LocalPlayer?.ControlledEntity, out TransformComponent? xform))
|
||||
if (EntityManager.TryGetComponent(PlayerManager.LocalEntity, out TransformComponent? xform))
|
||||
{
|
||||
map = xform.MapID;
|
||||
}
|
||||
@@ -512,7 +512,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
private bool CurrentEraserMouseCoordinates(out EntityCoordinates coordinates)
|
||||
{
|
||||
var ent = PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
var ent = PlayerManager.LocalEntity ?? EntityUid.Invalid;
|
||||
if (ent == EntityUid.Invalid)
|
||||
{
|
||||
coordinates = new EntityCoordinates();
|
||||
@@ -640,7 +640,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
if (CurrentPermission is not {Range: > 0} ||
|
||||
!CurrentMode.RangeRequired ||
|
||||
PlayerManager.LocalPlayer?.ControlledEntity is not {Valid: true} controlled)
|
||||
PlayerManager.LocalEntity is not {Valid: true} controlled)
|
||||
return;
|
||||
|
||||
var worldPos = EntityManager.GetComponent<TransformComponent>(controlled).WorldPosition;
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
if (!RangeRequired)
|
||||
return true;
|
||||
var controlled = pManager.PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
var controlled = pManager.PlayerManager.LocalEntity ?? EntityUid.Invalid;
|
||||
if (controlled == EntityUid.Invalid)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -10,14 +10,13 @@ namespace Robust.Client.Player
|
||||
|
||||
public override Filter FromEntities(Filter filter, params EntityUid[] entities)
|
||||
{
|
||||
if (_playerManager.LocalPlayer is not { } localPlayer
|
||||
|| localPlayer.Session.AttachedEntity is not {Valid: true} attachedUid)
|
||||
if (_playerManager.LocalEntity is not {Valid: true} attachedUid)
|
||||
return filter;
|
||||
|
||||
foreach (var uid in entities)
|
||||
{
|
||||
if (uid == attachedUid)
|
||||
filter.AddPlayer(localPlayer.Session);
|
||||
filter.AddPlayer(_playerManager.LocalSession!);
|
||||
}
|
||||
|
||||
return filter;
|
||||
|
||||
@@ -162,11 +162,14 @@ namespace Robust.Client.Player
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!EntManager.EnsureComponent(uid.Value, out EyeComponent eye))
|
||||
if (!EntManager.HasComponent<EyeComponent>(uid.Value))
|
||||
{
|
||||
if (_client.RunLevel != ClientRunLevel.SinglePlayerGame)
|
||||
Sawmill.Warning($"Attaching local player to an entity {EntManager.ToPrettyString(uid)} without an eye. This eye will not be netsynced and may cause issues.");
|
||||
var eye = (EyeComponent) Factory.GetComponent(typeof(EyeComponent));
|
||||
eye.Owner = uid.Value;
|
||||
eye.NetSyncEnabled = false;
|
||||
EntManager.AddComponent(uid.Value, eye);
|
||||
}
|
||||
|
||||
Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(uid)}.");
|
||||
|
||||
@@ -36,12 +36,12 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
|
||||
private void OnRecordingStarted(MappingDataNode metadata, List<object> messages)
|
||||
{
|
||||
if (_player.LocalPlayer == null)
|
||||
if (_player.LocalSession == null)
|
||||
return;
|
||||
|
||||
// Add information about the user doing the recording. This is used to set the default replay observer position
|
||||
// when playing back the replay.
|
||||
var guid = _player.LocalPlayer.UserId.UserId.ToString();
|
||||
var guid = _player.LocalUser.ToString();
|
||||
metadata[ReplayConstants.MetaKeyRecordedBy] = new ValueDataNode(guid);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,32 +5,33 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
<NoWarn>NU1701;CA1416</NoWarn>
|
||||
<OutputPath>../bin/Client</OutputPath>
|
||||
<RobustILLink>true</RobustILLink>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" PrivateAssets="compile" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Robust.Natives" Version="0.1.1" />
|
||||
<PackageReference Include="System.Numerics.Vectors" Version="4.4.0" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
|
||||
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0" />
|
||||
<PackageReference Include="DiscordRichPresence" PrivateAssets="compile" />
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.OpenAL" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" PrivateAssets="compile" />
|
||||
<PackageReference Include="Robust.Natives" />
|
||||
<PackageReference Include="System.Numerics.Vectors" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" PrivateAssets="compile" />
|
||||
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.Sodium" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" />
|
||||
<PackageReference Include="TerraFX.Interop.Xlib" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(EnableClientScripting)' == 'True'">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" PrivateAssets="compile" />
|
||||
|
||||
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -39,7 +40,6 @@
|
||||
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\OpenToolkit.GraphicsLibraryFramework\OpenToolkit.GraphicsLibraryFramework.csproj" />
|
||||
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj" />
|
||||
<ProjectReference Include="..\Robust.Physics\Robust.Physics.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -54,6 +54,7 @@
|
||||
<RobustLinkRoots Include="Robust.Client" />
|
||||
<RobustLinkRoots Include="Robust.Shared" />
|
||||
<RobustLinkAssemblies Include="TerraFX.Interop.Windows" />
|
||||
<RobustLinkAssemblies Include="TerraFX.Interop.Xlib" />
|
||||
<RobustLinkAssemblies Include="OpenToolkit.Graphics" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -11,15 +11,17 @@ namespace Robust.Client.Upload.Commands;
|
||||
|
||||
public sealed class UploadFileCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly IFileDialogManager _dialog = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
|
||||
public string Command => "uploadfile";
|
||||
public string Description => "Uploads a resource to the server.";
|
||||
public string Help => $"{Command} [relative path for the resource]";
|
||||
|
||||
public async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var cfgMan = IoCManager.Resolve<IConfigurationManager>();
|
||||
|
||||
if (!cfgMan.GetCVar(CVars.ResourceUploadingEnabled))
|
||||
if (!_cfgManager.GetCVar(CVars.ResourceUploadingEnabled))
|
||||
{
|
||||
shell.WriteError("Network Resource Uploading is currently disabled by the server.");
|
||||
return;
|
||||
@@ -33,10 +35,8 @@ public sealed class UploadFileCommand : IConsoleCommand
|
||||
|
||||
var path = new ResPath(args[0]).ToRelativePath();
|
||||
|
||||
var dialog = IoCManager.Resolve<IFileDialogManager>();
|
||||
|
||||
var filters = new FileDialogFilters(new FileDialogFilters.Group(path.Extension));
|
||||
await using var file = await dialog.OpenFile(filters);
|
||||
await using var file = await _dialog.OpenFile(filters);
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
@@ -44,7 +44,7 @@ public sealed class UploadFileCommand : IConsoleCommand
|
||||
return;
|
||||
}
|
||||
|
||||
var sizeLimit = cfgMan.GetCVar(CVars.ResourceUploadingLimitMb);
|
||||
var sizeLimit = _cfgManager.GetCVar(CVars.ResourceUploadingLimitMb);
|
||||
|
||||
if (sizeLimit > 0f && file.Length * SharedNetworkResourceManager.BytesToMegabytes > sizeLimit)
|
||||
{
|
||||
@@ -54,12 +54,12 @@ public sealed class UploadFileCommand : IConsoleCommand
|
||||
|
||||
var data = file.CopyToArray();
|
||||
|
||||
var netManager = IoCManager.Resolve<INetManager>();
|
||||
var msg = netManager.CreateNetMessage<NetworkResourceUploadMessage>();
|
||||
var msg = new NetworkResourceUploadMessage
|
||||
{
|
||||
RelativePath = path,
|
||||
Data = data
|
||||
};
|
||||
|
||||
msg.RelativePath = path;
|
||||
msg.Data = data;
|
||||
|
||||
netManager.ClientSendMessage(msg);
|
||||
_netManager.ClientSendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
@@ -13,21 +14,20 @@ namespace Robust.Client.Upload.Commands;
|
||||
|
||||
public sealed class UploadFolderCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private IResourceManager _resourceManager = default!;
|
||||
[Dependency] private IConfigurationManager _configManager = default!;
|
||||
[Dependency] private INetManager _netMan = default!;
|
||||
|
||||
public string Command => "uploadfolder";
|
||||
public string Description => Loc.GetString("uploadfolder-command-description");
|
||||
public string Help => Loc.GetString("uploadfolder-command-help");
|
||||
|
||||
private static readonly ResPath BaseUploadFolderPath = new("/UploadFolder");
|
||||
|
||||
[Dependency] private IResourceManager _resourceManager = default!;
|
||||
[Dependency] private IConfigurationManager _configManager = default!;
|
||||
[Dependency] private INetManager _netMan = default!;
|
||||
|
||||
public async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var fileCount = 0;
|
||||
|
||||
|
||||
if (!_configManager.GetCVar(CVars.ResourceUploadingEnabled))
|
||||
{
|
||||
shell.WriteError( Loc.GetString("uploadfolder-command-resource-upload-disabled"));
|
||||
@@ -52,8 +52,7 @@ public sealed class UploadFolderCommand : IConsoleCommand
|
||||
//Grab all files in specified folder and upload them
|
||||
foreach (var filepath in _resourceManager.UserData.Find($"{folderPath.ToRelativePath()}/").files )
|
||||
{
|
||||
|
||||
await using var filestream = _resourceManager.UserData.Open(filepath,FileMode.Open);
|
||||
await using var filestream = _resourceManager.UserData.Open(filepath, FileMode.Open);
|
||||
{
|
||||
var sizeLimit = _configManager.GetCVar(CVars.ResourceUploadingLimitMb);
|
||||
if (sizeLimit > 0f && filestream.Length * SharedNetworkResourceManager.BytesToMegabytes > sizeLimit)
|
||||
@@ -64,9 +63,11 @@ public sealed class UploadFolderCommand : IConsoleCommand
|
||||
|
||||
var data = filestream.CopyToArray();
|
||||
|
||||
var msg = _netMan.CreateNetMessage<NetworkResourceUploadMessage>();
|
||||
msg.RelativePath = filepath.RelativeTo(BaseUploadFolderPath);
|
||||
msg.Data = data;
|
||||
var msg = new NetworkResourceUploadMessage
|
||||
{
|
||||
RelativePath = filepath.RelativeTo(BaseUploadFolderPath),
|
||||
Data = data
|
||||
};
|
||||
|
||||
_netMan.ClientSendMessage(msg);
|
||||
fileCount++;
|
||||
|
||||
@@ -226,6 +226,10 @@ namespace Robust.Client.UserInterface
|
||||
/// <seealso cref="Rect"/>
|
||||
public UIBox2i PixelRect => UIBox2i.FromDimensions(PixelPosition, PixelSize);
|
||||
|
||||
public UIBox2 GlobalRect => UIBox2.FromDimensions(GlobalPosition, _size);
|
||||
|
||||
public UIBox2i GlobalPixelRect => UIBox2i.FromDimensions(GlobalPixelPosition, PixelSize);
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal alignment mode.
|
||||
/// This determines how the control should be laid out horizontally
|
||||
@@ -464,6 +468,14 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screen coordinates position relative to the control.
|
||||
/// </summary>
|
||||
public Vector2 GetLocalPosition(ScreenCoordinates coordinates)
|
||||
{
|
||||
return coordinates.Position - GlobalPixelPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notify the layout system that this control's <see cref="Measure"/> result may have changed
|
||||
/// and must be recalculated.
|
||||
|
||||
@@ -24,7 +24,10 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public ICollection<string> StyleClasses { get; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public IReadOnlyCollection<string> StylePseudoClass => _stylePseudoClass;
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -545,6 +545,36 @@ namespace Robust.Client.UserInterface
|
||||
Draw(renderHandle.DrawingHandleScreen);
|
||||
}
|
||||
|
||||
protected internal virtual void PreRenderChildren(ref ControlRenderArguments args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected internal virtual void PostRenderChildren(ref ControlRenderArguments args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected internal virtual void RenderChildOverride(ref ControlRenderArguments args, int childIndex, Vector2i position)
|
||||
{
|
||||
RenderControl(ref args, childIndex, position);
|
||||
}
|
||||
|
||||
public ref struct ControlRenderArguments
|
||||
{
|
||||
public IRenderHandle Handle;
|
||||
public ref int Total;
|
||||
public Vector2i Position;
|
||||
public Color Modulate;
|
||||
public UIBox2i? ScissorBox;
|
||||
public ref Matrix3 CoordinateTransform;
|
||||
}
|
||||
|
||||
protected void RenderControl(ref ControlRenderArguments args, int childIndex, Vector2i position)
|
||||
{
|
||||
UserInterfaceManagerInternal.RenderControl(args.Handle, ref args.Total, GetChild(childIndex), position, args.Modulate, args.ScissorBox, args.CoordinateTransform);
|
||||
}
|
||||
|
||||
public void UpdateDraw()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
@@ -32,8 +38,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
get => _group;
|
||||
set
|
||||
{
|
||||
if (value?.InternalButtons.Contains(this) ?? false)
|
||||
return; // No work to do.
|
||||
// Remove from old group.
|
||||
_group?.Buttons.Remove(this);
|
||||
_group?.InternalButtons.Remove(this);
|
||||
|
||||
_group = value;
|
||||
|
||||
@@ -42,11 +50,21 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
|
||||
value.Buttons.Add(this);
|
||||
value.InternalButtons.Add(this);
|
||||
ToggleMode = true;
|
||||
|
||||
// Set us to pressed if we're the first button.
|
||||
Pressed = value.Buttons.Count == 0;
|
||||
if (value.IsNoneSetAllowed)
|
||||
{
|
||||
// Still UNPRESS if there's another pressed button, but don't PRESS it otherwise.
|
||||
if (value.Pressed != this)
|
||||
_pressed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set us to pressed if we're the first button. Doesn't go through the setter to avoid setting off our own error check.
|
||||
_pressed = value.InternalButtons.Count == 1;
|
||||
}
|
||||
DrawModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +108,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value && Group != null)
|
||||
if (!value && Group is { IsNoneSetAllowed: false })
|
||||
{
|
||||
throw new InvalidOperationException("Cannot directly unset a grouped button. Set another button in the group instead.");
|
||||
}
|
||||
@@ -106,6 +124,20 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the button's press state and also handles click sounds.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void SetClickPressed(bool value)
|
||||
{
|
||||
Pressed = value;
|
||||
|
||||
if (Pressed != value)
|
||||
return;
|
||||
|
||||
UserInterfaceManager.ClickSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether key functions other than <see cref="EngineKeyFunctions.UIClick"/> trigger the button.
|
||||
/// </summary>
|
||||
@@ -221,7 +253,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
// Can't un press a radio button directly.
|
||||
if (Group == null || !Pressed)
|
||||
{
|
||||
Pressed = !Pressed;
|
||||
SetClickPressed(!Pressed);
|
||||
OnPressed?.Invoke(buttonEventArgs);
|
||||
OnToggled?.Invoke(new ButtonToggledEventArgs(Pressed, this, args));
|
||||
UnsetOtherGroupButtons();
|
||||
@@ -262,7 +294,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.UIClick && ToggleMode && _attemptingPress == 1)
|
||||
{
|
||||
Pressed = !Pressed;
|
||||
SetClickPressed(!Pressed);
|
||||
}
|
||||
else
|
||||
{
|
||||
UserInterfaceManager.ClickSound();
|
||||
}
|
||||
|
||||
OnPressed?.Invoke(buttonEventArgs);
|
||||
@@ -303,7 +339,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var button in _group.Buttons)
|
||||
foreach (var button in _group.InternalButtons)
|
||||
{
|
||||
if (button != this && button.Pressed)
|
||||
{
|
||||
@@ -317,6 +353,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
base.MouseEntered();
|
||||
|
||||
if (!Disabled)
|
||||
{
|
||||
UserInterfaceManager.HoverSound();
|
||||
}
|
||||
|
||||
var drawMode = DrawMode;
|
||||
_beingHovered = true;
|
||||
if (drawMode != DrawMode)
|
||||
@@ -412,6 +453,29 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </remarks>
|
||||
public sealed class ButtonGroup
|
||||
{
|
||||
internal readonly List<BaseButton> Buttons = new();
|
||||
/// <summary>
|
||||
/// Whether it is legal for this button group to have no selected button.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If true, it's legal for no button in the group to be active.
|
||||
/// This is then the initial state of a new group of buttons (no button is automatically selected),
|
||||
/// and it becomes legal to manually clear the active button through code.
|
||||
/// The user cannot manually unselect the active button regardless, only by selecting a difference button.
|
||||
/// </remarks>
|
||||
public bool IsNoneSetAllowed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ButtonGroup"/>
|
||||
/// </summary>
|
||||
/// <param name="isNoneSetAllowed">The value of <see cref="IsNoneSetAllowed"/> on the new button group.</param>
|
||||
public ButtonGroup(bool isNoneSetAllowed = true)
|
||||
{
|
||||
IsNoneSetAllowed = isNoneSetAllowed;
|
||||
}
|
||||
|
||||
internal readonly List<BaseButton> InternalButtons = new();
|
||||
public IReadOnlyList<BaseButton> Buttons => InternalButtons;
|
||||
|
||||
public BaseButton? Pressed => InternalButtons.FirstOrDefault(x => x.Pressed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
@@ -9,6 +9,9 @@ using Robust.Shared.Input;
|
||||
using Robust.Shared.Maths;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scrollable list of items in a user interface.
|
||||
/// </summary>
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
[Virtual]
|
||||
@@ -29,6 +32,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public const string StylePropertySelectedItemBackground = "selected-item-background";
|
||||
public const string StylePropertyDisabledItemBackground = "disabled-item-background";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ItemSeparation of individual list items
|
||||
/// </summary>
|
||||
public int ItemSeparation { get; set; } = 0; // Default value is 0px
|
||||
public int Count => _itemList.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
@@ -68,8 +75,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
itemHeight += ActualItemBackground.MinimumSize.Y * UIScale;
|
||||
|
||||
_totalContentHeight += (int)Math.Ceiling(itemHeight);
|
||||
_totalContentHeight += ItemSeparation;
|
||||
}
|
||||
|
||||
//Remove unneeded ItemSeparation on last item.
|
||||
_totalContentHeight -= ItemSeparation;
|
||||
_scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight);
|
||||
_updateScrollbarVisibility();
|
||||
}
|
||||
@@ -390,6 +399,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
|
||||
offset += itemHeight;
|
||||
|
||||
// Add a ItemSeparation at the bottom of each item.
|
||||
offset += ItemSeparation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +411,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
var color = ActualFontColor;
|
||||
var offsetY = (int) (box.Height - font.GetHeight(UIScale)) / 2;
|
||||
var baseLine = new Vector2i(0, offsetY + font.GetAscent(UIScale)) + box.TopLeft;
|
||||
var baseLine = new Vector2i(5, offsetY + font.GetAscent(UIScale)) + box.TopLeft;
|
||||
|
||||
foreach (var rune in text.EnumerateRunes())
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
[Virtual]
|
||||
public class MenuBar : PanelContainer
|
||||
{
|
||||
public const string StyleClassMenuBarPopup = "menuBarPopup";
|
||||
private readonly List<Menu> _menus = new();
|
||||
private readonly List<MenuTopButton> _buttons = new();
|
||||
private readonly BoxContainer _hBox;
|
||||
@@ -40,7 +41,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = new Vector2(300, 0)
|
||||
})
|
||||
}
|
||||
},
|
||||
StyleClasses = { StyleClassMenuBarPopup }
|
||||
};
|
||||
_popup.OnPopupHide += PopupHidden;
|
||||
UserInterfaceManager.ModalRoot.AddChild(_popup);
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public class OptionButton : ContainerButton
|
||||
{
|
||||
public const string StyleClassOptionButton = "optionButton";
|
||||
public const string StyleClassPopup = "optionButtonPopup";
|
||||
public const string StyleClassOptionTriangle = "optionTriangle";
|
||||
public readonly ScrollContainer OptionsScroll;
|
||||
|
||||
@@ -74,7 +75,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
_popup = new Popup()
|
||||
{
|
||||
Children = { OptionsScroll }
|
||||
Children = { new PanelContainer(), OptionsScroll },
|
||||
StyleClasses = { StyleClassPopup }
|
||||
};
|
||||
_popup.OnPopupHide += OnPopupHide;
|
||||
|
||||
@@ -99,6 +101,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
AddItem(label, id);
|
||||
}
|
||||
|
||||
public virtual void ButtonOverride(Button button)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddItem(string label, int? id = null)
|
||||
{
|
||||
if (id == null)
|
||||
@@ -132,6 +139,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
Select(0);
|
||||
}
|
||||
|
||||
ButtonOverride(button);
|
||||
}
|
||||
|
||||
private void TogglePopup(bool show)
|
||||
@@ -139,6 +148,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
if (show)
|
||||
{
|
||||
var globalPos = GlobalPosition;
|
||||
globalPos.Y += Size.Y + 1; // Place it below us, with a safety margin.
|
||||
globalPos.Y -= Margin.SumVertical;
|
||||
OptionsScroll.Measure(Window?.Size ?? Vector2Helpers.Infinity);
|
||||
var (minX, minY) = OptionsScroll.DesiredSize;
|
||||
var box = UIBox2.FromDimensions(globalPos, new Vector2(Math.Max(minX, Width), minY));
|
||||
|
||||
@@ -15,13 +15,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
base.Draw(handle);
|
||||
|
||||
var style = _getStyleBox();
|
||||
var style = GetStyleBox();
|
||||
style?.Draw(handle, PixelSizeBox, UIScale);
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
var styleSize = _getStyleBox()?.MinimumSize ?? Vector2.Zero;
|
||||
var styleSize = GetStyleBox()?.MinimumSize ?? Vector2.Zero;
|
||||
var measureSize = Vector2.Max(availableSize - styleSize, Vector2.Zero);
|
||||
var childSize = Vector2.Zero;
|
||||
foreach (var child in Children)
|
||||
@@ -36,7 +36,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
var ourSize = UIBox2.FromDimensions(Vector2.Zero, finalSize);
|
||||
var contentBox = _getStyleBox()?.GetContentBox(ourSize, 1) ?? ourSize;
|
||||
var contentBox = GetStyleBox()?.GetContentBox(ourSize, 1) ?? ourSize;
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
@@ -47,7 +47,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
|
||||
[System.Diagnostics.Contracts.Pure]
|
||||
private StyleBox? _getStyleBox()
|
||||
protected StyleBox? GetStyleBox()
|
||||
{
|
||||
if (PanelOverride != null)
|
||||
{
|
||||
|
||||
@@ -90,6 +90,8 @@ public sealed class TextEdit : Control
|
||||
internal bool DebugOverlay;
|
||||
private Vector2? _lastDebugMousePos;
|
||||
|
||||
public event Action<TextEditEventArgs>? OnTextChanged;
|
||||
|
||||
public TextEdit()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -315,7 +317,7 @@ public sealed class TextEdit : Control
|
||||
if (changed)
|
||||
{
|
||||
_selectionStart = _cursorPosition;
|
||||
// OnTextChanged?.Invoke(new LineEditEventArgs(this, _text));
|
||||
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
|
||||
// _updatePseudoClass();
|
||||
// OnBackspace?.Invoke(new LineEditBackspaceEventArgs(oldText, _text, cursor, selectStart));
|
||||
}
|
||||
@@ -349,7 +351,7 @@ public sealed class TextEdit : Control
|
||||
if (changed)
|
||||
{
|
||||
_selectionStart = _cursorPosition;
|
||||
// OnTextChanged?.Invoke(new LineEditEventArgs(this, _text));
|
||||
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
|
||||
// _updatePseudoClass();
|
||||
}
|
||||
|
||||
@@ -382,7 +384,10 @@ public sealed class TextEdit : Control
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
_selectionStart = _cursorPosition;
|
||||
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
|
||||
}
|
||||
|
||||
InvalidateHorizontalCursorPos();
|
||||
args.Handle();
|
||||
@@ -411,7 +416,10 @@ public sealed class TextEdit : Control
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
_selectionStart = _cursorPosition;
|
||||
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
|
||||
}
|
||||
|
||||
InvalidateHorizontalCursorPos();
|
||||
args.Handle();
|
||||
@@ -748,6 +756,7 @@ public sealed class TextEdit : Control
|
||||
|
||||
var startPos = _cursorPosition;
|
||||
TextRope = Rope.Insert(TextRope, startPos.Index, ev.Text);
|
||||
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
|
||||
|
||||
_selectionStart = _cursorPosition = new CursorPos(startPos.Index + startChars, LineBreakBias.Top);
|
||||
_imeData = (startPos, ev.Text.Length);
|
||||
@@ -844,6 +853,7 @@ public sealed class TextEdit : Control
|
||||
var upper = SelectionUpper.Index;
|
||||
|
||||
TextRope = Rope.ReplaceSubstring(TextRope, lower, upper - lower, text);
|
||||
OnTextChanged?.Invoke(new TextEditEventArgs(this, _textRope));
|
||||
|
||||
_selectionStart = _cursorPosition = new CursorPos(lower + text.Length, LineBreakBias.Top);
|
||||
// OnTextChanged?.Invoke(new LineEditEventArgs(this, _text));
|
||||
@@ -1441,6 +1451,12 @@ public sealed class TextEdit : Control
|
||||
AbortIme(delete: false);
|
||||
}
|
||||
|
||||
public sealed class TextEditEventArgs(TextEdit control, Rope.Node textRope) : EventArgs
|
||||
{
|
||||
public TextEdit Control { get; } = control;
|
||||
public Rope.Node TextRope { get; } = textRope;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies which line the cursor is positioned at when on a word-wrapping break.
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user