Compare commits

..

2 Commits

Author SHA1 Message Date
DrSmugleaf
318548a4d6 Make setter internal 2024-05-07 20:51:01 -07:00
DrSmugleaf
dde4dd06f6 Fix never setting BoundUserInterface.State 2024-05-07 19:15:48 -07:00
299 changed files with 3843 additions and 8162 deletions

View File

@@ -7,18 +7,6 @@ indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
max_line_length = 120
# ReSharper properties
resharper_csharp_max_line_length = 120
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_keep_existing_attribute_arrangement = true
resharper_place_field_attribute_on_same_line = if_owner_is_single_line
resharper_wrap_chained_binary_patterns = chop_if_long
resharper_wrap_chained_method_calls = chop_if_long
[*.{csproj,xml,yml,dll.config,targets,props}]
indent_size = 2

View File

@@ -33,10 +33,10 @@ jobs:
mkdir "release/${{ steps.parse_version.outputs.version }}"
mv release/*.zip "release/${{ steps.parse_version.outputs.version }}"
- name: Upload files to Suns
- name: Upload files to centcomm
uses: appleboy/scp-action@master
with:
host: suns.spacestation14.com
host: centcomm.spacestation14.io
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
source: "release/${{ steps.parse_version.outputs.version }}"
@@ -46,7 +46,7 @@ jobs:
- name: Update manifest JSON
uses: appleboy/ssh-action@master
with:
host: suns.spacestation14.com
host: centcomm.spacestation14.io
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
script: /home/robust-build-push/push.ps1 ${{ steps.parse_version.outputs.version }}

View File

@@ -1,24 +1,14 @@
<Project>
<PropertyGroup>
<!--
We actually set ManagePackageVersionsCentrally manually in another import file.
Since .NET SDK 8.0.300, ManagePackageVersionsCentrally is automatically set if Directory.Packages.props exists.
https://github.com/NuGet/NuGet.Client/pull/5572
We actively negate this here, as we have some packages in tree we don't want such automatic behavior for.
We use Directory.Build.props to get copy the state *after* our MSBuild config but before Nuget's config.
-->
<ManagePackageVersionsCentrally />
</PropertyGroup>
<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.8.1" />
<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.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" />
@@ -43,7 +33,7 @@
<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="8.0.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" />
@@ -55,8 +45,8 @@
<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.5" />
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
<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" />

View File

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

View File

@@ -54,351 +54,6 @@ END TEMPLATE-->
*None yet*
## 227.0.2
## 227.0.1
## 227.0.0
### Breaking changes
* Add a `loop` arg to SpriteSystem.GetFrame in case you don't want to get a looping animation.
* Remove obsolete VisibileSystem methods.
### New features
* Added `LocalizedEntityCommands`, which are console commands that have the ability to take entity system dependencies.
* Added `BeginRegistrationRegion` to `IConsoleHost` to allow efficient bulk-registration of console commands.
* Added `IConsoleHost.RegisterCommand` overload that takes an `IConsoleCommand`.
* Added a `Finished` boolean to `AnimationCompletedEvent` which allows distinguishing if an animation was removed prematurely or completed naturally.
* Add GetLocalTilesIntersecting for MapSystem.
* Add an analyzer for methods that should call the base implementation and use it for EntitySystems.
### Bugfixes
* Fix loading replays if string package is compressed inside a zip.
### Other
* Tab completions containing spaces are now properly quoted, so the command will actually work properly once entered.
* Mark EntityCoordinates.Offset as Pure so it shows as warnings if the variable is unused.
* Networked events will always be processed in order even if late.
## 226.3.0
### New features
* `System.Collections.IList` and `System.Collections.ICollection` are now sandbox safe, this fixes some collection expression cases.
* The sandboxing system will now report the methods responsible for references to illegal items.
## 226.2.0
### New features
* `Control.VisibilityChanged()` virtual function.
* Add some System.Random methods for NextFloat and NextPolarVector2.
### Bugfixes
* Fixes ContainerSystem failing client-side debug asserts when an entity gets unanchored & inserted into a container on the same tick.
* Remove potential race condition on server startup from invoking ThreadPool.SetMinThreads.
### Other
* Increase default value of res.rsi_atlas_size.
* Fix internal networking logic.
* Updates of `OutputPanel` contents caused by change in UI scale are now deferred until visible. Especially important to avoid updates from debug console.
* Debug console is now limited to only keep `con.max_entries` entries.
* Non-existent resources are cached by `IResourceCache.TryGetResource`. This avoids the game constantly trying to re-load non-existent resources in common patterns such as UI theme texture fallbacks.
* Default IPv4 MTU has been lowered to 700.
* Update Robust.LoaderApi.
### Internal
* Split out PVS serialization from compression and sending game states.
* Turn broadphase contacts into an IParallelRobustJob and remove unnecessary GetMapEntityIds for every contact.
## 226.1.0
### New features
* Add some GetLocalEntitiesIntersecting methods for `Entity<T>`.
### Other
* Fix internal networking logic
## 226.0.0
### Breaking changes
* `IEventBus.RaiseComponentEvent` now requires an EntityUid argument.
* The `AddedComponentEventArgs` and `RemovedComponentEventArgs` constructors are now internal
### New features
* Allow RequestScreenTexture to be set in overlays.
### Bugfixes
* Fix AnimationCompletedEvent not always going out.
## 225.0.0
### Breaking changes
* `NetEntity.Parse` and `TryParse` will now fail to parse empty strings.
* Try to prevent EventBus looping. This also caps the amount of directed component subscriptions for a particular component to 256.
### New features
* `IPrototypeManager.TryIndex` will now default to logging errors if passed an invalid prototype id struct (i,e., `EntProtoId` or `ProtoId<T>`). There is a new optional bool argument to disable logging errors.
* `Eye` now allows its `Position` to be set directly. Please only do this with the `FixedEye` child type constructed manually.
* Engine now respects the hub's `can_skip_build` parameter on info query, fixing an issue where the first hub advertisement fails due to ACZ taking too long.
* Add GetSession & TryGetSession to ActorSystem.
* Raise an event when an entity's name is changed.
### Bugfixes
* The `ent` toolshed command now takes `NetEntity` values, fixing parsing in practical uses.
* Fix ComponentFactory test mocks.
* Fix LookupFlags missing from a couple of EntityLookupSystem methods.
### Other
* Improved engine's Happy Eyeballs implementation, should result in more usage of IPv6 for HTTP APIs when available.
* Remove CompIdx locks to improve performance inside Pvs at higher player counts.
* Avoid a read lock in GetEntityQuery to also improve performance.
* Mark `EntityManager.System<T>` as Pure.
## 224.1.1
### Bugfixes
* Fixed UserInterfaceSystem sometimes throwing a key-not-found exception when trying to close UIs.
## 224.1.0
### New features
* `ServerIntegrationInstance` has new methods for adding dummy player sessions for tests that require multiple players.
* Linguini has been updated to v0.8.1. Errors will now be logged when a duplicate localization key is found.
* Added `UserInterfaceSystem.SetUi()` for modifying the `InterfaceData` associated with some BUI.
* Added the `EntityPrototypeView` control for spawning & rendering an entity prototype.
### Bugfixes
* Fix `UserInterfaceSystem` spamming client side errors when entities with UIs open are deleted while outside of PVS range.
* Fix Toolshed's EnumTypeParse not working enum values with upercase characters.
* Fixed `incmd` command not working due to an invalid cast.
### Other
* There have been various performance improvements to replay loading & playback.
### Internal
* Added `DummySession` and `DummyChannel` classes for use in integration tests and benchmarks to fool the server into thinking that there are multiple players connected.
* Added `ICommonSessionInternal` and updated `CommonSession` so that the internal setters now go through that interface.
## 224.0.1
### Bugfixes
* Fixes PVS throwing exceptions when invalid entities are passed to `ExpandPvsEvent`. Now it just logs an error.
* Fixes BUIs not properly closing, resulting in invalid entities in `UserInterfaceUserComponent.OpenInterfaces`
* Fixes an unknown/invalid prototype exception sometimes being thrown when running ``IPrototypeManager.ResolveResults()`
## 224.0.0
### Breaking changes
* `Matrix3` has been replaced with `System.Numerics.Matrix3x2`. Various Matrix related methods have been turned into extension methods in the `Matrix3Helpers` class.
* Engine `EntityCategory` prototype IDs have been changed to use CamelCase. I.e., `hideSpawnMenu` -> `HideSpawnMenu`
* Prototypes can now be implicitly cast `ProtoId<T>` or `EntProtoId` ID structs. The new implicit cast might cause previous function calls to be ambiguous.
### New features
* `Array.Clear(Array)` is now available in the sandbox.
* BUIs now use `ExpandPvsEvent`. I.e., if a player has a UI open, then the entity associated with that UI will always get sent to the player by the PVS system.
* Added `cvar_subs` command for listing all subscribers to cvar changes
* Entity categories have been reworked
* Each category now has a `HideSpawnMenu` field. The old `HideSpawnMenu` category is now just a normal category with that field set to true.
* Reworked category inheritance. Inheritance can now be disabled per category using a `Inheritable` field.
* Entity prototypes can now be automatically added to categories based on the components that they have, either by specifying components when defining the category in yml, or by adding the EntityCategoryAttribute to the component class.
### Bugfixes
* Fixed client-side BUI error log spam if an unknown entity has a UI open.
* Fixed placement manager spawning entities with incorrect rotations.
### Other
* Added a try-catch block to BUI constructors, to avoid clients getting stuck in error loops while applying states.
* Attempting to play sounds on terminating entities no longer logs an error.
## 223.3.0
### New features
* Better exception logging for IRobustJob.
* Add SetGridAudio helper for SharedAudioSystem.
### Bugfixes
* Fix placement manager not setting entity rotation correctly.
* Fix grid-based audio not playing correctly.
## 223.2.0
### New features
* Added several new `FormattedMessage` methods for better exception tolerance when parsing markup. Several existing methods have been marked as obsolete, with new renamed methods taking their place.
## 223.1.2
### Bugfixes
* `MapGridComponent.LastTileModifiedTick` is now actually networked to clients.
## 223.1.1
### Bugfixes
* Fixed an exception caused by enum cvars using integer type values instead of enum values
## 223.1.0
### Other
* Various `ContainerSystem` methods have been obsoleted in favour of overrides that take in an `Entity` struct instead of `EntityUid`
* Various `EntityCoordinates` methods have been obsoleted with replacements added to `SharedTransformSystem`
## 223.0.0
### Breaking changes
* The `ComponentState` class is now abstract. Networked components that don't have state information now just return a null state.
* The way that delta component states work has changed. It now expects there to be two different state classes, only one of which should implement `IComponentDeltaState<TFullState>`
### New features
* A new `replay.checkpoint_min_interval` cvar has been added. It can be used to limit the frequency at which checkpoints are generated when loading a replay.
* Added `IConfigurationManager.OnCVarValueChanged`. This is a c# event that gets invoked whenever any cvar value changes.
### Bugfixes
* `IEyeManager.GetWorldViewbounds()` and `IEyeManager.GetWorldViewbounds()` should now return the correct bounds if the main viewport does not take up the whole screen.
### Other
* The default values of various replay related cvars have been changed to try and reduce memory usage.
## 222.4.0
### New features
* Added the following types from `System.Numerics` to the sandbox: `Complex`, `Matrix3x2`, `Matrix4x4`, `Plane`, `Quaternion`, `Vector3`, `Vector4`.
## 222.3.0
### New features
* `ITileDefinition.EditorHidden` allows hiding a tile from the tile spawn panel.
* Ordered event subscriptions now take child types into account, so ordering based on a shared type will work.
### Bugfixes
* Cross-map BUI range checks now work.
* Paused entities update on prototype reload.
### Other
* Fixed build compatibility with .NET 8.0.300 SDK, due to changes in how Central Package Management behaves.
* Physics component has delta states to reduce network usage.
## 222.2.0
### New features
* Added `EntityQuery.Comp()` (abbreviation of `GetComponent()`)
### Bugfixes
* Fix `SerializationManager.TryGetVariableType` checking the wrong property.
* Fixed GrammarSystem mispredicting a character's gender
### Other
* User interface system now performs range checks in parallel
## 222.1.1
### Bugfixes
* Fixed never setting BoundUserInterface.State.
### Other
* Add truncate for filesaving.
* Add method for getting the type of a data field by name from ISerializationManager.
## 222.1.0
### New features
* Added `BoundKeyEventArgs.IsRepeat`.
* Added `net.lidgren_log_warning` and `net.lidgren_log_error` CVars.
### Bugfixes
* Fix assert trip when holding repeatable keybinds.
### Other
* Updated Lidgren to v0.3.1. This should provide performance improvements if warning/error logs are disabled.
## 222.0.0
### Breaking changes
* Mark IComponentFactory argument in EntityPrototype as mandatory.
### New features
* Add `EntProtoId<T>` to check for components on the attached entity as well.
### Bugfixes
* Fix PVS iterating duplicate chunks for multiple viewsubscriptions.
### Other
* Defer clientside BUI opens if it's the first state that comes in.
## 221.2.0
### New features

View File

@@ -2,7 +2,4 @@
id: Audio
name: Audio
description: Audio entity used by engine
save: false
components:
- type: Transform
gridTraversal: false
save: false

View File

@@ -1,7 +1,7 @@
- type: entity
id: debugRotation
abstract: true
categories: [ Debug ]
categories: [ debug ]
components:
- type: Sprite
netsync: false

View File

@@ -1,20 +1,17 @@
# debug related entities
- type: entityCategory
id: Debug
id: debug
name: entity-category-name-debug
description: entity-category-desc-debug
suffix: entity-category-suffix-debug
# entities that spawn other entities
- type: entityCategory
id: Spawner
id: spawner
name: entity-category-name-spawner
description: entity-category-desc-spawner
# simple category that just exists to hide prototypes in spawn menus
# entities that should be hidden from the spawn menu
- type: entityCategory
id: HideSpawnMenu
id: hideSpawnMenu
name: entity-category-name-hide
description: entity-category-desc-hide
hideSpawnMenu: true
inheritable: false

View File

@@ -9,7 +9,6 @@ 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-enum = {$arg} is not a {$enum} Enum.
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-parse-failure-session = There is no session with username: {$username}
@@ -44,13 +43,6 @@ cmd-cvar-compl-list = List available CVars
cmd-cvar-arg-name = <name | ?>
cmd-cvar-value-hidden = <value hidden>
## 'cvar_subs' command
cmd-cvar_subs-desc = Lists the OnValueChanged subscriptions for a CVar.
cmd-cvar_subs-help = Usage: cvar_subs <name>
cmd-cvar_subs-invalid-args = Must provide exactly one argument.
cmd-cvar_subs-arg-name = <name>
## 'list' command
cmd-list-desc = Lists available commands, with optional search filter
cmd-list-help = Usage: list [filter]
@@ -253,6 +245,9 @@ cmd-bind-arg-command = <InputCommand>
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
cmd-net-draw-interp-help = Usage: net_draw_interp
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
cmd-net-draw-interp-help = Usage: net_draw_interp
cmd-net-watch-ent-desc = Dumps all network updates for an EntityId to the console.
cmd-net-watch-ent-help = Usage: net_watchent <0|EntityUid>
@@ -304,9 +299,16 @@ cmd-savegrid-help = savegrid <gridID> <Path>
cmd-testbed-desc = Loads a physics testbed on the specified map.
cmd-testbed-help = testbed <mapid> <test>
cmd-saveconfig-desc = Saves the client configuration to the config file.
cmd-saveconfig-help = saveconfig
## 'flushcookies' command
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
cmd-flushcookies-desc = Flush CEF cookie storage to disk
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
Note that the actual operation is asynchronous.
## 'addcomp' command
cmd-addcomp-desc = Adds a component to an entity.
cmd-addcomp-help = addcomp <uid> <componentName>
@@ -382,9 +384,9 @@ cmd-tp-desc = Teleports a player to any location in the round.
cmd-tp-help = tp <x> <y> [<mapID>]
cmd-tpto-desc = Teleports the current player or the specified players/entities to the location of the first player/entity.
cmd-tpto-help = tpto <username|uid> [username|NetEntity]...
cmd-tpto-destination-hint = destination (NetEntity or username)
cmd-tpto-victim-hint = entity to teleport (NetEntity or username)
cmd-tpto-help = tpto <username|uid> [username|uid]...
cmd-tpto-destination-hint = destination (uid or username)
cmd-tpto-victim-hint = entity to teleport (uid or username)
cmd-tpto-parse-error = Cant resolve entity or player: {$str}
cmd-listplayers-desc = Lists all players currently connected.
@@ -444,6 +446,9 @@ cmd-showanchored-help = Usage: showanchored
cmd-dmetamem-desc = Dumps a type's members in a format suitable for the sandbox configuration file.
cmd-dmetamem-help = Usage: dmetamem <type>
cmd-dmetamem-desc = Displays chunk bounds for the purposes of rendering.
cmd-dmetamem-help = Usage: showchunkbb <type>
cmd-launchauth-desc = Load authentication tokens from launcher data to aid in testing of live servers.
cmd-launchauth-help = Usage: launchauth <account name>
@@ -510,6 +515,9 @@ cmd-profsnap-help = Usage: profsnap
cmd-devwindow-desc = Dev Window
cmd-devwindow-help = Usage: devwindow
cmd-devwindow-desc = Open file
cmd-devwindow-help = Usage: testopenfile
cmd-scene-desc = Immediately changes the UI scene/state.
cmd-scene-help = Usage: scene <className>
@@ -520,11 +528,14 @@ cmd-hwid-desc = Returns the current HWID (HardWare ID).
cmd-hwid-help = Usage: hwid
cmd-vvread-desc = Retrieve a path's value using VV (View Variables).
cmd-vvread-help = Usage: vvread <path>
cmd-vvread-desc = Usage: vvread <path>
cmd-vvwrite-desc = Modify a path's value using VV (View Variables).
cmd-vvwrite-help = Usage: vvwrite <path>
cmd-vv-desc = Opens View Variables (VV).
cmd-vv-help = Usage: vv <path|entity ID|guihover>
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
cmd-vvinvoke-help = Usage: vvinvoke <path> [arguments...]

View File

@@ -1,9 +1,8 @@
entity-category-name-debug = Debug
entity-category-desc-debug = Entity prototypes intended for debugging & testing.
entity-category-suffix-debug = Debug
entity-category-name-spawner = Spawner
entity-category-desc-spawner = Entity prototypes that spawn other entities.
entity-category-name-hide = Hidden
entity-category-desc-hide = Entity prototypes that should be hidden from entity spawn menus
entity-category-desc-hide = Entity prototypes that should be hidden from the spawn menu

View File

@@ -1,92 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.MustCallBaseAnalyzer>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class MustCallBaseAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<MustCallBaseAnalyzer, NUnitVerifier>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.IoC.MustCallBaseAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Analyzers;
public class Foo
{
[MustCallBase]
public virtual void Function()
{
}
[MustCallBase(true)]
public virtual void Function2()
{
}
}
public class Bar : Foo
{
public override void Function()
{
}
public override void Function2()
{
}
}
public class Baz : Foo
{
public override void Function()
{
base.Function();
}
}
public class Bal : Bar
{
public override void Function2()
{
}
}
""";
await Verifier(code,
// /0/Test0.cs(20,26): warning RA0028: Overriders of this function must always call the base function
VerifyCS.Diagnostic().WithSpan(20, 26, 20, 34),
// /0/Test0.cs(41,26): warning RA0028: Overriders of this function must always call the base function
VerifyCS.Diagnostic().WithSpan(41, 26, 41, 35));
}
}

View File

@@ -1,57 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.NoUncachedRegexAnalyzer>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class NoUncachedRegexAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<NoUncachedRegexAnalyzer, NUnitVerifier>()
{
TestState =
{
Sources = { code }
},
};
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using System.Text.RegularExpressions;
public static class Foo
{
public static void Bad()
{
Regex.Replace("foo", "bar", "baz");
}
public static void Good()
{
var r = new Regex("bar");
r.Replace("foo", "baz");
}
}
""";
await Verifier(code,
// /0/Test0.cs(7,9): warning RA0026: Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.
VerifyCS.Diagnostic().WithSpan(7, 9, 7, 43)
);
}
}

View File

@@ -10,7 +10,6 @@
<ItemGroup>
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LogicalName="Robust.Shared.Analyzers.AccessAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LogicalName="Robust.Shared.Analyzers.AccessPermissions.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\MustCallBaseAttribute.cs" LogicalName="Robust.Shared.IoC.MustCallBaseAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
</ItemGroup>

View File

@@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
using Robust.Shared.Serialization.Manager.Definition;
namespace Robust.Analyzers;
@@ -57,18 +56,8 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
"Make sure to add a setter."
);
private static readonly DiagnosticDescriptor DataFieldRedundantTagRule = new(
Diagnostics.IdDataFieldRedundantTag,
"Data field has redundant tag specified",
"Data field {0} in data definition {1} has an explicitly set tag that matches autogenerated tag",
"Usage",
DiagnosticSeverity.Info,
true,
"Make sure to remove the tag string from the data field attribute."
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule,
DataFieldRedundantTagRule
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
);
public override void Initialize(AnalysisContext context)
@@ -136,11 +125,6 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
}
if (HasRedundantTag(fieldSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
}
}
}
@@ -165,11 +149,6 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
}
if (HasRedundantTag(propertySymbol))
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
}
}
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
@@ -269,29 +248,6 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
return false;
}
private static bool HasRedundantTag(ISymbol symbol)
{
if (!IsDataField(symbol, out var _, out var attribute))
return false;
// No args, no problem
if (attribute.ConstructorArguments.Length == 0)
return false;
// If a tag is explicitly specified, it will be the first argument...
var tagArgument = attribute.ConstructorArguments[0];
// ...but the first arg could also something else, since tag is optional
// so we make sure that it's a string
if (tagArgument.Value is not string explicitName)
return false;
// Get the name that sourcegen would provide
var automaticName = DataDefinitionUtility.AutoGenerateTag(symbol.Name);
// If the explicit name matches the sourcegen name, we have a redundancy
return explicitName == automaticName;
}
private static bool IsImplicitDataDefinition(ITypeSymbol type)
{
if (HasAttribute(type, ImplicitDataDefinitionNamespace))

View File

@@ -1,5 +1,8 @@
#nullable enable
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
@@ -13,11 +16,8 @@ namespace Robust.Analyzers;
[ExportCodeFixProvider(LanguageNames.CSharp)]
public sealed class DefinitionFixer : CodeFixProvider
{
private const string DataFieldAttributeName = "DataField";
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable,
IdDataFieldRedundantTag
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
@@ -34,8 +34,6 @@ public sealed class DefinitionFixer : CodeFixProvider
return RegisterDataFieldFix(context, diagnostic);
case IdDataFieldPropertyWritable:
return RegisterDataFieldPropertyFix(context, diagnostic);
case IdDataFieldRedundantTag:
return RegisterRedundantTagFix(context, diagnostic);
}
}
@@ -74,68 +72,6 @@ public sealed class DefinitionFixer : CodeFixProvider
return document.WithSyntaxRoot(root);
}
private static async Task RegisterRedundantTagFix(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
var span = diagnostic.Location.SourceSpan;
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<MemberDeclarationSyntax>().First();
if (token == null)
return;
// Find the DataField attribute
AttributeSyntax? dataFieldAttribute = null;
foreach (var attributeList in token.AttributeLists)
{
foreach (var attribute in attributeList.Attributes)
{
if (attribute.Name.ToString() == DataFieldAttributeName)
{
dataFieldAttribute = attribute;
break;
}
}
if (dataFieldAttribute != null)
break;
}
if (dataFieldAttribute == null)
return;
context.RegisterCodeFix(CodeAction.Create(
"Remove explicitly set tag",
c => RemoveRedundantTag(context.Document, dataFieldAttribute, c),
"Remove explicitly set tag"
), diagnostic);
}
private static async Task<Document> RemoveRedundantTag(Document document, AttributeSyntax syntax, CancellationToken cancellation)
{
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
if (syntax.ArgumentList == null)
return document;
AttributeSyntax? newSyntax;
if (syntax.ArgumentList.Arguments.Count == 1)
{
// If this is the only argument, delete the ArgumentList so we don't leave empty parentheses
newSyntax = syntax.RemoveNode(syntax.ArgumentList, SyntaxRemoveOptions.KeepNoTrivia);
}
else
{
// Remove the first argument, which is the tag
var newArgs = syntax.ArgumentList.Arguments.RemoveAt(0);
var newArgList = syntax.ArgumentList.WithArguments(newArgs);
// Construct a new attribute with the tag removed
newSyntax = syntax.WithArgumentList(newArgList);
}
root = root!.ReplaceNode(syntax, newSyntax!);
return document.WithSyntaxRoot(root);
}
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);

View File

@@ -1,111 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
#nullable enable
/// <summary>
/// Enforces <c>MustCallBaseAttribute</c>.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class MustCallBaseAnalyzer : DiagnosticAnalyzer
{
private const string Attribute = "Robust.Shared.Analyzers.MustCallBaseAttribute";
private static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdMustCallBase,
"No base call in overriden function",
"Overriders of this function must always call the base function",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
}
private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
if (context.Symbol is not IMethodSymbol { IsOverride: true } method)
return;
var attrSymbol = context.Compilation.GetTypeByMetadataName(Attribute);
if (attrSymbol == null)
return;
if (DoesMethodOverriderHaveAttribute(method, attrSymbol) is not { } data)
return;
if (data is { onlyOverrides: true, depth: < 2 })
return;
var syntax = (MethodDeclarationSyntax) method.DeclaringSyntaxReferences[0].GetSyntax();
if (HasBaseCall(syntax))
return;
var diag = Diagnostic.Create(Rule, syntax.Identifier.GetLocation());
context.ReportDiagnostic(diag);
}
private static (int depth, bool onlyOverrides)? DoesMethodOverriderHaveAttribute(
IMethodSymbol method,
INamedTypeSymbol attributeSymbol)
{
var depth = 0;
while (method.OverriddenMethod != null)
{
depth += 1;
method = method.OverriddenMethod;
if (GetAttribute(method, attributeSymbol) is not { } attribute)
continue;
var onlyOverrides = attribute.ConstructorArguments is [{Kind: TypedConstantKind.Primitive, Value: true}];
return (depth, onlyOverrides);
}
return null;
}
private static bool HasBaseCall(MethodDeclarationSyntax syntax)
{
return syntax.Accept(new BaseCallLocator());
}
private static AttributeData? GetAttribute(ISymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
{
return namedTypeSymbol.GetAttributes()
.SingleOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
}
private sealed class BaseCallLocator : CSharpSyntaxVisitor<bool>
{
public override bool VisitBaseExpression(BaseExpressionSyntax node)
{
return true;
}
public override bool DefaultVisit(SyntaxNode node)
{
foreach (var childNode in node.ChildNodes())
{
if (childNode is not CSharpSyntaxNode cSharpSyntax)
continue;
if (cSharpSyntax.Accept(this))
return true;
}
return false;
}
}
}

View File

@@ -1,66 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class NoUncachedRegexAnalyzer : DiagnosticAnalyzer
{
private const string RegexTypeName = "Regex";
private const string RegexType = $"System.Text.RegularExpressions.{RegexTypeName}";
private static readonly DiagnosticDescriptor Rule = new (
Diagnostics.IdUncachedRegex,
"Use of uncached static Regex function",
"Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public static readonly HashSet<string> BadFunctions =
[
"Count",
"EnumerateMatches",
"IsMatch",
"Match",
"Matches",
"Replace",
"Split"
];
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterOperationAction(CheckInvocation, OperationKind.Invocation);
}
private static void CheckInvocation(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation invocation)
return;
// All Regex functions we care about are static.
var targetMethod = invocation.TargetMethod;
if (!targetMethod.IsStatic)
return;
// Bail early.
if (targetMethod.ContainingType.Name != "Regex")
return;
var regexType = context.Compilation.GetTypeByMetadataName(RegexType);
if (!SymbolEqualityComparer.Default.Equals(regexType, targetMethod.ContainingType))
return;
if (!BadFunctions.Contains(targetMethod.Name))
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.Syntax.GetLocation()));
}
}

View File

@@ -16,11 +16,6 @@
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<ItemGroup>
<!-- Needed for DataDefinitionAnalyzer. -->
<Compile Include="..\Robust.Shared\Serialization\Manager\Definition\DataDefinitionUtility.cs" LinkBase="Implementations" />
</ItemGroup>
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
<PropertyGroup>

View File

@@ -26,8 +26,7 @@ public sealed class DefaultSQLConfig : IConfig
public IEnumerable<IExporter> GetExporters()
{
//yield return SQLExporter.Default;
yield break;
yield return SQLExporter.Default;
}
public IEnumerable<IColumnProvider> GetColumnProviders() => DefaultConfig.Instance.GetColumnProviders();

View File

@@ -15,10 +15,11 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Npgsql;
using Npgsql.Internal;
using Npgsql.Internal.TypeHandlers;
using Npgsql.Internal.TypeHandling;
namespace Robust.Benchmarks.Exporters;
/*
public sealed class SQLExporter : IExporter
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
@@ -97,9 +98,7 @@ public sealed class SQLExporter : IExporter
public string Name => "sql";
}
*/
/*
// https://github.com/npgsql/efcore.pg/issues/1107#issuecomment-945126627
class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
{
@@ -139,7 +138,6 @@ class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
=> null; // Let the built-in resolver do this
}
}
*/
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<BenchmarkContext>
{

View File

@@ -239,7 +239,6 @@ namespace Robust.Build.Tasks
engine.LogErrorEvent(new BuildErrorEventArgs("XAMLIL", "", res.FilePath, 0, 0, 0, 0,
$"{res.FilePath}: {e.Message}", "", "CompileRobustXaml"));
}
res.Remove();
}
return true;
}

View File

@@ -41,7 +41,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
[Dependency] private readonly IParallelManager _parMan = default!;
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
[Dependency] private readonly IAudioInternal _audio = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly SharedTransformSystem _xformSys = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
@@ -216,6 +216,11 @@ public sealed partial class AudioSystem : SharedAudioSystem
}
}
if ((component.Flags & AudioFlags.GridAudio) != 0x0)
{
_metadata.SetFlag(entity.Owner, MetaDataFlags.Undetachable, true);
}
// Need to set all initial data for first frame.
ApplyAudioParams(component.Params, component);
component.Source.Global = component.Global;
@@ -331,22 +336,55 @@ public sealed partial class AudioSystem : SharedAudioSystem
}
Vector2 worldPos;
component.Volume = component.Params.Volume;
Vector2 delta;
var gridUid = xform.ParentUid;
// Handle grid audio differently by using grid position.
// Handle grid audio differently by using nearest-edge instead of entity centre.
if ((component.Flags & AudioFlags.GridAudio) != 0x0)
{
var parentUid = xform.ParentUid;
worldPos = _maps.GetGridPosition(parentUid);
}
else
{
worldPos = _xformSys.GetWorldPosition(entity);
// It's our grid so max volume.
if (_listenerGrid == gridUid)
{
component.Volume = component.Params.Volume;
component.Occlusion = 0f;
component.Position = listener.Position;
return;
}
// TODO: Need a grid-optimised version because this is gonna be expensive.
// Just to avoid clipping on and off grid or nearestPoint changing we'll
// always set the sound to listener's pos, we'll just manually do gain ourselves.
if (_physics.TryGetNearest(gridUid, listener, out _, out var gridDistance))
{
// Out of range
if (gridDistance > component.MaxDistance)
{
component.Gain = 0f;
return;
}
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.
component.Gain = paramsGain * _audio.GetAttenuationGain(
gridDistance,
component.Params.RolloffFactor,
component.Params.ReferenceDistance,
component.Params.MaxDistance);
component.Position = listener.Position;
return;
}
// Can't get nearest point so don't play anymore.
component.Gain = 0f;
return;
}
worldPos = _xformSys.GetWorldPosition(entity);
component.Volume = component.Params.Volume;
// Max distance check
delta = worldPos - listener.Position;
var delta = worldPos - listener.Position;
var distance = delta.Length();
// Out of range so just clip it for us.
@@ -365,15 +403,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
}
// Update audio occlusion
if ((component.Flags & AudioFlags.NoOcclusion) == AudioFlags.NoOcclusion)
{
component.Occlusion = 0f;
}
else
{
var occlusion = GetOcclusion(listener, delta, distance, entity);
component.Occlusion = occlusion;
}
var occlusion = GetOcclusion(listener, delta, distance, entity);
component.Occlusion = occlusion;
// Update audio positions.
component.Position = worldPos;
@@ -640,10 +671,10 @@ public sealed partial class AudioSystem : SharedAudioSystem
private (EntityUid Entity, AudioComponent Component) CreateAndStartPlayingStream(AudioParams? audioParams, AudioStream stream)
{
var audioP = audioParams ?? AudioParams.Default;
var entity = SetupAudio(null, audioP, initialize: false, length: stream.Length);
LoadStream(entity, stream);
var entity = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace);
var comp = SetupAudio(entity, null, audioP, stream.Length);
LoadStream((entity, comp), stream);
EntityManager.InitializeAndStartEntity(entity);
var comp = entity.Comp;
var source = comp.Source;
// TODO clamp the offset inside of SetPlaybackPosition() itself.

View File

@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var type = GetType(args[0]);
var type = Type.GetType(args[0]);
if (type == null)
{
@@ -25,17 +25,6 @@ namespace Robust.Client.Console.Commands
shell.WriteLine(sig);
}
}
private Type? GetType(string name)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.GetType(name) is { } type)
return type;
}
return null;
}
}
#endif
}

View File

@@ -225,7 +225,7 @@ namespace Robust.Client.Debugging
{
var viewBounds = args.WorldBounds;
var viewAABB = args.WorldAABB;
var mapId = args.MapId;
var mapId = _eyeManager.CurrentMap;
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0)
{
@@ -368,12 +368,12 @@ namespace Robust.Client.Debugging
}
worldHandle.UseShader(null);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.SetTransform(Matrix3.Identity);
}
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
{
var mapId = args.MapId;
var mapId = _eyeManager.CurrentMap;
var mousePos = _inputManager.MouseScreenPosition;
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.ShapeInfo) != 0x0)
@@ -443,7 +443,7 @@ namespace Robust.Client.Debugging
}
screenHandle.UseShader(null);
screenHandle.SetTransform(Matrix3x2.Identity);
screenHandle.SetTransform(Matrix3.Identity);
}
protected internal override void Draw(in OverlayDrawArgs args)
@@ -520,11 +520,11 @@ namespace Robust.Client.Debugging
var matrix1 = xform1.WorldMatrix;
var matrix2 = xform2.WorldMatrix;
var xf1 = new Vector2(matrix1.M31, matrix1.M32);
var xf2 = new Vector2(matrix2.M31, matrix2.M32);
var xf1 = new Vector2(matrix1.R0C2, matrix1.R1C2);
var xf2 = new Vector2(matrix2.R0C2, matrix2.R1C2);
var p1 = Vector2.Transform(joint.LocalAnchorA, matrix1);
var p2 = Vector2.Transform(joint.LocalAnchorB, matrix2);
var p1 = matrix1.Transform(joint.LocalAnchorA);
var p2 = matrix2.Transform(joint.LocalAnchorB);
var xfa = new Transform(xf1, xform1.WorldRotation);
var xfb = new Transform(xf2, xform2.WorldRotation);

View File

@@ -150,7 +150,7 @@ namespace Robust.Client.GameObjects
[DataField("color")]
private Color color = Color.White;
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
public Matrix3 LocalMatrix = Matrix3.Identity;
[Animatable]
[ViewVariables(VVAccess.ReadWrite)]
@@ -389,10 +389,10 @@ namespace Robust.Client.GameObjects
internal void UpdateLocalMatrix()
{
LocalMatrix = Matrix3Helpers.CreateTransform(in offset, in rotation, in scale);
LocalMatrix = Matrix3.CreateTransform(in offset, in rotation, in scale);
}
public Matrix3x2 GetLocalMatrix()
public Matrix3 GetLocalMatrix()
{
return LocalMatrix;
}
@@ -1304,21 +1304,22 @@ namespace Robust.Client.GameObjects
// worldRotation + eyeRotation should be the angle of the entity on-screen. If no-rot is enabled this is just set to zero.
// However, at some point later the eye-matrix is applied separately, so we subtract -eye rotation for now:
var entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
var entityMatrix = Matrix3.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
var transformSprite = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSprite);
if (GranularLayersRendering)
{
//Default rendering
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation);
var transformDefault = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation);
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformDefault);
//Snap to cardinals
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
var transformSnap = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSnap);
//No rotation
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, -eyeRotation);
var transformNoRot = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
entityMatrix = Matrix3.CreateTransform(worldPosition, -eyeRotation);
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformNoRot);
foreach (var layer in Layers) {
switch (layer.RenderingStrategy)
@@ -1379,7 +1380,7 @@ namespace Robust.Client.GameObjects
// TODO whenever sprite comp gets ECS'd , just make this a direct method call.
var ev = new QueueSpriteTreeUpdateEvent(entities.GetComponent<TransformComponent>(Owner));
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
entities.EventBus.RaiseComponentEvent(this, ref ev);
}
private void QueueUpdateIsInert()
@@ -1389,7 +1390,7 @@ namespace Robust.Client.GameObjects
// TODO whenever sprite comp gets ECS'd , just make this a direct method call.
var ev = new SpriteUpdateInertEvent();
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
entities.EventBus.RaiseComponentEvent(this, ref ev);
}
[Obsolete("Use SpriteSystem instead.")]
@@ -1545,7 +1546,7 @@ namespace Robust.Client.GameObjects
private RSI.State? _actualState;
[ViewVariables] public RSI.State? ActualState => _actualState;
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
public Matrix3 LocalMatrix = Matrix3.Identity;
[ViewVariables(VVAccess.ReadWrite)]
public Vector2 Scale
@@ -1691,7 +1692,7 @@ namespace Robust.Client.GameObjects
internal void UpdateLocalMatrix()
{
LocalMatrix = Matrix3Helpers.CreateTransform(in _offset, in _rotation, in _scale);
LocalMatrix = Matrix3.CreateTransform(in _offset, in _rotation, in _scale);
}
RSI? ISpriteLayer.Rsi { get => RSI; set => SetRsi(value); }
@@ -1963,27 +1964,27 @@ namespace Robust.Client.GameObjects
/// Given the apparent rotation of an entity on screen (world + eye rotation), get layer's matrix for drawing &
/// relevant RSI direction.
/// </summary>
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3x2 layerDrawMatrix)
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3 layerDrawMatrix)
{
if (_parent.NoRotation || dir == RsiDirection.South)
layerDrawMatrix = LocalMatrix;
else
{
layerDrawMatrix = Matrix3x2.Multiply(_rsiDirectionMatrices[(int)dir], LocalMatrix);
Matrix3.Multiply(in _rsiDirectionMatrices[(int)dir], in LocalMatrix, out layerDrawMatrix);
}
}
private static Matrix3x2[] _rsiDirectionMatrices = new Matrix3x2[]
private static Matrix3[] _rsiDirectionMatrices = new Matrix3[]
{
// array order chosen such that this array can be indexed by casing an RSI direction to an int
Matrix3x2.Identity, // should probably just avoid matrix multiplication altogether if the direction is south.
Matrix3Helpers.CreateRotation(-Direction.North.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.East.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.West.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.SouthEast.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.SouthWest.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.NorthEast.ToAngle()),
Matrix3Helpers.CreateRotation(-Direction.NorthWest.ToAngle())
Matrix3.Identity, // should probably just avoid matrix multiplication altogether if the direction is south.
Matrix3.CreateRotation(-Direction.North.ToAngle()),
Matrix3.CreateRotation(-Direction.East.ToAngle()),
Matrix3.CreateRotation(-Direction.West.ToAngle()),
Matrix3.CreateRotation(-Direction.SouthEast.ToAngle()),
Matrix3.CreateRotation(-Direction.SouthWest.ToAngle()),
Matrix3.CreateRotation(-Direction.NorthEast.ToAngle()),
Matrix3.CreateRotation(-Direction.NorthWest.ToAngle())
};
/// <summary>
@@ -2017,7 +2018,7 @@ namespace Robust.Client.GameObjects
/// <summary>
/// Render a layer. This assumes that the input angle is between 0 and 2pi.
/// </summary>
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatrix, Angle angle, Direction? overrideDirection)
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3 spriteMatrix, Angle angle, Direction? overrideDirection)
{
if (!Visible || Blank)
return;
@@ -2039,7 +2040,7 @@ namespace Robust.Client.GameObjects
if (CopyToShaderParameters == null)
{
// Set the drawing transform for this layer
var transformMatrix = Matrix3x2.Multiply(layerMatrix, spriteMatrix);
Matrix3.Multiply(in layerMatrix, in spriteMatrix, out var transformMatrix);
drawingHandle.SetTransform(in transformMatrix);
RenderTexture(drawingHandle, texture);

View File

@@ -11,7 +11,6 @@ namespace Robust.Client.GameObjects
{
private readonly List<Entity<AnimationPlayerComponent>> _activeAnimations = new();
private EntityQuery<AnimationPlayerComponent> _playerQuery;
private EntityQuery<MetaDataComponent> _metaQuery;
[Dependency] private readonly IComponentFactory _compFact = default!;
@@ -19,7 +18,6 @@ namespace Robust.Client.GameObjects
public override void Initialize()
{
base.Initialize();
_playerQuery = GetEntityQuery<AnimationPlayerComponent>();
_metaQuery = GetEntityQuery<MetaDataComponent>();
}
@@ -76,8 +74,7 @@ namespace Robust.Client.GameObjects
foreach (var key in remie)
{
component.PlayingAnimations.Remove(key);
var completedEvent = new AnimationCompletedEvent {Uid = uid, Key = key, Finished = true};
EntityManager.EventBus.RaiseLocalEvent(uid, completedEvent, true);
EntityManager.EventBus.RaiseLocalEvent(uid, new AnimationCompletedEvent {Uid = uid, Key = key}, true);
}
return false;
@@ -174,42 +171,31 @@ namespace Robust.Client.GameObjects
return component.PlayingAnimations.ContainsKey(key);
}
[Obsolete]
public void Stop(AnimationPlayerComponent component, string key)
{
Stop((component.Owner, component), key);
component.PlayingAnimations.Remove(key);
}
public void Stop(Entity<AnimationPlayerComponent?> entity, string key)
public void Stop(EntityUid uid, string key)
{
if (!_playerQuery.Resolve(entity.Owner, ref entity.Comp, false) ||
!entity.Comp.PlayingAnimations.Remove(key))
{
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
return;
}
var completedEvent = new AnimationCompletedEvent {Uid = entity.Owner, Key = key, Finished = false};
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, completedEvent, true);
player.PlayingAnimations.Remove(key);
}
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
{
Stop((uid, component), key);
if (!Resolve(uid, ref component, false))
return;
component.PlayingAnimations.Remove(key);
}
}
/// <summary>
/// Raised whenever an animation stops, either due to running its course or being stopped manually.
/// </summary>
public sealed class AnimationCompletedEvent : EntityEventArgs
{
public EntityUid Uid { get; init; }
public string Key { get; init; } = string.Empty;
/// <summary>
/// If true, the animation finished by getting to its natural end.
/// If false, it was removed prematurely via <see cref="AnimationPlayerSystem.Stop(Robust.Client.GameObjects.AnimationPlayerComponent,string)"/> or similar overloads.
/// </summary>
public bool Finished { get; init; }
}
}

View File

@@ -120,7 +120,7 @@ namespace Robust.Client.GameObjects
{
var type = _serializer.FindSerializedType(typeof(BaseContainer), data.ContainerType);
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type!, inject:false);
container.Init(this, id, (uid, component));
InitContainer(container, (uid, component), id);
component.Containers.Add(id, container);
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Shared.Console;
using Robust.Shared.Containers;
@@ -119,7 +118,7 @@ public sealed class EntityLookupOverlay : Overlay
//DebugTools.Assert(!ent.IsInContainer(_entityManager));
var (entPos, entRot) = _transform.GetWorldPositionRotation(ent);
var lookupPos = Vector2.Transform(entPos, invMatrix);
var lookupPos = invMatrix.Transform(entPos);
var lookupRot = entRot - rotation;
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, _xformQuery);
@@ -128,6 +127,6 @@ public sealed class EntityLookupOverlay : Overlay
}
});
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.SetTransform(Matrix3.Identity);
}
}

View File

@@ -63,7 +63,7 @@ namespace Robust.Client.GameObjects
protected internal override void Draw(in OverlayDrawArgs args)
{
var map = args.MapId;
var map = _eyeManager.CurrentMap;
if (map == MapId.Nullspace) return;
foreach (var (_, treeComp) in _trees.GetIntersectingTrees(map, args.WorldBounds))

View File

@@ -70,7 +70,7 @@ namespace Robust.Client.GameObjects
protected internal override void Draw(in OverlayDrawArgs args)
{
var currentMap = args.MapId;
var currentMap = _eyeManager.CurrentMap;
var viewport = args.WorldBounds;
var worldHandle = args.WorldHandle;
@@ -109,7 +109,7 @@ namespace Robust.Client.GameObjects
}
}
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.SetTransform(Matrix3.Identity);
}
}
}

View File

@@ -20,7 +20,6 @@ namespace Robust.Client.GameObjects
/// </summary>
public sealed class InputSystem : SharedInputSystem, IPostInjectInit
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IClientGameStateManager _stateManager = default!;
@@ -83,35 +82,18 @@ namespace Robust.Client.GameObjects
}
}
var clientMsg = message switch
// send it off to the server
var clientMsg = (ClientFullInputCmdMessage)message;
var fullMsg = new FullInputCmdMessage(
clientMsg.Tick,
clientMsg.SubTick,
(int)clientMsg.InputSequence,
clientMsg.InputFunctionId,
clientMsg.State,
GetNetCoordinates(clientMsg.Coordinates),
clientMsg.ScreenCoordinates)
{
ClientFullInputCmdMessage clientInput => clientInput,
FullInputCmdMessage fullInput => new ClientFullInputCmdMessage(
fullInput.Tick,
fullInput.SubTick,
fullInput.InputFunctionId,
GetCoordinates(fullInput.Coordinates),
fullInput.ScreenCoordinates,
fullInput.State,
GetEntity(fullInput.Uid)),
_ => throw new ArgumentOutOfRangeException()
};
var fullMsg = message switch
{
FullInputCmdMessage fullInput => fullInput,
ClientFullInputCmdMessage client => new FullInputCmdMessage(
client.Tick,
client.SubTick,
client.InputFunctionId,
clientMsg.State,
GetNetCoordinates(client.Coordinates),
clientMsg.ScreenCoordinates,
GetNetEntity(clientMsg.Uid)
),
_ => throw new ArgumentOutOfRangeException()
Uid = GetNetEntity(clientMsg.Uid)
};
DispatchInputCommand(clientMsg, fullMsg);
@@ -149,7 +131,7 @@ namespace Robust.Client.GameObjects
_conHost.RegisterCommand("incmd",
"Inserts an input command into the simulation",
"incmd <KeyFunction> <KeyState> [wxPos] [wyPos]",
"incmd <KeyFunction> <d|u KeyState> <wxPos> <wyPos>",
GenerateInputCommand);
}
@@ -165,47 +147,17 @@ namespace Robust.Client.GameObjects
if (_playerManager.LocalEntity is not { } pent)
return;
if (args.Length is not (2 or 4))
{
shell.WriteLine(Loc.GetString($"cmd-invalid-arg-number-error"));
return;
}
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
var keyFunction = new BoundKeyFunction(args[0]);
if (!Enum.TryParse<BoundKeyState>(args[1], out var state))
{
shell.WriteLine(Loc.GetString("cmd-parse-failure-enum", ("arg", args[1]), ("enum", nameof(BoundKeyState))));
return;
}
var pxform = Transform(pent);
var wPos = pxform.WorldPosition + new Vector2(float.Parse(args[2]), float.Parse(args[3]));
var coords = EntityCoordinates.FromMap(pent, new MapCoordinates(wPos, pxform.MapID), _transform, EntityManager);
var wOffset = Vector2.Zero;
if (args.Length == 4)
{
if (!float.TryParse(args[2], out var wX))
{
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[2])));
return;
}
if (!float.TryParse(args[3], out var wY))
{
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[3])));
return;
}
wOffset = new Vector2(wX, wY);
}
var coords = EntityCoordinates.FromMap(pent, _transform.GetMapCoordinates(pent).Offset(wOffset), _transform, EntityManager);
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
var message = new ClientFullInputCmdMessage(_timing.CurTick,
_timing.TickFraction,
funcId,
coords,
new ScreenCoordinates(0, 0, default),
state,
EntityUid.Invalid);
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
HandleInputCommand(_playerManager.LocalSession, keyFunction, message);
}

View File

@@ -184,8 +184,7 @@ namespace Robust.Client.GameObjects
/// <summary>
/// Gets the specified frame for this sprite at the specified time.
/// </summary>
/// <param name="loop">Should we clamp on the last frame and not loop</param>
public Texture GetFrame(SpriteSpecifier spriteSpec, TimeSpan curTime, bool loop = true)
public Texture GetFrame(SpriteSpecifier spriteSpec, TimeSpan curTime)
{
Texture? sprite = null;
@@ -197,29 +196,19 @@ namespace Robust.Client.GameObjects
var frames = state!.GetFrames(RsiDirection.South);
var delays = state.GetDelays();
var totalDelay = delays.Sum();
var time = curTime.TotalSeconds % totalDelay;
var delaySum = 0f;
// No looping
if (!loop && curTime.TotalSeconds >= totalDelay)
for (var i = 0; i < delays.Length; i++)
{
sprite = frames[^1];
}
// Loopable
else
{
var time = curTime.TotalSeconds % totalDelay;
var delaySum = 0f;
var delay = delays[i];
delaySum += delay;
for (var i = 0; i < delays.Length; i++)
{
var delay = delays[i];
delaySum += delay;
if (time > delaySum)
continue;
if (time > delaySum)
continue;
sprite = frames[i];
break;
}
sprite = frames[i];
break;
}
sprite ??= Frame0(spriteSpec);

View File

@@ -54,11 +54,11 @@ namespace Robust.Client.GameStates
private readonly HashSet<NetEntity> _stateEnts = new();
private readonly List<EntityUid> _toDelete = new();
private readonly List<IComponent> _toRemove = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> _outputData = new();
private readonly Dictionary<NetEntity, Dictionary<ushort, IComponentState>> _outputData = new();
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
private readonly ObjectPool<Dictionary<ushort, IComponentState?>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, IComponentState?>>(new DictPolicy<ushort, IComponentState?>(), 256);
private readonly ObjectPool<Dictionary<ushort, IComponentState>> _compDataPool =
new DefaultObjectPool<Dictionary<ushort, IComponentState>>(new DictPolicy<ushort, IComponentState>(), 256);
private uint _metaCompNetId;
@@ -259,7 +259,7 @@ namespace Robust.Client.GameStates
public void UpdateFullRep(GameState state, bool cloneDelta = false)
=> _processor.UpdateFullRep(state, cloneDelta);
public Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
=> _processor.GetFullRep();
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
@@ -600,12 +600,8 @@ namespace Robust.Client.GameStates
if (_sawmill.Level <= LogLevel.Debug)
_sawmill.Debug($" A component was dirtied: {comp.GetType()}");
if (compState != null)
{
var handleState = new ComponentHandleState(compState, null);
_entities.EventBus.RaiseComponentEvent(entity, comp, ref handleState);
}
var handleState = new ComponentHandleState(compState, null);
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
comp.LastModifiedTick = _timing.LastRealTick;
}
}
@@ -637,12 +633,8 @@ namespace Robust.Client.GameStates
if (_sawmill.Level <= LogLevel.Debug)
_sawmill.Debug($" A component was removed: {comp.GetType()}");
if (state != null)
{
var stateEv = new ComponentHandleState(state, null);
_entities.EventBus.RaiseComponentEvent(entity, comp, ref stateEv);
}
var stateEv = new ComponentHandleState(state, null);
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
comp.ClearCreationTick(); // don't undo the re-adding.
comp.LastModifiedTick = _timing.LastRealTick;
}
@@ -695,7 +687,7 @@ namespace Robust.Client.GameStates
DebugTools.Assert(component.NetSyncEnabled);
var state = _entityManager.GetComponentState(bus, component, null, GameTick.Zero);
DebugTools.Assert(state is not IComponentDeltaState);
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
compData.Add(netId, state);
}
}
@@ -1357,11 +1349,8 @@ namespace Robust.Client.GameStates
foreach (var (comp, cur, next) in _compStateWork.Values)
{
if (cur == null && next == null)
continue;
var handleState = new ComponentHandleState(cur, next);
bus.RaiseComponentEvent(uid, comp, ref handleState);
bus.RaiseComponentEvent(comp, ref handleState);
}
}
@@ -1512,11 +1501,8 @@ namespace Robust.Client.GameStates
_entityManager.AddComponent(uid, comp, true, meta);
}
if (state == null)
continue;
var handleState = new ComponentHandleState(state, null);
_entityManager.EventBus.RaiseComponentEvent(uid, comp, ref handleState);
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
}
// ensure we don't have any extra components

View File

@@ -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, IComponentState?>> _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();
compData = new Dictionary<ushort, IComponentState>();
_lastStateFullRep.Add(entityState.NetEntity, compData);
}
@@ -221,20 +221,21 @@ Had full state: {LastFullState != null}"
var compState = change.State;
if (compState is IComponentDeltaState delta
&& !delta.FullState
&& compData.TryGetValue(change.NetID, out var old)) // May fail if relying on implicit data
{
DebugTools.Assert(old is not IComponentDeltaState, "last state is not a full state");
DebugTools.Assert(old is IComponentDeltaState oldDelta && oldDelta.FullState, "last state is not a full state");
if (cloneDelta)
{
compState = delta.CreateNewFullState(old!);
compState = delta.CreateNewFullState(old);
}
else
{
delta.ApplyToFullState(old!);
delta.ApplyToFullState(old);
compState = old;
}
DebugTools.Assert(compState is not IComponentDeltaState, "newly constructed state is not a full state");
DebugTools.Assert(compState is IComponentDeltaState newState && newState.FullState, "newly constructed state is not a full state");
}
compData[change.NetID] = compState;
@@ -390,7 +391,7 @@ Had full state: {LastFullState != null}"
LastFullStateRequested = null;
}
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> implicitData)
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, IComponentState>> implicitData)
{
foreach (var (netEntity, implicitEntState) in implicitData)
{
@@ -398,7 +399,6 @@ Had full state: {LastFullState != null}"
foreach (var (netId, implicitCompState) in implicitEntState)
{
DebugTools.Assert(implicitCompState is not IComponentDeltaState);
ref var serverState = ref CollectionsMarshal.GetValueRefOrAddDefault(fullRep, netId, out var exists);
if (!exists)
@@ -407,32 +407,36 @@ Had full state: {LastFullState != null}"
continue;
}
if (serverState is not IComponentDeltaState serverDelta)
if (serverState is not IComponentDeltaState serverDelta || serverDelta.FullState)
continue;
DebugTools.AssertNotNull(implicitCompState);
// Server sent an initial delta state. This is fine as long as the client can infer an initial full
// state from the entity prototype.
serverDelta.ApplyToFullState(implicitCompState!);
if (implicitCompState is not IComponentDeltaState implicitDelta || !implicitDelta.FullState)
{
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {netEntity}");
continue;
}
serverDelta.ApplyToFullState(implicitCompState);
serverState = implicitCompState;
DebugTools.Assert(serverState is not IComponentDeltaState);
DebugTools.Assert(implicitCompState is IComponentDeltaState d && d.FullState);
}
}
}
public Dictionary<ushort, IComponentState?> GetLastServerStates(NetEntity netEntity)
public Dictionary<ushort, IComponentState> GetLastServerStates(NetEntity netEntity)
{
return _lastStateFullRep[netEntity];
}
public Dictionary<NetEntity, Dictionary<ushort, IComponentState?>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, IComponentState>> GetFullRep()
{
return _lastStateFullRep;
}
public bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary<ushort, IComponentState?>? dictionary)
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary)
{
return _lastStateFullRep.TryGetValue(entity, out dictionary);
}

View File

@@ -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, IComponentState?>> 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

View File

@@ -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, IComponentState?>> 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, IComponentState?> 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, IComponentState?>? dictionary);
[NotNullWhen(true)] out Dictionary<ushort, IComponentState>? dictionary);
}
}

View File

@@ -49,7 +49,7 @@ namespace Robust.Client.GameStates
while (query.MoveNext(out var uid, out var transform))
{
// if not on the same map, continue
if (transform.MapID != args.MapId || _container.IsEntityInContainer(uid))
if (transform.MapID != _eyeManager.CurrentMap || _container.IsEntityInContainer(uid))
continue;
if (transform.GridUid == uid)

View File

@@ -64,22 +64,29 @@ namespace Robust.Client.Graphics
/// <inheritdoc />
public Box2 GetWorldViewport()
{
return GetWorldViewbounds().CalcBoundingBox();
var vpSize = _displayManager.ScreenSize;
var topLeft = ScreenToMap(Vector2.Zero);
var topRight = ScreenToMap(new Vector2(vpSize.X, 0));
var bottomRight = ScreenToMap(vpSize);
var bottomLeft = ScreenToMap(new Vector2(0, vpSize.Y));
var left = MathHelper.Min(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
var bottom = MathHelper.Min(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
var right = MathHelper.Max(topLeft.X, topRight.X, bottomRight.X, bottomLeft.X);
var top = MathHelper.Max(topLeft.Y, topRight.Y, bottomRight.Y, bottomLeft.Y);
return new Box2(left, bottom, right, top);
}
/// <inheritdoc />
public Box2Rotated GetWorldViewbounds()
{
// This is an inefficient and roundabout way of geting the viewport.
// But its a method that shouldn't get used much.
var vp = MainViewport as Control;
var vpSize = vp?.PixelSize ?? _displayManager.ScreenSize;
var vpSize = _displayManager.ScreenSize;
var topRight = ScreenToMap(new Vector2(vpSize.X, 0)).Position;
var bottomLeft = ScreenToMap(new Vector2(0, vpSize.Y)).Position;
// This assumes the main viewports eye and the main eye are the same.
var rotation = new Angle(CurrentEye.Rotation);
var center = (bottomLeft + topRight) / 2;
@@ -101,16 +108,18 @@ namespace Robust.Client.Graphics
}
/// <inheritdoc />
public void GetScreenProjectionMatrix(out Matrix3x2 projMatrix)
public void GetScreenProjectionMatrix(out Matrix3 projMatrix)
{
Matrix3x2 result = default;
Matrix3 result = default;
result.M11 = PixelsPerMeter;
result.M22 = -PixelsPerMeter;
result.R0C0 = PixelsPerMeter;
result.R1C1 = -PixelsPerMeter;
var screenSize = _displayManager.ScreenSize;
result.M31 = screenSize.X / 2f;
result.M32 = screenSize.Y / 2f;
result.R0C2 = screenSize.X / 2f;
result.R1C2 = screenSize.Y / 2f;
result.R2C2 = 1;
/* column major
Sx 0 Tx

View File

@@ -1,5 +1,4 @@
using System;
using System.Numerics;
using System.Numerics;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Graphics;
using Robust.Shared.Map;
@@ -14,29 +13,26 @@ namespace Robust.Client.Graphics
public interface IEyeManager
{
/// <summary>
/// The primary eye, which is usually the eye associated with the main viewport.
/// The current eye that is being used to render the game.
/// </summary>
/// <remarks>
/// Generally, you should avoid using this whenever possible. E.g., when rendering overlays should use the
/// eye & viewbounds that gets passed to the draw method.
/// Setting this property to null will use the default eye.
/// </remarks>
IEye CurrentEye { get; set; }
IViewportControl MainViewport { get; set; }
[Obsolete]
/// <summary>
/// The ID of the map on which the current eye is "placed".
/// </summary>
MapId CurrentMap { get; }
/// <summary>
/// A world-space box that is at LEAST the area covered by the main viewport.
/// A world-space box that is at LEAST the area covered by the viewport.
/// May be larger due to say rotation.
/// </summary>
Box2 GetWorldViewport();
/// <summary>
/// A world-space box of the area visible in the main viewport.
/// </summary>
Box2Rotated GetWorldViewbounds();
/// <summary>
@@ -44,10 +40,10 @@ namespace Robust.Client.Graphics
/// to UI screen space.
/// </summary>
/// <param name="projMatrix"></param>
void GetScreenProjectionMatrix(out Matrix3x2 projMatrix);
void GetScreenProjectionMatrix(out Matrix3 projMatrix);
/// <summary>
/// Projects a point from world space to UI screen space using the main viewport.
/// Projects a point from world space to UI screen space using the current camera.
/// </summary>
/// <param name="point">Point in world to transform.</param>
/// <returns>Corresponding point in UI screen space.</returns>

View File

@@ -367,7 +367,7 @@ namespace Robust.Client.Graphics.Clyde
_renderHandle.UseShader(entry.Sprite.PostShader);
CalcScreenMatrices(viewport.Size, out var proj, out var view);
_renderHandle.SetProjView(proj, view);
_renderHandle.SetModelTransform(Matrix3x2.Identity);
_renderHandle.SetModelTransform(Matrix3.Identity);
var rounded = roundedPos - entityPostRenderTarget.Size / 2;
@@ -480,7 +480,7 @@ namespace Robust.Client.Graphics.Clyde
var worldBounds = CalcWorldBounds(viewport);
var worldAABB = worldBounds.CalcBoundingBox();
if (eye.Position.MapId != MapId.Nullspace)
if (_eyeManager.CurrentMap != MapId.Nullspace)
{
using (DebugGroup("Lights"))
using (_prof.Group("Lights"))
@@ -530,7 +530,7 @@ namespace Robust.Client.Graphics.Clyde
// Because the math is wrong.
// So there are distortions from incorrect projection.
_renderHandle.UseShader(_fovDebugShaderInstance);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
var pos = UIBox2.FromDimensions(viewport.Size / 2 - new Vector2(200, 200), new Vector2(400, 400));
_renderHandle.DrawingHandleScreen.DrawTextureRect(FovTexture, pos);
}
@@ -538,18 +538,15 @@ namespace Robust.Client.Graphics.Clyde
if (DebugLayers == ClydeDebugLayers.Light)
{
_renderHandle.UseShader(null);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
_renderHandle.DrawingHandleScreen.DrawTextureRect(
viewport.WallBleedIntermediateRenderTarget2.Texture,
UIBox2.FromDimensions(Vector2.Zero, viewport.Size), new Color(1, 1, 1, 0.5f));
}
if (eye.Position.MapId != MapId.Nullspace)
using (_prof.Group("Overlays WS"))
{
using (_prof.Group("Overlays WS"))
{
RenderOverlays(viewport, OverlaySpace.WorldSpace, worldAABB, worldBounds);
}
RenderOverlays(viewport, OverlaySpace.WorldSpace, worldAABB, worldBounds);
}
_currentViewport = oldVp;

View File

@@ -98,34 +98,28 @@ namespace Robust.Client.Graphics.Clyde
[FieldOffset(20 * sizeof(float))] public Vector3 ViewMatrixC2;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ProjViewMatrices(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix)
public ProjViewMatrices(in Matrix3 projMatrix, in Matrix3 viewMatrix)
{
// We put the rows of the input matrix into the columns of our GPU matrices
// this transpose is required, as in C#, we premultiply vectors with matrices
// (vM) while GL postmultiplies vectors with matrices (Mv); however, since
// the Matrix3x2 data is stored row-major, and GL uses column-major, the
// memory layout is the same (or would be, if Matrix3x2 didn't have an
// implicit column)
ProjMatrixC0 = new Vector3(projMatrix.M11, projMatrix.M12, 0);
ProjMatrixC1 = new Vector3(projMatrix.M21, projMatrix.M22, 0);
ProjMatrixC2 = new Vector3(projMatrix.M31, projMatrix.M32, 1);
ProjMatrixC0 = new Vector3(projMatrix.R0C0, projMatrix.R1C0, projMatrix.R2C0);
ProjMatrixC1 = new Vector3(projMatrix.R0C1, projMatrix.R1C1, projMatrix.R2C1);
ProjMatrixC2 = new Vector3(projMatrix.R0C2, projMatrix.R1C2, projMatrix.R2C2);
ViewMatrixC0 = new Vector3(viewMatrix.M11, viewMatrix.M12, 0);
ViewMatrixC1 = new Vector3(viewMatrix.M21, viewMatrix.M22, 0);
ViewMatrixC2 = new Vector3(viewMatrix.M31, viewMatrix.M32, 1);
ViewMatrixC0 = new Vector3(viewMatrix.R0C0, viewMatrix.R1C0, viewMatrix.R2C0);
ViewMatrixC1 = new Vector3(viewMatrix.R0C1, viewMatrix.R1C1, viewMatrix.R2C1);
ViewMatrixC2 = new Vector3(viewMatrix.R0C2, viewMatrix.R1C2, viewMatrix.R2C2);
}
public void Apply(Clyde clyde, GLShaderProgram program)
{
program.SetUniformMaybe("projectionMatrix", new Matrix3x2(
ProjMatrixC0.X, ProjMatrixC0.Y, // Implicit 0
ProjMatrixC1.X, ProjMatrixC1.Y, // Implicit 0
ProjMatrixC2.X, ProjMatrixC2.Y // Implicit 1
program.SetUniformMaybe("projectionMatrix", new Matrix3(
ProjMatrixC0.X, ProjMatrixC1.X, ProjMatrixC2.X,
ProjMatrixC0.Y, ProjMatrixC1.Y, ProjMatrixC2.Y,
ProjMatrixC0.Z, ProjMatrixC1.Z, ProjMatrixC2.Z
));
program.SetUniformMaybe("viewMatrix", new Matrix3x2(
ViewMatrixC0.X, ViewMatrixC0.Y, // Implicit 0
ViewMatrixC1.X, ViewMatrixC1.Y, // Implicit 0
ViewMatrixC2.X, ViewMatrixC2.Y // Implicit 1
program.SetUniformMaybe("viewMatrix", new Matrix3(
ViewMatrixC0.X, ViewMatrixC1.X, ViewMatrixC2.X,
ViewMatrixC0.Y, ViewMatrixC1.Y, ViewMatrixC2.Y,
ViewMatrixC0.Z, ViewMatrixC1.Z, ViewMatrixC2.Z
));
}
}

View File

@@ -492,18 +492,18 @@ namespace Robust.Client.Graphics.Clyde
var offset = new Vector2(component.Radius, component.Radius);
Matrix3x2 matrix;
Matrix3 matrix;
if (mask == null)
{
matrix = Matrix3x2.Identity;
matrix = Matrix3.Identity;
}
else
{
// Only apply rotation if a mask is said, because else it doesn't matter.
matrix = Matrix3Helpers.CreateRotation(rotation);
matrix = Matrix3.CreateRotation(rotation);
}
(matrix.M31, matrix.M32) = lightPos;
(matrix.R0C2, matrix.R1C2) = lightPos;
_drawQuad(-offset, offset, matrix, lightShader);
}
@@ -692,7 +692,7 @@ namespace Robust.Client.Graphics.Clyde
// Blur horizontally to _wallBleedIntermediateRenderTarget1.
shader.SetUniformMaybe("direction", Vector2.UnitX);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.LightBlurTarget.Texture);
@@ -700,7 +700,7 @@ namespace Robust.Client.Graphics.Clyde
// Blur vertically to _wallBleedIntermediateRenderTarget2.
shader.SetUniformMaybe("direction", Vector2.UnitY);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.LightRenderTarget.Texture);
}
@@ -754,14 +754,14 @@ namespace Robust.Client.Graphics.Clyde
// Blur horizontally to _wallBleedIntermediateRenderTarget1.
shader.SetUniformMaybe("direction", Vector2.UnitX);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget1.Texture);
BindRenderTargetFull(viewport.WallBleedIntermediateRenderTarget2);
// Blur vertically to _wallBleedIntermediateRenderTarget2.
shader.SetUniformMaybe("direction", Vector2.UnitY);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.Identity, shader);
_drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader);
SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget2.Texture);
}
@@ -909,13 +909,13 @@ namespace Robust.Client.Graphics.Clyde
// Second modification is that output must be fov-centred (difference-space)
uZero -= fovCentre;
var clipToDiff = new Matrix3x2(uX.X, uX.Y, uY.X, uY.Y, uZero.X, uZero.Y);
var clipToDiff = new Matrix3(in uX, in uY, in uZero);
fovShader.SetUniformMaybe("clipToDiff", clipToDiff);
_drawQuad(Vector2.Zero, Vector2.One, Matrix3x2.Identity, fovShader);
_drawQuad(Vector2.Zero, Vector2.One, Matrix3.Identity, fovShader);
}
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3x2 eyeTransform)
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3 eyeTransform)
{
using var _ = _prof.Group("UpdateOcclusionGeometry");
using var _p = DebugGroup(nameof(UpdateOcclusionGeometry));
@@ -968,9 +968,9 @@ namespace Robust.Client.Graphics.Clyde
var worldTransform = xformSystem.GetWorldMatrix(transform, xforms);
var box = occluder.BoundingBox;
var tl = Vector2.Transform(box.TopLeft, worldTransform);
var tr = Vector2.Transform(box.TopRight, worldTransform);
var br = Vector2.Transform(box.BottomRight, worldTransform);
var tl = worldTransform.Transform(box.TopLeft);
var tr = worldTransform.Transform(box.TopRight);
var br = worldTransform.Transform(box.BottomRight);
var bl = tl + br - tr;
// Faces.
@@ -1010,9 +1010,9 @@ namespace Robust.Client.Graphics.Clyde
//
// Calculate delta positions from camera.
var dTl = Vector2.Transform(tl, eyeTransform);
var dTr = Vector2.Transform(tr, eyeTransform);
var dBl = Vector2.Transform(bl, eyeTransform);
var dTl = eyeTransform.Transform(tl);
var dTr = eyeTransform.Transform(tr);
var dBl = eyeTransform.Transform(bl);
var dBr = dBl + dTr - dTl;
// Get which neighbors are occluding.

View File

@@ -33,17 +33,17 @@ namespace Robust.Client.Graphics.Clyde
DrawingHandleWorld = new DrawingHandleWorldImpl(white, this);
}
public void SetModelTransform(in Matrix3x2 matrix)
public void SetModelTransform(in Matrix3 matrix)
{
_clyde.DrawSetModelTransform(matrix);
}
public Matrix3x2 GetModelTransform()
public Matrix3 GetModelTransform()
{
return _clyde.DrawGetModelTransform();
}
public void SetProjView(in Matrix3x2 proj, in Matrix3x2 view)
public void SetProjView(in Matrix3 proj, in Matrix3 view)
{
_clyde.DrawSetProjViewTransform(proj, view);
}
@@ -177,9 +177,9 @@ namespace Robust.Client.Graphics.Clyde
var oldModel = _clyde._currentMatrixModel;
var newModel = oldModel;
position += new Vector2(oldModel.M31, oldModel.M32);
newModel.M31 = 0;
newModel.M32 = 0;
position += new Vector2(oldModel.R0C2, oldModel.R1C2);
newModel.R0C2 = 0;
newModel.R1C2 = 0;
SetModelTransform(newModel);
// Switch rendering to pseudo-world space.
@@ -194,7 +194,7 @@ namespace Robust.Client.Graphics.Clyde
// Maaaaybe this is meant to have a minus sign.
var rot = -(float) eyeRot.Theta;
var view = Matrix3Helpers.CreateTransform(ofsX, ofsY, rot, scale.X, scale.Y);
var view = Matrix3.CreateTransform(ofsX, ofsY, rot, scale.X, scale.Y);
SetProjView(proj, view);
}
@@ -297,12 +297,12 @@ namespace Robust.Client.Graphics.Clyde
_renderHandle = renderHandle;
}
public override void SetTransform(in Matrix3x2 matrix)
public override void SetTransform(in Matrix3 matrix)
{
_renderHandle.SetModelTransform(matrix);
}
public override Matrix3x2 GetTransform()
public override Matrix3 GetTransform()
{
return _renderHandle.GetModelTransform();
}
@@ -402,12 +402,12 @@ namespace Robust.Client.Graphics.Clyde
_renderHandle = renderHandle;
}
public override void SetTransform(in Matrix3x2 matrix)
public override void SetTransform(in Matrix3 matrix)
{
_renderHandle.SetModelTransform(matrix);
}
public override Matrix3x2 GetTransform()
public override Matrix3 GetTransform()
{
return _renderHandle.GetModelTransform();
}

View File

@@ -62,7 +62,7 @@ namespace Robust.Client.Graphics.Clyde
// This matrix is applied to most normal geometry coming in.
// Some is applied while the batch is being created (e.g. simple texture draw calls).
// For DrawPrimitives OTOH the model matrix is passed along with the render command so is applied in the shader.
private Matrix3x2 _currentMatrixModel = Matrix3x2.Identity;
private Matrix3 _currentMatrixModel = Matrix3.Identity;
// Buffers and data for the batching system. Written into during (queue) and processed during (submit).
private readonly Vertex2D[] BatchVertexData = new Vertex2D[MaxBatchQuads * 4];
@@ -84,8 +84,8 @@ namespace Robust.Client.Graphics.Clyde
// Current projection & view matrices that are being used ot render.
// This gets updated to keep track during (queue) and (misc), but not during (submit).
private Matrix3x2 _currentMatrixProj;
private Matrix3x2 _currentMatrixView;
private Matrix3 _currentMatrixProj;
private Matrix3 _currentMatrixView;
// (queue) and (misc), current state of the scissor test. Null if disabled.
private UIBox2i? _currentScissorState;
@@ -110,25 +110,25 @@ namespace Robust.Client.Graphics.Clyde
UniformConstantsUBO.Reallocate(constants);
}
private void CalcScreenMatrices(in Vector2i screenSize, out Matrix3x2 proj, out Matrix3x2 view)
private void CalcScreenMatrices(in Vector2i screenSize, out Matrix3 proj, out Matrix3 view)
{
proj = Matrix3x2.Identity;
proj.M11 = 2f / screenSize.X;
proj.M22 = -2f / screenSize.Y;
proj.M31 = -1;
proj.M32 = 1;
proj = Matrix3.Identity;
proj.R0C0 = 2f / screenSize.X;
proj.R1C1 = -2f / screenSize.Y;
proj.R0C2 = -1;
proj.R1C2 = 1;
if (_currentRenderTarget.FlipY)
{
proj.M22 *= -1;
proj.M32 *= -1;
proj.R1C1 *= -1;
proj.R1C2 *= -1;
}
view = Matrix3x2.Identity;
view = Matrix3.Identity;
}
private void CalcWorldMatrices(in Vector2i screenSize, in Vector2 renderScale, IEye eye,
out Matrix3x2 proj, out Matrix3x2 view)
out Matrix3 proj, out Matrix3 view)
{
eye.GetViewMatrix(out view, renderScale);
@@ -136,20 +136,20 @@ namespace Robust.Client.Graphics.Clyde
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CalcWorldProjMatrix(in Vector2i screenSize, out Matrix3x2 proj)
private void CalcWorldProjMatrix(in Vector2i screenSize, out Matrix3 proj)
{
proj = Matrix3x2.Identity;
proj.M11 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
proj.M22 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
proj = Matrix3.Identity;
proj.R0C0 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
proj.R1C1 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
if (_currentRenderTarget.FlipY)
{
proj.M22 *= -1;
proj.M32 *= -1;
proj.R1C1 *= -1;
proj.R1C2 *= -1;
}
}
private void SetProjViewBuffer(in Matrix3x2 proj, in Matrix3x2 view)
private void SetProjViewBuffer(in Matrix3 proj, in Matrix3 view)
{
// TODO: Fix perf here.
// This immediately causes a glBufferData() call every time this is changed.
@@ -160,7 +160,7 @@ namespace Robust.Client.Graphics.Clyde
ProjViewUBO.Reallocate(combined);
}
private void SetProjViewFull(in Matrix3x2 proj, in Matrix3x2 view)
private void SetProjViewFull(in Matrix3 proj, in Matrix3 view)
{
_currentMatrixProj = proj;
_currentMatrixView = view;
@@ -285,21 +285,21 @@ namespace Robust.Client.Graphics.Clyde
};
}
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3x2 modelMatrix, GLShaderProgram program)
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3 modelMatrix, GLShaderProgram program)
{
DrawQuadWithVao(QuadVAO, a, b, modelMatrix, program);
}
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3x2 modelMatrix,
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3 modelMatrix,
GLShaderProgram program)
{
BindVertexArray(vao.Handle);
CheckGlError();
var rectTransform = Matrix3x2.Identity;
(rectTransform.M11, rectTransform.M22) = b - a;
(rectTransform.M31, rectTransform.M32) = a;
rectTransform = rectTransform * modelMatrix;
var rectTransform = Matrix3.Identity;
(rectTransform.R0C0, rectTransform.R1C1) = b - a;
(rectTransform.R0C2, rectTransform.R1C2) = a;
rectTransform.Multiply(modelMatrix);
program.SetUniformMaybe(UniIModelMatrix, rectTransform);
_debugStats.LastGLDrawCalls += 1;
@@ -315,7 +315,7 @@ namespace Robust.Client.Graphics.Clyde
FlushBatchQueue();
// Reset renderer state.
_currentMatrixModel = Matrix3x2.Identity;
_currentMatrixModel = Matrix3.Identity;
_queuedShaderInstance = _defaultShader;
SetScissorFull(null);
}
@@ -496,7 +496,7 @@ namespace Robust.Client.Graphics.Clyde
case bool b:
program.SetUniform(name, b ? 1 : 0);
break;
case Matrix3x2 matrix3:
case Matrix3 matrix3:
program.SetUniform(name, matrix3);
break;
case Matrix4 matrix4:
@@ -532,17 +532,17 @@ namespace Robust.Client.Graphics.Clyde
return ref command;
}
private void DrawSetModelTransform(in Matrix3x2 matrix)
private void DrawSetModelTransform(in Matrix3 matrix)
{
_currentMatrixModel = matrix;
}
private Matrix3x2 DrawGetModelTransform()
private Matrix3 DrawGetModelTransform()
{
return _currentMatrixModel;
}
private void DrawSetProjViewTransform(in Matrix3x2 proj, in Matrix3x2 view)
private void DrawSetProjViewTransform(in Matrix3 proj, in Matrix3 view)
{
BreakBatch();
@@ -571,9 +571,9 @@ namespace Robust.Client.Graphics.Clyde
EnsureBatchSpaceAvailable(4, GetQuadBatchIndexCount());
EnsureBatchState(texture, true, GetQuadBatchPrimitiveType(), _queuedShader);
bl = Vector2.Transform(bl, _currentMatrixModel);
br = Vector2.Transform(br, _currentMatrixModel);
tr = Vector2.Transform(tr, _currentMatrixModel);
bl = _currentMatrixModel.Transform(bl);
br = _currentMatrixModel.Transform(br);
tr = _currentMatrixModel.Transform(tr);
tl = tr + bl - br;
// TODO: split batch if necessary.
@@ -676,8 +676,8 @@ namespace Robust.Client.Graphics.Clyde
EnsureBatchSpaceAvailable(2, 0);
EnsureBatchState(_stockTextureWhite.TextureId, false, BatchPrimitiveType.LineList, _queuedShader);
a = Vector2.Transform(a, _currentMatrixModel);
b = Vector2.Transform(b, _currentMatrixModel);
a = _currentMatrixModel.Transform(a);
b = _currentMatrixModel.Transform(b);
// TODO: split batch if necessary.
var vIdx = BatchVertexIndex;
@@ -807,7 +807,7 @@ namespace Robust.Client.Graphics.Clyde
command.DrawBatch.ShaderInstance = metaData.ShaderInstance;
command.DrawBatch.Count = currentIndex - metaData.StartIndex;
command.DrawBatch.ModelMatrix = Matrix3x2.Identity;
command.DrawBatch.ModelMatrix = Matrix3.Identity;
_debugStats.LastBatches += 1;
}
@@ -882,7 +882,7 @@ namespace Robust.Client.Graphics.Clyde
_queuedRenderCommands.Clear();
_currentViewport = null;
_lightingReady = false;
_currentMatrixModel = Matrix3x2.Identity;
_currentMatrixModel = Matrix3.Identity;
SetScissorFull(null);
BindRenderTargetFull(_mainWindow!.RenderTarget);
_batchMetaData = null;
@@ -961,13 +961,13 @@ namespace Robust.Client.Graphics.Clyde
public BatchPrimitiveType PrimitiveType;
// TODO: this makes the render commands so much more large please remove.
public Matrix3x2 ModelMatrix;
public Matrix3 ModelMatrix;
}
private struct RenderCommandProjViewMatrix
{
public Matrix3x2 ProjMatrix;
public Matrix3x2 ViewMatrix;
public Matrix3 ProjMatrix;
public Matrix3 ViewMatrix;
}
private struct RenderCommandScissor
@@ -1056,11 +1056,11 @@ namespace Robust.Client.Graphics.Clyde
private readonly struct FullStoredRendererState
{
public readonly Matrix3x2 ProjMatrix;
public readonly Matrix3x2 ViewMatrix;
public readonly Matrix3 ProjMatrix;
public readonly Matrix3 ViewMatrix;
public readonly LoadedRenderTarget RenderTarget;
public FullStoredRendererState(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix,
public FullStoredRendererState(in Matrix3 projMatrix, in Matrix3 viewMatrix,
LoadedRenderTarget renderTarget)
{
ProjMatrix = projMatrix;

View File

@@ -506,7 +506,7 @@ namespace Robust.Client.Graphics.Clyde
data.Parameters[name] = value;
}
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
private protected override void SetParameterImpl(string name, in Matrix3 value)
{
var data = Parent._shaderInstances[Handle];
data.ParametersDirty = true;

View File

@@ -664,7 +664,7 @@ namespace Robust.Client.Graphics.Clyde
fixed (byte* p = buffer)
{
GL.GetTexImage(TextureTarget.Texture2D, 0, PF.Rgba, PT.UnsignedByte, (IntPtr) p);
GL.GetnTexImage(TextureTarget.Texture2D, 0, PF.Rgba, PT.UnsignedByte, bufSize, (IntPtr) p);
}
GL.BindTexture(TextureTarget.Texture2D, curTexture2D);

View File

@@ -52,7 +52,7 @@ namespace Robust.Client.Graphics.Clyde
// view matrix
vp.Eye.GetViewMatrixInv(out var viewMatrixInv, vp.RenderScale);
point = Vector2.Transform(point, viewMatrixInv);
point = viewMatrixInv * point;
return point;
}
@@ -136,7 +136,7 @@ namespace Robust.Client.Graphics.Clyde
// view matrix
Eye.GetViewMatrixInv(out var viewMatrixInv, RenderScale);
newPoint = Vector2.Transform(newPoint, viewMatrixInv);
newPoint = viewMatrixInv * newPoint;
return new MapCoordinates(newPoint, Eye.Position.MapId);
}
@@ -150,7 +150,7 @@ namespace Robust.Client.Graphics.Clyde
var newPoint = point;
eye.GetViewMatrix(out var viewMatrix, RenderScale);
newPoint = Vector2.Transform(newPoint, viewMatrix);
newPoint = viewMatrix * newPoint;
// (inlined version of UiProjMatrix)
newPoint *= new Vector2(1, -1) * EyeManager.PixelsPerMeter;
@@ -159,14 +159,14 @@ namespace Robust.Client.Graphics.Clyde
return newPoint;
}
public Matrix3x2 GetWorldToLocalMatrix()
public Matrix3 GetWorldToLocalMatrix()
{
if (Eye == null)
return Matrix3x2.Identity;
return Matrix3.Identity;
Eye.GetViewMatrix(out var viewMatrix, RenderScale * new Vector2(EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter));
viewMatrix.M31 += Size.X / 2f;
viewMatrix.M32 += Size.Y / 2f;
viewMatrix.R0C2 += Size.X / 2f;
viewMatrix.R1C2 += Size.Y / 2f;
return viewMatrix;
}

View File

@@ -361,7 +361,7 @@ namespace Robust.Client.Graphics.Clyde
{
}
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
private protected override void SetParameterImpl(string name, in Matrix3 value)
{
}
@@ -482,7 +482,7 @@ namespace Robust.Client.Graphics.Clyde
return default;
}
public Matrix3x2 GetWorldToLocalMatrix() => default;
public Matrix3 GetWorldToLocalMatrix() => default;
public Vector2 WorldToLocal(Vector2 point)
{

View File

@@ -247,33 +247,28 @@ namespace Robust.Client.Graphics.Clyde
GL.Uniform1(uniformId, singles.Length, singles);
}
public void SetUniform(string uniformName, in Matrix3x2 matrix)
public void SetUniform(string uniformName, in Matrix3 matrix)
{
var uniformId = GetUniform(uniformName);
SetUniformDirect(uniformId, matrix);
}
public void SetUniform(int uniformName, in Matrix3x2 matrix)
public void SetUniform(int uniformName, in Matrix3 matrix)
{
var uniformId = GetUniform(uniformName);
SetUniformDirect(uniformId, matrix);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe void SetUniformDirect(int slot, in Matrix3x2 value)
private unsafe void SetUniformDirect(int slot, in Matrix3 value, bool transpose=true)
{
// We put the rows of the input matrix into the columns of our GPU matrices
// this transpose is required, as in C#, we premultiply vectors with matrices
// (vM) while GL postmultiplies vectors with matrices (Mv); however, since
// the Matrix3x2 data is stored row-major, and GL uses column-major, the
// memory layout is the same (or would be, if Matrix3x2 didn't have an
// implicit column)
var buf = stackalloc float[9]{
value.M11, value.M12, 0,
value.M21, value.M22, 0,
value.M31, value.M32, 1
};
GL.UniformMatrix3(slot, 1, false, (float*)buf);
Matrix3 tmpTranspose = value;
if (transpose)
{
// transposition not supported on GLES2, & no access to _hasGLES
tmpTranspose.Transpose();
}
GL.UniformMatrix3(slot, 1, false, (float*) &tmpTranspose);
_clyde.CheckGlError();
}
@@ -479,7 +474,7 @@ namespace Robust.Client.Graphics.Clyde
}
}
public void SetUniformMaybe(string uniformName, in Matrix3x2 value)
public void SetUniformMaybe(string uniformName, in Matrix3 value)
{
if (TryGetUniform(uniformName, out var slot))
{
@@ -495,7 +490,7 @@ namespace Robust.Client.Graphics.Clyde
}
}
public void SetUniformMaybe(int uniformName, in Matrix3x2 value)
public void SetUniformMaybe(int uniformName, in Matrix3 value)
{
if (TryGetUniform(uniformName, out var slot))
{

View File

@@ -44,19 +44,19 @@ namespace Robust.Client.Graphics
{
CheckDisposed();
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation, in scale);
var matrix = Matrix3.CreateTransform(in position, in rotation, in scale);
SetTransform(in matrix);
}
public void SetTransform(in Vector2 position, in Angle rotation)
{
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation);
var matrix = Matrix3.CreateTransform(in position, in rotation);
SetTransform(in matrix);
}
public abstract void SetTransform(in Matrix3x2 matrix);
public abstract void SetTransform(in Matrix3 matrix);
public abstract Matrix3x2 GetTransform();
public abstract Matrix3 GetTransform();
public abstract void UseShader(ShaderInstance? shader);

View File

@@ -49,7 +49,7 @@ namespace Robust.Client.Graphics
/// <summary>
/// Matrix equivalent of <see cref="LocalToWorld(Vector2)"/>.
/// </summary>
Matrix3x2 GetWorldToLocalMatrix();
Matrix3 GetWorldToLocalMatrix();
/// <summary>
/// Converts a point in world-space to the viewport's screen coordinates.

View File

@@ -22,7 +22,7 @@ namespace Robust.Client.Graphics
/// If set to true, <see cref="ScreenTexture"/> will be set to the current frame (at the moment before the overlay is rendered). This can be costly to performance, but
/// some shaders will require it as a passed in uniform to operate.
/// </summary>
public virtual bool RequestScreenTexture { get; set; } = false;
public virtual bool RequestScreenTexture => false;
/// <summary>
/// If <see cref="RequestScreenTexture"> is true, then this will be set to the texture corresponding to the current frame. If false, it will always be null.

View File

@@ -148,7 +148,7 @@ namespace Robust.Client.Graphics
SetParameterImpl(name, value);
}
public void SetParameter(string name, in Matrix3x2 value)
public void SetParameter(string name, in Matrix3 value)
{
EnsureAlive();
EnsureMutable();
@@ -219,7 +219,7 @@ namespace Robust.Client.Graphics
private protected abstract void SetParameterImpl(string name, int value);
private protected abstract void SetParameterImpl(string name, Vector2i value);
private protected abstract void SetParameterImpl(string name, bool value);
private protected abstract void SetParameterImpl(string name, in Matrix3x2 value);
private protected abstract void SetParameterImpl(string name, in Matrix3 value);
private protected abstract void SetParameterImpl(string name, in Matrix4 value);
private protected abstract void SetParameterImpl(string name, Texture value);
private protected abstract void SetStencilImpl(StencilParameters value);

View File

@@ -176,7 +176,7 @@ namespace Robust.Client.Graphics
return node.AsVector4();
}
case ShaderDataType.Mat3:
return node.AsMatrix3x2();
return node.AsMatrix3();
case ShaderDataType.Mat4:
return node.AsMatrix4();
default:
@@ -219,7 +219,7 @@ namespace Robust.Client.Graphics
case bool i:
instance.SetParameter(key, i);
break;
case Matrix3x2 i:
case Matrix3 i:
instance.SetParameter(key, i);
break;
case Matrix4 i:

View File

@@ -346,7 +346,7 @@ namespace Robust.Client.Input
{
if (binding.CanRepeat)
{
return SetBindState(binding, BoundKeyState.Down, uiOnly, isRepeat);
return SetBindState(binding, BoundKeyState.Down, uiOnly);
}
return true;
@@ -375,7 +375,7 @@ namespace Robust.Client.Input
SetBindState(binding, BoundKeyState.Up);
}
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false, bool isRepeat = false)
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false)
{
if (binding.BindingType == KeyBindingType.Command && state == BoundKeyState.Down)
{
@@ -387,7 +387,6 @@ namespace Robust.Client.Input
// I honestly have no idea what the best solution here is.
// note from the future: context switches won't cause re-entrancy anymore because InputContextContainer defers context switches
DebugTools.Assert(!_currentlyFindingViewport, "Re-entrant key events??");
DebugTools.Assert(!isRepeat || binding.CanRepeat);
try
{
@@ -400,7 +399,7 @@ namespace Robust.Client.Input
binding.State = state;
var eventArgs = new BoundKeyEventArgs(binding.Function, binding.State,
MouseScreenPosition, binding.CanFocus, isRepeat);
MouseScreenPosition, binding.CanFocus);
// UI returns true here into blockPass if it wants to prevent us from giving input events
// to the viewport, but doesn't want it hard-handled so we keep processing possible key actions.

View File

@@ -120,6 +120,6 @@ public sealed class TileEdgeOverlay : GridOverlay
}
}
args.WorldHandle.SetTransform(Matrix3x2.Identity);
args.WorldHandle.SetTransform(Matrix3.Identity);
}
}

View File

@@ -139,7 +139,7 @@ namespace Robust.Client.Physics
return true;
}, true);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.SetTransform(Matrix3.Identity);
}
}
}

View File

@@ -2,7 +2,6 @@ using System.Buffers;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;

View File

@@ -19,7 +19,7 @@ namespace Robust.Client.Placement.Modes
public SnapgridCenter(PlacementManager pMan) : base(pMan) { }
public override void Render(in OverlayDrawArgs args)
public override void Render(DrawingHandleWorld handle)
{
if (Grid != null)
{
@@ -34,18 +34,18 @@ namespace Robust.Client.Placement.Modes
{
var from = ScreenToWorld(new Vector2(a, 0));
var to = ScreenToWorld(new Vector2(a, viewportSize.Y));
args.WorldHandle.DrawLine(from, to, new Color(0, 0, 1f));
handle.DrawLine(from, to, new Color(0, 0, 1f));
}
for (var a = gridstart.Y; a < viewportSize.Y; a += SnapSize * EyeManager.PixelsPerMeter)
{
var from = ScreenToWorld(new Vector2(0, a));
var to = ScreenToWorld(new Vector2(viewportSize.X, a));
args.WorldHandle.DrawLine(from, to, new Color(0, 0, 1f));
handle.DrawLine(from, to, new Color(0, 0, 1f));
}
}
// Draw grid BELOW the ghost thing.
base.Render(args);
base.Render(handle);
}
public override void AlignPlacementMode(ScreenCoordinates mouseScreen)

View File

@@ -628,20 +628,20 @@ namespace Robust.Client.Placement
return true;
}
private void Render(in OverlayDrawArgs args)
private void Render(DrawingHandleWorld handle)
{
if (CurrentMode == null || !IsActive)
{
if (EraserRect.HasValue)
{
args.WorldHandle.UseShader(_drawingShader);
args.WorldHandle.DrawRect(EraserRect.Value, new Color(255, 0, 0, 50));
args.WorldHandle.UseShader(null);
handle.UseShader(_drawingShader);
handle.DrawRect(EraserRect.Value, new Color(255, 0, 0, 50));
handle.UseShader(null);
}
return;
}
CurrentMode.Render(args);
CurrentMode.Render(handle);
if (CurrentPermission is not {Range: > 0} ||
!CurrentMode.RangeRequired ||
@@ -650,7 +650,7 @@ namespace Robust.Client.Placement
var worldPos = EntityManager.GetComponent<TransformComponent>(controlled).WorldPosition;
args.WorldHandle.DrawCircle(worldPos, CurrentPermission.Range, new Color(1, 1, 1, 0.25f));
handle.DrawCircle(worldPos, CurrentPermission.Range, new Color(1, 1, 1, 0.25f));
}
private void HandleStartPlacement(MsgPlacement msg)

View File

@@ -88,7 +88,7 @@ namespace Robust.Client.Placement
/// <returns></returns>
public abstract bool IsValidPosition(EntityCoordinates position);
public virtual void Render(in OverlayDrawArgs args)
public virtual void Render(DrawingHandleWorld handle)
{
var uid = pManager.CurrentPlacementOverlayEntity;
if (!pManager.EntityManager.TryGetComponent(uid, out SpriteComponent? sprite) || !sprite.Visible)
@@ -125,8 +125,7 @@ namespace Robust.Client.Placement
var worldRot = pManager.EntityManager.GetComponent<TransformComponent>(coordinate.EntityId).WorldRotation + dirAng;
sprite.Color = IsValidPosition(coordinate) ? ValidPlaceColor : InvalidPlaceColor;
var rot = args.Viewport.Eye?.Rotation ?? default;
spriteSys.Render(uid.Value, sprite, args.WorldHandle, rot, worldRot, worldPos);
spriteSys.Render(uid.Value, sprite, handle, pManager.EyeManager.CurrentEye.Rotation, worldRot, worldPos);
}
}

View File

@@ -19,7 +19,7 @@ namespace Robust.Client.Placement
protected internal override void Draw(in OverlayDrawArgs args)
{
_manager.Render(args);
_manager.Render(args.WorldHandle);
}
}
}

View File

@@ -261,8 +261,8 @@ namespace Robust.Client.Player
{
// This is a new userid, so we create a new session.
DebugTools.Assert(state.UserId != LocalPlayer?.UserId);
var newSession = (ICommonSessionInternal)CreateAndAddSession(state.UserId, state.Name);
newSession.SetPing(state.Ping);
var newSession = (CommonSession) CreateAndAddSession(state.UserId, state.Name);
newSession.Ping = state.Ping;
SetStatus(newSession, state.Status);
SetAttachedEntity(newSession, controlled, out _, true);
dirty = true;
@@ -279,9 +279,9 @@ namespace Robust.Client.Player
}
dirty = true;
var local = (ICommonSessionInternal)session;
local.SetName(state.Name);
local.SetPing(state.Ping);
var local = (CommonSession) session;
local.Name = state.Name;
local.Ping = state.Ping;
SetStatus(local, state.Status);
SetAttachedEntity(local, controlled, out _, true);
}

View File

@@ -20,31 +20,6 @@ namespace Robust.Client.Replays.Loading;
// so that when jumping to tick 1001 the client only has to apply states for tick 1000 and 1001, instead of 0, 1, 2, ...
public sealed partial class ReplayLoadManager
{
// Scratch data used by UpdateEntityStates.
// Avoids copying changes for every change to an entity between checkpoints, instead copies once per checkpoint on
// first change. We can also use this to avoid building a dictionary of ComponentChange inside the inner loop.
private class UpdateScratchData
{
public Dictionary<ushort, ComponentChange> Changes;
public EntityState lastChange;
public HashSet<ushort>? netComps;
public UpdateScratchData(EntityState oldEntState)
{
Changes = oldEntState.ComponentChanges.Value.ToDictionary(x => x.NetID);
lastChange = oldEntState;
netComps = oldEntState.NetComponents;
}
public EntityState BakeChanges()
{
return new EntityState(lastChange.NetEntity,
Changes.Values.ToList(),
lastChange.EntityLastModified,
netComps);
}
}
public async Task<(CheckpointState[], TimeSpan[])> GenerateCheckpointsAsync(
ReplayMessage? initMessages,
HashSet<string> initialCvars,
@@ -158,12 +133,6 @@ public sealed partial class ReplayLoadManager
var spawnedTracker = 0;
var stateTracker = 0;
var curState = state0;
var stats_due_ticks = 0;
var stats_due_spawned = 0;
var stats_due_state = 0;
var modifiedEntities = new Dictionary<NetEntity, UpdateScratchData>();
for (var i = 1; i < states.Count; i++)
{
if (i % 10 == 0)
@@ -174,42 +143,23 @@ public sealed partial class ReplayLoadManager
DebugTools.Assert(curState.FromSequence <= lastState.ToSequence);
UpdatePlayerStates(curState.PlayerStates.Span, playerStates);
UpdateEntityStates(curState.EntityStates.Span, entStates, modifiedEntities, ref spawnedTracker, ref stateTracker, detached);
UpdateEntityStates(curState.EntityStates.Span, entStates, ref spawnedTracker, ref stateTracker, detached);
UpdateMessages(messages[i], uploadedFiles, prototypes, cvars, detachQueue, ref timeBase);
ProcessQueue(curState.ToSequence, detachQueue, detached, entStates);
UpdateDeletions(curState.EntityDeletions, entStates, detached, modifiedEntities);
UpdateDeletions(curState.EntityDeletions, entStates, detached);
serverTime[i] = GetTime(curState.ToSequence) - initialTime;
ticksSinceLastCheckpoint++;
// Don't create checkpoints too frequently no matter the circumstance
if (ticksSinceLastCheckpoint < _checkpointMinInterval)
continue;
// Check if enough time, spawned entities or changed states have occurred.
if (ticksSinceLastCheckpoint < _checkpointInterval
&& spawnedTracker < _checkpointEntitySpawnThreshold
&& stateTracker < _checkpointEntityStateThreshold)
{
continue;
// Track and update statistics about why checkpoints are getting created:
if (ticksSinceLastCheckpoint >= _checkpointInterval)
{
stats_due_ticks += 1;
}
else if (spawnedTracker >= _checkpointEntitySpawnThreshold)
{
stats_due_spawned += 1;
}
else if (stateTracker >= _checkpointEntityStateThreshold)
{
stats_due_state += 1;
}
ticksSinceLastCheckpoint = 0;
spawnedTracker = 0;
stateTracker = 0;
ApplyModifiedEntities(entStates, modifiedEntities);
var newState = new GameState(GameTick.Zero,
curState.ToSequence,
default,
@@ -219,8 +169,7 @@ public sealed partial class ReplayLoadManager
checkPoints.Add(new CheckpointState(newState, timeBase, cvars, i, detached));
}
_sawmill.Info($"Finished generating {checkPoints.Count} checkpoints. Elapsed time: {st.Elapsed}. Checkpoint every {(float)states.Count / checkPoints.Count} ticks on average");
_sawmill.Info($"Checkpoint stats - Spawning: {stats_due_spawned} StateChanges: {stats_due_state} Ticks: {stats_due_ticks}. ");
_sawmill.Info($"Finished generating checkpoints. Elapsed time: {st.Elapsed}");
await callback(states.Count, states.Count, LoadingState.ProcessingFiles, false);
return (checkPoints.ToArray(), serverTime);
}
@@ -367,18 +316,16 @@ public sealed partial class ReplayLoadManager
}
private void UpdateDeletions(NetListAsArray<NetEntity> entityDeletions,
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached, Dictionary<NetEntity, UpdateScratchData> modifiedEntities)
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached)
{
foreach (var ent in entityDeletions.Span)
{
entStates.Remove(ent);
detached.Remove(ent);
modifiedEntities.Remove(ent);
}
}
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<NetEntity, EntityState> entStates,
Dictionary<NetEntity, UpdateScratchData> modified,
ref int spawnedTracker, ref int stateTracker, HashSet<NetEntity> detached)
{
foreach (var entState in span)
@@ -393,85 +340,25 @@ public sealed partial class ReplayLoadManager
#if DEBUG
foreach (var state in modifiedState.ComponentChanges.Value)
{
DebugTools.Assert(state.State is not IComponentDeltaState delta);
DebugTools.Assert(state.State is not IComponentDeltaState delta || delta.FullState);
}
#endif
continue;
}
// Get scratch versions (with write access) for entities modified since last checkpoint
UpdateScratchData? scratch;
if (!modified.TryGetValue(entState.NetEntity, out scratch))
{
scratch = new UpdateScratchData(oldEntState);
modified[entState.NetEntity] = scratch;
}
stateTracker++;
DebugTools.Assert(oldEntState.NetEntity == entState.NetEntity);
// Note this does not change entStates, that change occurs later in ApplyModifiedEntities (to avoid early copies)
UpdateScratch(entState, scratch.Changes);
if (entState.NetComponents != null)
scratch.netComps = entState.NetComponents;
scratch.lastChange = entState;
entStates[entState.NetEntity] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
#if DEBUG
foreach (var state in entStates[entState.NetEntity].ComponentChanges.Span)
{
DebugTools.Assert(state.State is not IComponentDeltaState delta);
DebugTools.Assert(state.State is not IComponentDeltaState delta || delta.FullState);
}
#endif
}
}
private void ApplyModifiedEntities(Dictionary<NetEntity, EntityState> entStates, Dictionary<NetEntity, UpdateScratchData> modifiedEntities)
{
foreach (var modified in modifiedEntities)
{
entStates[modified.Key] = modified.Value.BakeChanges();
}
modifiedEntities.Clear();
}
private void UpdateScratch(
EntityState newState,
Dictionary<ushort, ComponentChange> oldState)
{
// remove any deleted components
if (newState.NetComponents != null)
{
foreach (var change in oldState.Values)
{
if (!newState.NetComponents.Contains(change.NetID))
oldState.Remove(change.NetID);
}
}
foreach (var newCompState in newState.ComponentChanges.Value)
{
if (!oldState.TryGetValue(newCompState.NetID, out var existing))
{
// This is a new component
// I'm not 100% sure about this, but I think delta states should always be full states here?
DebugTools.Assert(newCompState.State is not IComponentDeltaState newDelta);
oldState[newCompState.NetID] = newCompState;
continue;
}
// Modify or replace existing component
if (newCompState.State is not IComponentDeltaState delta)
{
oldState[newCompState.NetID] = newCompState;
continue;
}
DebugTools.Assert(existing.State != null && existing.State is not IComponentDeltaState);
oldState[newCompState.NetID] = new ComponentChange(existing.NetID, delta.CreateNewFullState(existing.State!), newCompState.LastModifiedTick);
}
}
private EntityState MergeStates(
EntityState newState,
IReadOnlyCollection<ComponentChange> oldState,
@@ -497,20 +384,20 @@ public sealed partial class ReplayLoadManager
if (!newCompStates.Remove(existing.NetID, out var newCompState))
continue;
if (newCompState.State is not IComponentDeltaState delta)
if (newCompState.State is not IComponentDeltaState delta || delta.FullState)
{
combined[index] = newCompState;
continue;
}
DebugTools.Assert(existing.State != null && existing.State is not IComponentDeltaState);
combined[index] = new ComponentChange(existing.NetID, delta.CreateNewFullState(existing.State!), newCompState.LastModifiedTick);
DebugTools.Assert(existing.State is IComponentDeltaState fullDelta && fullDelta.FullState);
combined[index] = new ComponentChange(existing.NetID, delta.CreateNewFullState(existing.State), newCompState.LastModifiedTick);
}
foreach (var compChange in newCompStates.Values)
{
// I'm not 100% sure about this, but I think delta states should always be full states here?
DebugTools.Assert(compChange.State is not IComponentDeltaState);
DebugTools.Assert(compChange.State is not IComponentDeltaState delta || delta.FullState);
combined.Add(compChange);
}

View File

@@ -45,7 +45,7 @@ public sealed partial class ReplayLoadManager
continue;
var state = _entMan.GetComponentState(_entMan.EventBus, component, null, GameTick.Zero);
DebugTools.Assert(state is not IComponentDeltaState);
DebugTools.Assert(state is not IComponentDeltaState delta || delta.FullState);
list.Add(new ComponentChange(netId, state, GameTick.Zero));
set.Add(netId);
}
@@ -61,7 +61,7 @@ public sealed partial class ReplayLoadManager
{
if (comp.NetID == _metaId)
{
var state = (MetaDataComponentState) comp.State!;
var state = (MetaDataComponentState) comp.State;
return state.PrototypeId;
}
}

View File

@@ -162,7 +162,9 @@ public sealed partial class ReplayLoadManager
}
using var stringFile = fileReader.Open(FileStrings);
_serializer.SetStringSerializerPackage(stringHash, stringFile.CopyToArray());
var stringData = new byte[stringFile.Length];
stringFile.ReadExactly(stringData);
_serializer.SetStringSerializerPackage(stringHash, stringData);
using var cvarsFile = fileReader.Open(FileCvars);
// Note, this does not invoke the received-initial-cvars event. But at least currently, that doesn't matter

View File

@@ -34,7 +34,6 @@ public sealed partial class ReplayLoadManager : IReplayLoadManager
private ushort _metaId;
private bool _initialized;
private int _checkpointInterval;
private int _checkpointMinInterval;
private int _checkpointEntitySpawnThreshold;
private int _checkpointEntityStateThreshold;
private ISawmill _sawmill = default!;
@@ -46,7 +45,6 @@ public sealed partial class ReplayLoadManager : IReplayLoadManager
_initialized = true;
_confMan.OnValueChanged(CVars.CheckpointInterval, value => _checkpointInterval = value, true);
_confMan.OnValueChanged(CVars.CheckpointMinInterval, value => _checkpointMinInterval = value, true);
_confMan.OnValueChanged(CVars.CheckpointEntitySpawnThreshold, value => _checkpointEntitySpawnThreshold = value,
true);
_confMan.OnValueChanged(CVars.CheckpointEntityStateThreshold, value => _checkpointEntityStateThreshold = value,

View File

@@ -17,7 +17,7 @@ internal sealed partial class ReplayPlaybackManager
/// </summary>
/// <param name="index">The target tick/index. The actual checkpoint will have an index less than or equal to this.</param>
/// <param name="flushEntities">Whether to delete all entities</param>
public void ResetToNearestCheckpoint(int index, bool flushEntities, CheckpointState? checkpoint = null)
public void ResetToNearestCheckpoint(int index, bool flushEntities)
{
if (Replay == null)
throw new Exception("Not currently playing a replay");
@@ -25,8 +25,7 @@ internal sealed partial class ReplayPlaybackManager
if (flushEntities)
_entMan.FlushEntities();
// Look up the desired checkpoint, unless our caller kindly provided one to us.
checkpoint ??= GetLastCheckpoint(Replay, index);
var checkpoint = GetLastCheckpoint(Replay, index);
_sawmill.Info($"Resetting to checkpoint. From {Replay.CurrentIndex} to {checkpoint.Index}");
var st = new Stopwatch();

View File

@@ -1,7 +1,6 @@
using System;
using Robust.Client.GameObjects;
using Robust.Client.GameStates;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Client.Replays.Playback;
@@ -28,35 +27,27 @@ internal sealed partial class ReplayPlaybackManager
return;
}
Playing &= !pausePlayback;
value = Math.Clamp(value, 0, Replay.States.Count - 1);
if (value == Replay.CurrentIndex)
{
ScrubbingTarget = null;
return;
}
BeforeSetTick?.Invoke();
// Begin timing replay processing so we can abort when we run out of time (_replayMaxScrubTime)
var st = RStopwatch.StartNew();
bool skipEffectEvents = value > Replay.CurrentIndex + _visualEventThreshold;
if (value < Replay.CurrentIndex)
{
skipEffectEvents = true;
ResetToNearestCheckpoint(value, false);
}
else if (value > Replay.CurrentIndex + _checkpointMinInterval)
else if (value > Replay.CurrentIndex + _checkpointInterval)
{
// If we are skipping many ticks into the future, we try to skip directly to a checkpoint instead of
// applying every tick.
var nextCheckpoint = GetLastCheckpoint(Replay, value);
// Sanity-Check that the checkpoint is actually BEFORE the desired position.
// Also check that it gets us closer to goal position than we already are.
if (nextCheckpoint.Index <= value && nextCheckpoint.Index > Replay.CurrentIndex)
ResetToNearestCheckpoint(value, false, nextCheckpoint);
var nextCheckpoint = GetNextCheckpoint(Replay, Replay.CurrentIndex);
if (nextCheckpoint.Index < value)
ResetToNearestCheckpoint(value, false);
}
_entMan.EntitySysManager.GetEntitySystem<ClientDirtySystem>().Reset();
@@ -84,23 +75,6 @@ internal sealed partial class ReplayPlaybackManager
DebugTools.Assert(Replay.LastApplied >= state.FromSequence);
DebugTools.Assert(Replay.LastApplied + 1 <= state.ToSequence);
Replay.LastApplied = state.ToSequence;
if (st.Elapsed.TotalMilliseconds > _replayMaxScrubTime)
{
// Out of time to advance replay this tick
// Note: We check at end of loop so we always advance at least 1 tick.
break;
}
}
// Use ScrubbingTarget to force a later invocation to continue moving towards the target tick
if (Replay.CurrentIndex < value)
{
ScrubbingTarget = value;
}
else
{
ScrubbingTarget = null;
}
AfterSetTick?.Invoke();

View File

@@ -51,8 +51,7 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
public ReplayData? Replay { get; private set; }
public NetUserId? Recorder => Replay?.Recorder;
private int _checkpointMinInterval;
private int _replayMaxScrubTime;
private int _checkpointInterval;
private int _visualEventThreshold;
public uint? AutoPauseCountdown { get; set; }
public int? ScrubbingTarget { get; set; }
@@ -94,8 +93,7 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
_initialized = true;
_sawmill = _logMan.GetSawmill("replay");
_metaId = _factory.GetRegistration(typeof(MetaDataComponent)).NetID!.Value;
_confMan.OnValueChanged(CVars.CheckpointMinInterval, (value) => _checkpointMinInterval = value, true);
_confMan.OnValueChanged(CVars.ReplayMaxScrubTime, (value) => _replayMaxScrubTime = value, true);
_confMan.OnValueChanged(CVars.CheckpointInterval, (value) => _checkpointInterval = value, true);
_confMan.OnValueChanged(CVars.ReplaySkipThreshold, (value) => _visualEventThreshold = value, true);
_client.RunLevelChanged += OnRunLevelChanged;
}

View File

@@ -47,7 +47,7 @@ namespace Robust.Client.ResourceManagement
{
sawmill.Debug("Preloading textures...");
var sw = Stopwatch.StartNew();
var resList = GetTypeData<TextureResource>().Resources;
var resList = GetTypeDict<TextureResource>();
var texList = _manager.ContentFindFiles("/Textures/")
// Skip PNG files inside RSIs.
@@ -119,7 +119,7 @@ namespace Robust.Client.ResourceManagement
private void PreloadRsis(ISawmill sawmill)
{
var sw = Stopwatch.StartNew();
var resList = GetTypeData<RSIResource>().Resources;
var resList = GetTypeDict<RSIResource>();
var rsiList = _manager.ContentFindFiles("/Textures/")
.Where(p => p.ToString().EndsWith(".rsi/meta.json"))

View File

@@ -17,7 +17,9 @@ namespace Robust.Client.ResourceManagement;
/// </summary>
internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInternal, IDisposable
{
private readonly Dictionary<Type, TypeData> _cachedResources = new();
private readonly Dictionary<Type, Dictionary<ResPath, BaseResource>> _cachedResources =
new();
private readonly Dictionary<Type, BaseResource> _fallbacks = new();
public T GetResource<T>(string path, bool useFallback = true) where T : BaseResource, new()
@@ -27,8 +29,8 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
public T GetResource<T>(ResPath path, bool useFallback = true) where T : BaseResource, new()
{
var cache = GetTypeData<T>();
if (cache.Resources.TryGetValue(path, out var cached))
var cache = GetTypeDict<T>();
if (cache.TryGetValue(path, out var cached))
{
return (T) cached;
}
@@ -38,7 +40,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
{
var dependencies = IoCManager.Instance!;
resource.Load(dependencies, path);
cache.Resources[path] = resource;
cache[path] = resource;
return resource;
}
catch (Exception e)
@@ -65,31 +67,24 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
public bool TryGetResource<T>(ResPath path, [NotNullWhen(true)] out T? resource) where T : BaseResource, new()
{
var cache = GetTypeData<T>();
if (cache.Resources.TryGetValue(path, out var cached))
var cache = GetTypeDict<T>();
if (cache.TryGetValue(path, out var cached))
{
resource = (T) cached;
return true;
}
if (cache.NonExistent.Contains(path))
{
resource = null;
return false;
}
var _resource = new T();
try
{
var dependencies = IoCManager.Instance!;
_resource.Load(dependencies, path);
resource = _resource;
cache.Resources[path] = resource;
cache[path] = resource;
return true;
}
catch (FileNotFoundException)
{
cache.NonExistent.Add(path);
resource = null;
return false;
}
@@ -114,9 +109,9 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
public void ReloadResource<T>(ResPath path) where T : BaseResource, new()
{
var cache = GetTypeData<T>();
var cache = GetTypeDict<T>();
if (!cache.Resources.TryGetValue(path, out var res))
if (!cache.TryGetValue(path, out var res))
{
return;
}
@@ -150,7 +145,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
public void CacheResource<T>(ResPath path, T resource) where T : BaseResource, new()
{
GetTypeData<T>().Resources[path] = resource;
GetTypeDict<T>()[path] = resource;
}
public T GetFallback<T>() where T : BaseResource, new()
@@ -173,7 +168,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
public IEnumerable<KeyValuePair<ResPath, T>> GetAllResources<T>() where T : BaseResource, new()
{
return GetTypeData<T>().Resources.Select(p => new KeyValuePair<ResPath, T>(p.Key, (T) p.Value));
return GetTypeDict<T>().Select(p => new KeyValuePair<ResPath, T>(p.Key, (T) p.Value));
}
public event Action<TextureLoadedEventArgs>? OnRawTextureLoaded;
@@ -198,7 +193,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
if (disposing)
{
foreach (var res in _cachedResources.Values.SelectMany(dict => dict.Resources.Values))
foreach (var res in _cachedResources.Values.SelectMany(dict => dict.Values))
{
res.Dispose();
}
@@ -215,9 +210,15 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
#endregion IDisposable Members
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private TypeData GetTypeData<T>()
protected Dictionary<ResPath, BaseResource> GetTypeDict<T>()
{
return _cachedResources.GetOrNew(typeof(T));
if (!_cachedResources.TryGetValue(typeof(T), out var ret))
{
ret = new Dictionary<ResPath, BaseResource>();
_cachedResources.Add(typeof(T), ret);
}
return ret;
}
public void TextureLoaded(TextureLoadedEventArgs eventArgs)
@@ -229,13 +230,4 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
{
OnRsiLoaded?.Invoke(eventArgs);
}
private sealed class TypeData
{
public readonly Dictionary<ResPath, BaseResource> Resources = new();
// List of resources which DON'T exist.
// Needed to avoid innocuous TryGet calls repeatedly trying to re-load non-existent resources from disk.
public readonly HashSet<ResPath> NonExistent = new();
}
}

View File

@@ -212,18 +212,9 @@ namespace Robust.Client.UserInterface
}
}
/// <summary>
/// Called when this control's visibility in the control tree changed.
/// </summary>
protected virtual void VisibilityChanged(bool newVisible)
{
}
private void _propagateVisibilityChanged(bool newVisible)
{
VisibilityChanged(newVisible);
OnVisibilityChanged?.Invoke(this);
if (!VisibleInTree)
{
UserInterfaceManagerInternal.ControlHidden(this);
@@ -576,7 +567,7 @@ namespace Robust.Client.UserInterface
public Vector2i Position;
public Color Modulate;
public UIBox2i? ScissorBox;
public ref Matrix3x2 CoordinateTransform;
public ref Matrix3 CoordinateTransform;
}
protected void RenderControl(ref ControlRenderArguments args, int childIndex, Vector2i position)

View File

@@ -130,7 +130,7 @@ public sealed class TileSpawningUIController : UIController
_window.TileList.Clear();
IEnumerable<ITileDefinition> tileDefs = _tiles.Where(def => !def.EditorHidden);
IEnumerable<ITileDefinition> tileDefs = _tiles;
if (!string.IsNullOrEmpty(searchStr))
{

View File

@@ -15,36 +15,6 @@ namespace Robust.Client.UserInterface.Controls
public Label Label { get; }
public TextureRect TextureRect { get; }
/// <summary>
/// Should the checkbox be to the left or the right of the label.
/// </summary>
public bool LeftAlign
{
get => _leftAlign;
set
{
if (_leftAlign == value)
return;
_leftAlign = value;
if (value)
{
Label.HorizontalExpand = false;
TextureRect.SetPositionFirst();
Label.SetPositionInParent(1);
}
else
{
Label.HorizontalExpand = true;
Label.SetPositionFirst();
TextureRect.SetPositionInParent(1);
}
}
}
private bool _leftAlign = true;
public CheckBox()
{
ToggleMode = true;
@@ -61,21 +31,10 @@ namespace Robust.Client.UserInterface.Controls
StyleClasses = { StyleClassCheckBox },
VerticalAlignment = VAlignment.Center,
};
hBox.AddChild(TextureRect);
Label = new Label();
if (LeftAlign)
{
Label.HorizontalExpand = false;
hBox.AddChild(TextureRect);
hBox.AddChild(Label);
}
else
{
Label.HorizontalExpand = true;
hBox.AddChild(Label);
hBox.AddChild(TextureRect);
}
hBox.AddChild(Label);
}
protected override void DrawModeChanged()

View File

@@ -1,4 +1,3 @@
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.IoC;
@@ -21,19 +20,19 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
/// <summary>
/// Base background colour.
/// </summary>
public Robust.Shared.Maths.Vector4 BaseColor;
public Vector4 BaseColor;
/// <summary>
/// Colour to add to the background colour along the X-axis.
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
/// </summary>
public Robust.Shared.Maths.Vector4 XAxis;
public Vector4 XAxis;
/// <summary>
/// Colour to add to the background colour along the y-axis.
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
/// </summary>
public Robust.Shared.Maths.Vector4 YAxis;
public Vector4 YAxis;
/// <summary>
/// If true, then <see cref="BaseColor"/>, <see cref="XAxis"/>, and <see cref="YAxis"/> will be interpreted as HSVa
@@ -54,7 +53,7 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
var old = handle.GetShader();
handle.UseShader(_shader);
var globalPixelPos = Vector2.Transform(default, handle.GetTransform());
var globalPixelPos = handle.GetTransform().Transform(default);
_shader.SetParameter("size", box.Size);
_shader.SetParameter("offset", globalPixelPos);
_shader.SetParameter("xAxis", XAxis);
@@ -93,14 +92,14 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
{
var colorData = Hsv
? Color.ToHsv(color)
: new Robust.Shared.Maths.Vector4(color.R, color.G, color.B, color.A);
: new Vector4(color.R, color.G, color.B, color.A);
SetBaseColor(colorData);
}
/// <summary>
/// Helper method that sets the base color by taking in some color and removing the components that are controlled by the x and y axes.
/// </summary>
public void SetBaseColor(Robust.Shared.Maths.Vector4 colorData)
public void SetBaseColor(Vector4 colorData)
{
BaseColor = colorData - colorData * XAxis - colorData * YAxis;
}

View File

@@ -15,8 +15,6 @@ namespace Robust.Client.UserInterface.Controls
public const string StylePseudoClassHover = "hover";
public const string StylePseudoClassDisabled = "disabled";
public StyleBox? StyleBoxOverride { get; set; }
public ContainerButton()
{
DrawModeChanged();
@@ -26,11 +24,6 @@ namespace Robust.Client.UserInterface.Controls
{
get
{
if (StyleBoxOverride != null)
{
return StyleBoxOverride;
}
if (TryGetStyleProperty<StyleBox>(StylePropertyStyleBox, out var box))
{
return box;

View File

@@ -1,64 +0,0 @@
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
namespace Robust.Client.UserInterface.Controls;
[Virtual]
public class EntityPrototypeView : SpriteView
{
private string? _currentPrototype;
private EntityUid? _ourEntity;
public EntityPrototypeView()
{
}
public EntityPrototypeView(EntProtoId? entProto, IEntityManager entMan) : base(entMan)
{
SetPrototype(entProto);
}
public void SetPrototype(EntProtoId? entProto)
{
SpriteSystem ??= EntMan.System<SpriteSystem>();
if (entProto == _currentPrototype
&& EntMan.TryGetComponent(Entity?.Owner, out MetaDataComponent? meta)
&& meta.EntityPrototype?.ID == _currentPrototype)
{
return;
}
_currentPrototype = entProto;
SetEntity(null);
if (_ourEntity != null)
{
EntMan.DeleteEntity(_ourEntity);
}
if (_currentPrototype != null)
{
_ourEntity = EntMan.Spawn(_currentPrototype);
SpriteSystem.ForceUpdate(_ourEntity.Value);
SetEntity(_ourEntity);
}
}
protected override void EnteredTree()
{
base.EnteredTree();
if (_currentPrototype != null)
SetPrototype(_currentPrototype);
}
protected override void ExitedTree()
{
base.ExitedTree();
if (!EntMan.Deleted(_ourEntity))
EntMan.QueueDeleteEntity(_ourEntity);
}
}

View File

@@ -53,7 +53,7 @@ namespace Robust.Client.UserInterface.Controls
private TimeSpan? _lastClickTime;
private Vector2? _lastClickPosition;
private bool IsPlaceHolderVisible => !(HidePlaceHolderOnFocus && HasKeyboardFocus()) && string.IsNullOrEmpty(_text) && _placeHolder != null;
private bool IsPlaceHolderVisible => string.IsNullOrEmpty(_text) && _placeHolder != null;
public event Action<LineEditEventArgs>? OnTextChanged;
public event Action<LineEditEventArgs>? OnTextEntered;
@@ -186,8 +186,6 @@ namespace Robust.Client.UserInterface.Controls
public int SelectionLower => Math.Min(_selectionStart, _cursorPosition);
public int SelectionUpper => Math.Max(_selectionStart, _cursorPosition);
public bool HidePlaceHolderOnFocus { get; set; }
public bool IgnoreNext { get; set; }
private (int start, int length)? _imeData;

View File

@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.RichText;
using Robust.Shared.Collections;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -19,7 +20,7 @@ namespace Robust.Client.UserInterface.Controls
public const string StylePropertyStyleBox = "stylebox";
private readonly RingBufferList<RichTextEntry> _entries = new();
private readonly List<RichTextEntry> _entries = new();
private bool _isAtBottom = true;
private int _totalContentHeight;
@@ -29,8 +30,6 @@ namespace Robust.Client.UserInterface.Controls
public bool ScrollFollowing { get; set; } = true;
private bool _invalidOnVisible;
public OutputPanel()
{
IoCManager.InjectDependencies(this);
@@ -46,8 +45,6 @@ namespace Robust.Client.UserInterface.Controls
_scrollBar.OnValueChanged += _ => _isAtBottom = _scrollBar.IsAtEnd;
}
public int EntryCount => _entries.Count;
public StyleBox? StyleBoxOverride
{
get => _styleBoxOverride;
@@ -94,7 +91,7 @@ namespace Robust.Client.UserInterface.Controls
{
var entry = new RichTextEntry(message, this, _tagManager, null);
entry.Update(_tagManager, _getFont(), _getContentBox().Width, UIScale);
entry.Update(_getFont(), _getContentBox().Width, UIScale);
_entries.Add(entry);
var font = _getFont();
@@ -137,7 +134,7 @@ namespace Robust.Client.UserInterface.Controls
// So when a new color tag gets hit this stack gets the previous color pushed on.
var context = new MarkupDrawingContext(2);
foreach (ref var entry in _entries)
foreach (ref var entry in CollectionsMarshal.AsSpan(_entries))
{
if (entryOffset + entry.Height < 0)
{
@@ -150,7 +147,7 @@ namespace Robust.Client.UserInterface.Controls
break;
}
entry.Draw(_tagManager, handle, font, contentBox, entryOffset, context, UIScale);
entry.Draw(handle, font, contentBox, entryOffset, context, UIScale);
entryOffset += entry.Height + font.GetLineSeparation(UIScale);
}
@@ -188,9 +185,9 @@ namespace Robust.Client.UserInterface.Controls
_totalContentHeight = 0;
var font = _getFont();
var sizeX = _getContentBox().Width;
foreach (ref var entry in _entries)
foreach (ref var entry in CollectionsMarshal.AsSpan(_entries))
{
entry.Update(_tagManager, font, sizeX, UIScale);
entry.Update(font, sizeX, UIScale);
_totalContentHeight += entry.Height + font.GetLineSeparation(UIScale);
}
@@ -242,13 +239,7 @@ namespace Robust.Client.UserInterface.Controls
protected internal override void UIScaleChanged()
{
// If this control isn't visible, don't invalidate entries immediately.
// This saves invalidating the debug console if it's hidden,
// which is a huge boon as auto-scaling changes UI scale a lot in that scenario.
if (!VisibleInTree)
_invalidOnVisible = true;
else
_invalidateEntries();
_invalidateEntries();
base.UIScaleChanged();
}
@@ -266,14 +257,5 @@ namespace Robust.Client.UserInterface.Controls
// existing ones were valid when the UI scale was set.
_invalidateEntries();
}
protected override void VisibilityChanged(bool newVisible)
{
if (newVisible && _invalidOnVisible)
{
_invalidateEntries();
_invalidOnVisible = false;
}
}
}
}

View File

@@ -68,7 +68,7 @@ namespace Robust.Client.UserInterface.Controls
}
var font = _getFont();
_entry.Update(_tagManager, font, availableSize.X * UIScale, UIScale, LineHeightScale);
_entry.Update(font, availableSize.X * UIScale, UIScale, LineHeightScale);
return new Vector2(_entry.Width / UIScale, _entry.Height / UIScale);
}
@@ -82,7 +82,7 @@ namespace Robust.Client.UserInterface.Controls
return;
}
_entry.Draw(_tagManager, handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale, LineHeightScale);
_entry.Draw(handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale, LineHeightScale);
}
[Pure]

View File

@@ -1,3 +1,4 @@
using Robust.Shared.Maths;
using System;
using System.Collections.Generic;
using System.Numerics;
@@ -14,10 +15,9 @@ namespace Robust.Client.UserInterface.Controls
public const string RightButtonStyle = "spinbox-right";
public const string MiddleButtonStyle = "spinbox-middle";
public LineEdit LineEditControl { get; }
private List<SpinBoxButton> _leftButtons = new();
private List<SpinBoxButton> _rightButtons = new();
private List<Button> _leftButtons = new();
private List<Button> _rightButtons = new();
private int _stepSize = 1;
private bool _buttonsDisabled;
/// <summary>
/// Determines whether the SpinBox value gets changed by the input text.
@@ -30,7 +30,12 @@ namespace Robust.Client.UserInterface.Controls
get => _value;
set
{
OverrideValue(value);
if (IsValid != null && !IsValid(value))
{
return;
}
_value = value;
LineEditControl.Text = value.ToString();
ValueChanged?.Invoke(new ValueChangedEventArgs(value));
}
}
@@ -47,7 +52,6 @@ namespace Robust.Client.UserInterface.Controls
return;
}
_value = value;
UpdateButtonCanPress();
LineEditControl.Text = value.ToString();
}
@@ -83,7 +87,6 @@ namespace Robust.Client.UserInterface.Controls
ClearButtons();
AddLeftButton(-1, "-");
AddRightButton(1, "+");
UpdateButtonCanPress();
}
/// <summary>
@@ -91,8 +94,8 @@ namespace Robust.Client.UserInterface.Controls
/// </summary>
public void AddRightButton(int num, string text)
{
var button = new SpinBoxButton(num) { Text = text };
button.OnPressed += _ => Value += num;
var button = new Button { Text = text };
button.OnPressed += (args) => Value += num;
AddChild(button);
button.AddStyleClass(RightButtonStyle);
if (_rightButtons.Count > 0)
@@ -108,8 +111,8 @@ namespace Robust.Client.UserInterface.Controls
/// </summary>
public void AddLeftButton(int num, string text)
{
var button = new SpinBoxButton(num) { Text = text };
button.OnPressed += _ => Value += num;
var button = new Button { Text = text };
button.OnPressed += (args) => Value += num;
AddChild(button);
button.SetPositionInParent(_leftButtons.Count);
button.AddStyleClass(_leftButtons.Count == 0 ? LeftButtonStyle : MiddleButtonStyle);
@@ -159,24 +162,6 @@ namespace Robust.Client.UserInterface.Controls
{
rightButton.Disabled = disabled;
}
_buttonsDisabled = disabled;
}
private void UpdateButtonCanPress()
{
if (IsValid == null)
return;
foreach (var button in _leftButtons)
{
button.Disabled = !IsValid(_value + button.Value) || _buttonsDisabled;
}
foreach (var button in _rightButtons)
{
button.Disabled = !IsValid(_value + button.Value) || _buttonsDisabled;
}
}
/// <summary>
@@ -210,16 +195,6 @@ namespace Robust.Client.UserInterface.Controls
else if (args.Delta.Y < 0)
Value -= _stepSize;
}
private sealed class SpinBoxButton : Button
{
public readonly int Value;
public SpinBoxButton(int value)
{
Value = value;
}
}
}
public sealed class ValueChangedEventArgs : EventArgs

View File

@@ -14,9 +14,9 @@ namespace Robust.Client.UserInterface.Controls
[Virtual]
public class SpriteView : Control
{
protected SpriteSystem? SpriteSystem;
private SpriteSystem? _sprite;
private SharedTransformSystem? _transform;
protected readonly IEntityManager EntMan;
private readonly IEntityManager _entMan;
[ViewVariables]
public SpriteComponent? Sprite => Entity?.Comp1;
@@ -120,26 +120,20 @@ namespace Robust.Client.UserInterface.Controls
public SpriteView()
{
IoCManager.Resolve(ref EntMan);
RectClipContent = true;
}
public SpriteView(IEntityManager entMan)
{
EntMan = entMan;
IoCManager.Resolve(ref _entMan);
RectClipContent = true;
}
public SpriteView(EntityUid? uid, IEntityManager entMan)
{
EntMan = entMan;
_entMan = entMan;
RectClipContent = true;
SetEntity(uid);
}
public SpriteView(NetEntity uid, IEntityManager entMan)
{
EntMan = entMan;
_entMan = entMan;
RectClipContent = true;
SetEntity(uid);
}
@@ -160,8 +154,8 @@ namespace Robust.Client.UserInterface.Controls
if (Entity?.Owner == uid)
return;
if (!EntMan.TryGetComponent(uid, out SpriteComponent? sprite)
|| !EntMan.TryGetComponent(uid, out TransformComponent? xform))
if (!_entMan.TryGetComponent(uid, out SpriteComponent? sprite)
|| !_entMan.TryGetComponent(uid, out TransformComponent? xform))
{
Entity = null;
NetEnt = null;
@@ -169,7 +163,7 @@ namespace Robust.Client.UserInterface.Controls
}
Entity = new(uid.Value, sprite, xform);
NetEnt = EntMan.GetNetEntity(uid);
NetEnt = _entMan.GetNetEntity(uid);
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
@@ -229,11 +223,11 @@ namespace Robust.Client.UserInterface.Controls
if (!ResolveEntity(out var uid, out var sprite, out var xform))
return;
SpriteSystem ??= EntMan.System<SpriteSystem>();
_transform ??= EntMan.System<TransformSystem>();
_sprite ??= _entMan.System<SpriteSystem>();
_transform ??= _entMan.System<TransformSystem>();
// Ensure the sprite is animated despite possible not being visible in any viewport.
SpriteSystem.ForceUpdate(uid);
_sprite.ForceUpdate(uid);
var stretchVec = Stretch switch
{
@@ -264,13 +258,13 @@ namespace Robust.Client.UserInterface.Controls
[NotNullWhen(true)] out SpriteComponent? sprite,
[NotNullWhen(true)] out TransformComponent? xform)
{
if (NetEnt != null && Entity == null && EntMan.TryGetEntity(NetEnt, out var ent))
if (NetEnt != null && Entity == null && _entMan.TryGetEntity(NetEnt, out var ent))
SetEntity(ent);
if (Entity != null)
{
(uid, sprite, xform) = Entity.Value;
return !EntMan.Deleted(uid);
return !_entMan.Deleted(uid);
}
sprite = null;

View File

@@ -65,10 +65,6 @@ namespace Robust.Client.UserInterface.Controls
}
}
public StyleBox? PanelStyleBoxOverride { get; set; }
public Color? TabFontColorOverride { get; set; }
public Color? TabFontColorInactiveOverride { get; set; }
public event Action<int>? OnTabChanged;
public TabContainer()
@@ -365,9 +361,6 @@ namespace Robust.Client.UserInterface.Controls
[System.Diagnostics.Contracts.Pure]
private Color _getTabFontColorActive()
{
if (TabFontColorOverride != null)
return TabFontColorOverride.Value;
if (TryGetStyleProperty(stylePropertyTabFontColor, out Color color))
{
return color;
@@ -378,9 +371,6 @@ namespace Robust.Client.UserInterface.Controls
[System.Diagnostics.Contracts.Pure]
private Color _getTabFontColorInactive()
{
if (TabFontColorInactiveOverride != null)
return TabFontColorInactiveOverride.Value;
if (TryGetStyleProperty(StylePropertyTabFontColorInactive, out Color color))
{
return color;
@@ -391,9 +381,6 @@ namespace Robust.Client.UserInterface.Controls
[System.Diagnostics.Contracts.Pure]
private StyleBox? _getPanel()
{
if (PanelStyleBoxOverride != null)
return PanelStyleBoxOverride;
TryGetStyleProperty<StyleBox>(StylePropertyPanelStyleBox, out var box);
return box;
}

View File

@@ -13,12 +13,6 @@ namespace Robust.Client.UserInterface.Controls
}
public override float UIScale => UIScaleSet;
internal float UIScaleSet { get; set; }
/// <summary>
/// Set after the window is resized, to batch up UI scale updates on window resizes.
/// </summary>
internal bool UIScaleUpdateNeeded { get; set; }
public override IClydeWindow Window { get; }
/// <summary>

View File

@@ -277,21 +277,8 @@ public sealed partial class DebugConsole
CommandBar.CursorPosition = lastRange.end;
CommandBar.SelectionStart = lastRange.start;
var insertValue = CommandParsing.Escape(completion);
// If the replacement contains a space, we must quote it to treat it as a single argument.
var mustQuote = insertValue.Contains(' ');
if ((completionFlags & CompletionOptionFlags.PartialCompletion) == 0)
{
if (mustQuote)
insertValue = $"\"{insertValue}\"";
insertValue += " ";
}
else if (mustQuote)
{
// If it's a partial completion, only quote the start.
insertValue = '"' + insertValue;
}
CommandBar.InsertAtCursor(insertValue);

View File

@@ -7,7 +7,6 @@ using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Input;
@@ -52,8 +51,6 @@ namespace Robust.Client.UserInterface.CustomControls
private readonly ConcurrentQueue<FormattedMessage> _messageQueue = new();
private readonly ISawmill _logger;
private int _maxEntries;
public DebugConsole()
{
RobustXamlLoader.Load(this);
@@ -81,7 +78,6 @@ namespace Robust.Client.UserInterface.CustomControls
_consoleHost.AddString += OnAddString;
_consoleHost.AddFormatted += OnAddFormatted;
_consoleHost.ClearText += OnClearText;
_cfg.OnValueChanged(CVars.ConMaxEntries, MaxEntriesChanged, true);
UserInterfaceManager.ModalRoot.AddChild(_compPopup);
}
@@ -93,17 +89,10 @@ namespace Robust.Client.UserInterface.CustomControls
_consoleHost.AddString -= OnAddString;
_consoleHost.AddFormatted -= OnAddFormatted;
_consoleHost.ClearText -= OnClearText;
_cfg.UnsubValueChanged(CVars.ConMaxEntries, MaxEntriesChanged);
UserInterfaceManager.ModalRoot.RemoveChild(_compPopup);
}
private void MaxEntriesChanged(int value)
{
_maxEntries = value;
TrimExtraOutputEntries();
}
private void OnClearText(object? _, EventArgs args)
{
Clear();
@@ -176,15 +165,6 @@ namespace Robust.Client.UserInterface.CustomControls
private void _addFormattedLineInternal(FormattedMessage message)
{
Output.AddMessage(message);
TrimExtraOutputEntries();
}
private void TrimExtraOutputEntries()
{
while (Output.EntryCount > _maxEntries)
{
Output.RemoveEntry(0);
}
}
private void _flushQueue()

View File

@@ -43,11 +43,11 @@ namespace Robust.Client.UserInterface.CustomControls
/// <remarks>
/// This is generally just be a combination of <see cref="IClydeViewport.GetWorldToLocalMatrix"/> and <see cref="GetLocalToScreenMatrix"/>
/// </remarks>
Matrix3x2 GetWorldToScreenMatrix();
Matrix3 GetWorldToScreenMatrix();
/// <summary>
/// Returns a matrix that can be used to transform from view-port local to screen coordinates.
/// </summary>
Matrix3x2 GetLocalToScreenMatrix();
Matrix3 GetLocalToScreenMatrix();
}
}

View File

@@ -155,17 +155,17 @@ namespace Robust.Client.UserInterface.CustomControls
return WorldToLocalPixel(point) + GlobalPixelPosition;
}
public Matrix3x2 GetWorldToScreenMatrix()
public Matrix3 GetWorldToScreenMatrix()
{
if (Viewport == null)
return Matrix3x2.Identity;
return Matrix3.Identity;
return Viewport.GetWorldToLocalMatrix() * GetLocalToScreenMatrix();
}
public Matrix3x2 GetLocalToScreenMatrix()
public Matrix3 GetLocalToScreenMatrix()
{
return Matrix3Helpers.CreateTransform(GlobalPixelPosition, 0, Vector2.One / _viewportResolution);
return Matrix3.CreateTransform(GlobalPixelPosition, 0, Vector2.One / _viewportResolution);
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Robust.Client.UserInterface
return Task.FromResult<Stream?>(null);
}
public Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null, bool truncate = true)
public Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null)
{
return Task.FromResult<(Stream fileStream, bool alreadyExisted)?>(null);
}

View File

@@ -51,7 +51,7 @@ namespace Robust.Client.UserInterface
return await OpenFileNfd(filters);
}
public async Task<(Stream, bool)?> SaveFile(FileDialogFilters? filters, bool truncate = true)
public async Task<(Stream, bool)?> SaveFile(FileDialogFilters? filters)
{
var name = await GetSaveFileName(filters);
if (name == null)
@@ -61,7 +61,7 @@ namespace Robust.Client.UserInterface
try
{
return (File.Open(name, truncate ? FileMode.Truncate : FileMode.Open), true);
return (File.Open(name, FileMode.Open), true);
}
catch (FileNotFoundException)
{

View File

@@ -28,7 +28,6 @@ namespace Robust.Client.UserInterface
/// The file stream the user chose to save to, and whether the file already existed.
/// Null if the user cancelled the action.
/// </returns>
/// <param name="truncate">Should we truncate an existing file to 0-size then write or append.</param>
Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null, bool truncate = true);
Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null);
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.Input;
@@ -63,7 +63,7 @@ namespace Robust.Client.UserInterface
Color GetMainClearColor();
void RenderControl(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
UIBox2i? scissorBox, Matrix3x2 coordinateTransform);
UIBox2i? scissorBox, Matrix3 coordinateTransform);
}
}

View File

@@ -17,6 +17,7 @@ namespace Robust.Client.UserInterface
internal struct RichTextEntry
{
private readonly Color _defaultColor;
private readonly MarkupTagManager _tagManager;
private readonly Type[]? _tagsAllowed;
public readonly FormattedMessage Message;
@@ -36,7 +37,7 @@ namespace Robust.Client.UserInterface
/// </summary>
public ValueList<int> LineBreaks;
private readonly Dictionary<int, Control>? _tagControls;
private readonly Dictionary<int, Control> _tagControls = new();
public RichTextEntry(FormattedMessage message, Control parent, MarkupTagManager tagManager, Type[]? tagsAllowed = null, Color? defaultColor = null)
{
@@ -45,26 +46,23 @@ namespace Robust.Client.UserInterface
Width = 0;
LineBreaks = default;
_defaultColor = defaultColor ?? new(200, 200, 200);
_tagManager = tagManager;
_tagsAllowed = tagsAllowed;
Dictionary<int, Control>? tagControls = null;
var nodeIndex = -1;
foreach (var node in Message)
foreach (var node in Message.Nodes)
{
nodeIndex++;
if (node.Name == null)
continue;
if (!tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag) || !tag.TryGetControl(node, out var control))
if (!_tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag) || !tag.TryGetControl(node, out var control))
continue;
parent.Children.Add(control);
tagControls ??= new Dictionary<int, Control>();
tagControls.Add(nodeIndex, control);
_tagControls.Add(nodeIndex, control);
}
_tagControls = tagControls;
}
/// <summary>
@@ -74,7 +72,7 @@ namespace Robust.Client.UserInterface
/// <param name="maxSizeX">The maximum horizontal size of the container of this entry.</param>
/// <param name="uiScale"></param>
/// <param name="lineHeightScale"></param>
public void Update(MarkupTagManager tagManager, Font defaultFont, float maxSizeX, float uiScale, float lineHeightScale = 1)
public void Update(Font defaultFont, float maxSizeX, float uiScale, float lineHeightScale = 1)
{
// This method is gonna suck due to complexity.
// Bear with me here.
@@ -93,10 +91,10 @@ namespace Robust.Client.UserInterface
// Nodes can change the markup drawing context and return additional text.
// It's also possible for nodes to return inline controls. They get treated as one large rune.
var nodeIndex = -1;
foreach (var node in Message)
foreach (var node in Message.Nodes)
{
nodeIndex++;
var text = ProcessNode(tagManager, node, context);
var text = ProcessNode(node, context);
if (!context.Font.TryPeek(out var font))
font = defaultFont;
@@ -115,7 +113,7 @@ namespace Robust.Client.UserInterface
return;
}
if (_tagControls == null || !_tagControls.TryGetValue(nodeIndex, out var control))
if (!_tagControls.TryGetValue(nodeIndex, out var control))
continue;
if (ProcessRune(ref this, new Rune(' '), out breakLine))
@@ -168,7 +166,6 @@ namespace Robust.Client.UserInterface
}
public readonly void Draw(
MarkupTagManager tagManager,
DrawingHandleScreen handle,
Font defaultFont,
UIBox2 drawBox,
@@ -187,10 +184,10 @@ namespace Robust.Client.UserInterface
var controlYAdvance = 0f;
var nodeIndex = -1;
foreach (var node in Message)
foreach (var node in Message.Nodes)
{
nodeIndex++;
var text = ProcessNode(tagManager, node, context);
var text = ProcessNode(node, context);
if (!context.Color.TryPeek(out var color) || !context.Font.TryPeek(out var font))
{
color = _defaultColor;
@@ -213,7 +210,7 @@ namespace Robust.Client.UserInterface
globalBreakCounter += 1;
}
if (_tagControls == null || !_tagControls.TryGetValue(nodeIndex, out var control))
if (!_tagControls.TryGetValue(nodeIndex, out var control))
continue;
var invertedScale = 1f / uiScale;
@@ -226,22 +223,24 @@ namespace Robust.Client.UserInterface
}
}
private readonly string ProcessNode(MarkupTagManager tagManager, MarkupNode node, MarkupDrawingContext context)
private readonly string ProcessNode(MarkupNode node, MarkupDrawingContext context)
{
// If a nodes name is null it's a text node.
if (node.Name == null)
return node.Value.StringValue ?? "";
//Skip the node if there is no markup tag for it.
if (!tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag))
if (!_tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag))
return "";
if (!node.Closing)
{
context.Tags.Add(tag);
tag.PushDrawContext(node, context);
return tag.TextBefore(node);
}
context.Tags.Remove(tag);
tag.PopDrawContext(node, context);
return tag.TextAfter(node);
}

View File

@@ -114,10 +114,8 @@ internal partial class UserInterfaceManager
args.Handle();
}
// Attempt to ensure that keybind-up events get raised after a keybind-down.
DebugTools.Assert(!_focusedControls.TryGetValue(args.Function, out var existing)
|| !existing.VisibleInTree
|| args.IsRepeat && existing == control);
// Attempt to ensure that keybind-up events only get raised after a single keybind-down.
DebugTools.Assert(!_focusedControls.ContainsKey(args.Function));
_focusedControls[args.Function] = control;
OnKeyBindDown?.Invoke(control);
@@ -126,7 +124,7 @@ internal partial class UserInterfaceManager
public void KeyBindUp(BoundKeyEventArgs args)
{
// Only raise keybind-up for the control on which we previously raised keybind-down
if (!_focusedControls.Remove(args.Function, out var control) || !control.VisibleInTree)
if (!_focusedControls.Remove(args.Function, out var control) || control.Disposed)
return;
var guiArgs = new GUIBoundKeyEventArgs(args.Function, args.State, args.PointerLocation, args.CanFocus,

View File

@@ -130,9 +130,9 @@ internal sealed partial class UserInterfaceManager
{
var total = 0;
var drawingHandle = renderHandle.DrawingHandleScreen;
drawingHandle.SetTransform(Matrix3x2.Identity);
RenderControl(renderHandle, ref total, root, Vector2i.Zero, Color.White, null, Matrix3x2.Identity);
drawingHandle.SetTransform(Matrix3x2.Identity);
drawingHandle.SetTransform(Matrix3.Identity);
RenderControl(renderHandle, ref total, root, Vector2i.Zero, Color.White, null, Matrix3.Identity);
drawingHandle.SetTransform(Matrix3.Identity);
OnPostDrawUIRoot?.Invoke(new PostDrawUIRootEventArgs(root, drawingHandle));
_prof.WriteValue("Controls rendered", ProfData.Int32(total));

View File

@@ -123,12 +123,7 @@ internal partial class UserInterfaceManager
private void UpdateUIScale(WindowRoot root)
{
var newScale = CalculateAutoScale(root);
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (newScale == root.UIScaleSet)
return;
root.UIScaleSet = newScale;
root.UIScaleSet = CalculateAutoScale(root);
_propagateUIScaleChanged(root);
root.InvalidateMeasure();
}
@@ -147,21 +142,7 @@ internal partial class UserInterfaceManager
{
if (!_windowsToRoot.TryGetValue(windowResizedEventArgs.Window.Id, out var root))
return;
root.UIScaleUpdateNeeded = true;
UpdateUIScale(root);
root.InvalidateMeasure();
}
private void CheckRootUIScaleUpdate(WindowRoot root)
{
if (!root.UIScaleUpdateNeeded)
return;
using (_prof.Group("UIScaleUpdate"))
{
UpdateUIScale(root);
}
root.UIScaleUpdateNeeded = false;
}
}

View File

@@ -216,8 +216,6 @@ namespace Robust.Client.UserInterface
{
foreach (var root in _roots)
{
CheckRootUIScaleUpdate(root);
using (_prof.Group("Root"))
{
var totalUpdated = root.DoFrameUpdateRecursive(args);
@@ -332,7 +330,7 @@ namespace Robust.Client.UserInterface
}
public void RenderControl(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
UIBox2i? scissorBox, Matrix3x2 coordinateTransform)
UIBox2i? scissorBox, Matrix3 coordinateTransform)
{
if (!control.Visible)
{
@@ -380,7 +378,8 @@ namespace Robust.Client.UserInterface
var handle = renderHandle.DrawingHandleScreen;
var oldXform = handle.GetTransform();
var xform = oldXform * Matrix3Helpers.CreateTransform(position, Angle.Zero, Vector2.One);
var xform = oldXform;
xform.Multiply(Matrix3.CreateTransform(position, Angle.Zero, Vector2.One));
handle.SetTransform(xform);
modulate *= control.Modulate;
@@ -407,7 +406,7 @@ namespace Robust.Client.UserInterface
foreach (var child in control.Children)
{
var pos = position + (Vector2i)Vector2.Transform(child.PixelPosition, coordinateTransform);
var pos = position + (Vector2i) coordinateTransform.Transform(child.PixelPosition);
control.RenderChildOverride(ref args, child.GetPositionInParent(), pos);
}

View File

@@ -4,6 +4,22 @@ namespace Robust.Client.Utility
{
internal static class OpenTKConversions
{
public static OpenToolkit.Mathematics.Matrix3 ConvertOpenTK(this Matrix3 matrix)
{
return new()
{
M11 = matrix.R0C0,
M12 = matrix.R0C1,
M13 = matrix.R0C2,
M21 = matrix.R1C0,
M22 = matrix.R1C1,
M23 = matrix.R1C2,
M31 = matrix.R2C0,
M32 = matrix.R2C1,
M33 = matrix.R2C2
};
}
public static OpenToolkit.Mathematics.Color4 ConvertOpenTK(this Color color)
{
return new(color.R, color.G, color.B, color.A);

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