mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b65e0c64ea | ||
|
|
4c388bc03d | ||
|
|
658dee1591 | ||
|
|
b6e5cca127 | ||
|
|
da5416a2da | ||
|
|
021845d956 | ||
|
|
7fab9f3b8d | ||
|
|
69c1161562 | ||
|
|
095fe9d60f | ||
|
|
14138fbcc2 | ||
|
|
48ce24e98b | ||
|
|
9cde21a7b3 | ||
|
|
ae1051e813 | ||
|
|
a3f80ac7dd | ||
|
|
f98ef78a21 | ||
|
|
bf8054b181 | ||
|
|
6b875e6676 | ||
|
|
a687c0a6c0 | ||
|
|
0580cf3ff7 | ||
|
|
590964d5bf | ||
|
|
ceda39813d | ||
|
|
a3a8912f42 | ||
|
|
b40973157d | ||
|
|
1de8731465 | ||
|
|
3a479cb5f4 | ||
|
|
76eeebf439 | ||
|
|
2fa83181e2 | ||
|
|
36f02b4a18 | ||
|
|
e842142dd7 | ||
|
|
2eb740cea8 | ||
|
|
a044f04e3b | ||
|
|
a4723d1f62 | ||
|
|
627c1eb054 | ||
|
|
836aec0b87 | ||
|
|
9116e64291 | ||
|
|
a6bfb5f557 | ||
|
|
5c83678c78 | ||
|
|
eac94b1032 | ||
|
|
efd870d070 | ||
|
|
94f98073b0 | ||
|
|
5aa9378de0 | ||
|
|
850e9ab695 | ||
|
|
7319f3a241 | ||
|
|
b6252c9e4f | ||
|
|
fcd507d1f9 | ||
|
|
1eb874f4c3 | ||
|
|
a628d31c4b | ||
|
|
2b0ecd7166 | ||
|
|
bde650689b | ||
|
|
87d8d74d8c | ||
|
|
dddf13a19a | ||
|
|
75626a86a3 | ||
|
|
3e3cd0e257 | ||
|
|
a3a90154a4 | ||
|
|
9240c94e59 | ||
|
|
a95ba9f181 | ||
|
|
074a4faa92 | ||
|
|
6b4d74f46e | ||
|
|
f648218756 | ||
|
|
b497efb0c0 | ||
|
|
d8f2b917b4 | ||
|
|
6dd6b79db6 | ||
|
|
ff4548f108 | ||
|
|
8f41405b31 | ||
|
|
f285c62674 | ||
|
|
56c30edf04 | ||
|
|
783d529ec4 | ||
|
|
895bfb8ec0 | ||
|
|
6ef67cf513 | ||
|
|
15a2f6702c | ||
|
|
c5c2c2022a | ||
|
|
3c378640dd | ||
|
|
0a149fa91c | ||
|
|
fe8d1d9422 |
@@ -15,10 +15,10 @@
|
||||
<PackageVersion Include="ILReader.Core" Version="1.0.0.4" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.0" />
|
||||
<PackageVersion Include="Linguini.Bundle" Version="0.1.3" />
|
||||
<PackageVersion Include="Linguini.Bundle" Version="0.8.1" />
|
||||
<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 +43,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="6.0.4" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||
<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 +55,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.3" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
159
RELEASE-NOTES.md
159
RELEASE-NOTES.md
@@ -54,6 +54,165 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 226.2.2
|
||||
|
||||
|
||||
## 226.2.1
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
- type: entity
|
||||
id: debugRotation
|
||||
abstract: true
|
||||
categories: [ debug ]
|
||||
categories: [ Debug ]
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
# 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
|
||||
|
||||
# entities that should be hidden from the spawn menu
|
||||
# simple category that just exists to hide prototypes in spawn menus
|
||||
- type: entityCategory
|
||||
id: hideSpawnMenu
|
||||
id: HideSpawnMenu
|
||||
name: entity-category-name-hide
|
||||
description: entity-category-desc-hide
|
||||
hideSpawnMenu: true
|
||||
inheritable: false
|
||||
|
||||
@@ -9,6 +9,7 @@ cmd-parse-failure-float = {$arg} is not a valid float.
|
||||
cmd-parse-failure-bool = {$arg} is not a valid bool.
|
||||
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
|
||||
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
|
||||
cmd-parse-failure-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}
|
||||
@@ -43,6 +44,13 @@ 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]
|
||||
@@ -245,9 +253,6 @@ 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>
|
||||
|
||||
@@ -299,16 +304,9 @@ 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>
|
||||
@@ -384,9 +382,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|uid]...
|
||||
cmd-tpto-destination-hint = destination (uid or username)
|
||||
cmd-tpto-victim-hint = entity to teleport (uid or username)
|
||||
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-parse-error = Cant resolve entity or player: {$str}
|
||||
|
||||
cmd-listplayers-desc = Lists all players currently connected.
|
||||
@@ -446,9 +444,6 @@ 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>
|
||||
|
||||
@@ -515,9 +510,6 @@ 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>
|
||||
|
||||
@@ -528,14 +520,11 @@ 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-desc = Usage: vvread <path>
|
||||
cmd-vvread-help = 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...]
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
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 the spawn menu
|
||||
entity-category-desc-hide = Entity prototypes that should be hidden from entity spawn menus
|
||||
|
||||
@@ -26,7 +26,8 @@ public sealed class DefaultSQLConfig : IConfig
|
||||
|
||||
public IEnumerable<IExporter> GetExporters()
|
||||
{
|
||||
yield return SQLExporter.Default;
|
||||
//yield return SQLExporter.Default;
|
||||
yield break;
|
||||
}
|
||||
|
||||
public IEnumerable<IColumnProvider> GetColumnProviders() => DefaultConfig.Instance.GetColumnProviders();
|
||||
|
||||
@@ -15,11 +15,10 @@ 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
|
||||
@@ -98,7 +97,9 @@ public sealed class SQLExporter : IExporter
|
||||
|
||||
public string Name => "sql";
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// https://github.com/npgsql/efcore.pg/issues/1107#issuecomment-945126627
|
||||
class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
|
||||
{
|
||||
@@ -138,6 +139,7 @@ class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
|
||||
=> null; // Let the built-in resolver do this
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<BenchmarkContext>
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = Type.GetType(args[0]);
|
||||
var type = GetType(args[0]);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
@@ -25,6 +25,17 @@ 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
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
|
||||
worldHandle.UseShader(null);
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
|
||||
@@ -443,7 +443,7 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
|
||||
screenHandle.UseShader(null);
|
||||
screenHandle.SetTransform(Matrix3.Identity);
|
||||
screenHandle.SetTransform(Matrix3x2.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.R0C2, matrix1.R1C2);
|
||||
var xf2 = new Vector2(matrix2.R0C2, matrix2.R1C2);
|
||||
var xf1 = new Vector2(matrix1.M31, matrix1.M32);
|
||||
var xf2 = new Vector2(matrix2.M31, matrix2.M32);
|
||||
|
||||
var p1 = matrix1.Transform(joint.LocalAnchorA);
|
||||
var p2 = matrix2.Transform(joint.LocalAnchorB);
|
||||
var p1 = Vector2.Transform(joint.LocalAnchorA, matrix1);
|
||||
var p2 = Vector2.Transform(joint.LocalAnchorB, matrix2);
|
||||
|
||||
var xfa = new Transform(xf1, xform1.WorldRotation);
|
||||
var xfb = new Transform(xf2, xform2.WorldRotation);
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Robust.Client.GameObjects
|
||||
[DataField("color")]
|
||||
private Color color = Color.White;
|
||||
|
||||
public Matrix3 LocalMatrix = Matrix3.Identity;
|
||||
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
|
||||
|
||||
[Animatable]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -389,10 +389,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
internal void UpdateLocalMatrix()
|
||||
{
|
||||
LocalMatrix = Matrix3.CreateTransform(in offset, in rotation, in scale);
|
||||
LocalMatrix = Matrix3Helpers.CreateTransform(in offset, in rotation, in scale);
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalMatrix()
|
||||
public Matrix3x2 GetLocalMatrix()
|
||||
{
|
||||
return LocalMatrix;
|
||||
}
|
||||
@@ -1304,22 +1304,21 @@ 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 = Matrix3.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
|
||||
var entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, NoRotation ? -eyeRotation : worldRotation - cardinal);
|
||||
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSprite);
|
||||
var transformSprite = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
|
||||
if (GranularLayersRendering)
|
||||
{
|
||||
//Default rendering
|
||||
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation);
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformDefault);
|
||||
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation);
|
||||
var transformDefault = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
//Snap to cardinals
|
||||
entityMatrix = Matrix3.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformSnap);
|
||||
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation - angle.GetCardinalDir().ToAngle());
|
||||
var transformSnap = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
//No rotation
|
||||
entityMatrix = Matrix3.CreateTransform(worldPosition, -eyeRotation);
|
||||
Matrix3.Multiply(in LocalMatrix, in entityMatrix, out var transformNoRot);
|
||||
|
||||
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, -eyeRotation);
|
||||
var transformNoRot = Matrix3x2.Multiply(LocalMatrix, entityMatrix);
|
||||
|
||||
foreach (var layer in Layers) {
|
||||
switch (layer.RenderingStrategy)
|
||||
@@ -1380,7 +1379,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(this, ref ev);
|
||||
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
|
||||
}
|
||||
|
||||
private void QueueUpdateIsInert()
|
||||
@@ -1390,7 +1389,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(this, ref ev);
|
||||
entities.EventBus.RaiseComponentEvent(Owner, this, ref ev);
|
||||
}
|
||||
|
||||
[Obsolete("Use SpriteSystem instead.")]
|
||||
@@ -1546,7 +1545,7 @@ namespace Robust.Client.GameObjects
|
||||
private RSI.State? _actualState;
|
||||
[ViewVariables] public RSI.State? ActualState => _actualState;
|
||||
|
||||
public Matrix3 LocalMatrix = Matrix3.Identity;
|
||||
public Matrix3x2 LocalMatrix = Matrix3x2.Identity;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 Scale
|
||||
@@ -1692,7 +1691,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
internal void UpdateLocalMatrix()
|
||||
{
|
||||
LocalMatrix = Matrix3.CreateTransform(in _offset, in _rotation, in _scale);
|
||||
LocalMatrix = Matrix3Helpers.CreateTransform(in _offset, in _rotation, in _scale);
|
||||
}
|
||||
|
||||
RSI? ISpriteLayer.Rsi { get => RSI; set => SetRsi(value); }
|
||||
@@ -1964,27 +1963,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 Matrix3 layerDrawMatrix)
|
||||
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3x2 layerDrawMatrix)
|
||||
{
|
||||
if (_parent.NoRotation || dir == RsiDirection.South)
|
||||
layerDrawMatrix = LocalMatrix;
|
||||
else
|
||||
{
|
||||
Matrix3.Multiply(in _rsiDirectionMatrices[(int)dir], in LocalMatrix, out layerDrawMatrix);
|
||||
layerDrawMatrix = Matrix3x2.Multiply(_rsiDirectionMatrices[(int)dir], LocalMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
private static Matrix3[] _rsiDirectionMatrices = new Matrix3[]
|
||||
private static Matrix3x2[] _rsiDirectionMatrices = new Matrix3x2[]
|
||||
{
|
||||
// array order chosen such that this array can be indexed by casing an RSI direction to an int
|
||||
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())
|
||||
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())
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -2018,7 +2017,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 Matrix3 spriteMatrix, Angle angle, Direction? overrideDirection)
|
||||
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatrix, Angle angle, Direction? overrideDirection)
|
||||
{
|
||||
if (!Visible || Blank)
|
||||
return;
|
||||
@@ -2040,7 +2039,7 @@ namespace Robust.Client.GameObjects
|
||||
if (CopyToShaderParameters == null)
|
||||
{
|
||||
// Set the drawing transform for this layer
|
||||
Matrix3.Multiply(in layerMatrix, in spriteMatrix, out var transformMatrix);
|
||||
var transformMatrix = Matrix3x2.Multiply(layerMatrix, spriteMatrix);
|
||||
drawingHandle.SetTransform(in transformMatrix);
|
||||
|
||||
RenderTexture(drawingHandle, texture);
|
||||
|
||||
@@ -11,6 +11,7 @@ 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!;
|
||||
@@ -18,6 +19,7 @@ namespace Robust.Client.GameObjects
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_playerQuery = GetEntityQuery<AnimationPlayerComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
}
|
||||
|
||||
@@ -171,28 +173,32 @@ namespace Robust.Client.GameObjects
|
||||
return component.PlayingAnimations.ContainsKey(key);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public void Stop(AnimationPlayerComponent component, string key)
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
Stop((component.Owner, component), key);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, string key)
|
||||
public void Stop(Entity<AnimationPlayerComponent?> entity, string key)
|
||||
{
|
||||
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
|
||||
if (!_playerQuery.Resolve(entity.Owner, ref entity.Comp, false) ||
|
||||
!entity.Comp.PlayingAnimations.Remove(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
player.PlayingAnimations.Remove(key);
|
||||
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, new AnimationCompletedEvent {Uid = entity.Owner, Key = key}, true);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
component.PlayingAnimations.Remove(key);
|
||||
Stop((uid, component), 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; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -118,7 +119,7 @@ public sealed class EntityLookupOverlay : Overlay
|
||||
//DebugTools.Assert(!ent.IsInContainer(_entityManager));
|
||||
var (entPos, entRot) = _transform.GetWorldPositionRotation(ent);
|
||||
|
||||
var lookupPos = invMatrix.Transform(entPos);
|
||||
var lookupPos = Vector2.Transform(entPos, invMatrix);
|
||||
var lookupRot = entRot - rotation;
|
||||
|
||||
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, _xformQuery);
|
||||
@@ -127,6 +128,6 @@ public sealed class EntityLookupOverlay : Overlay
|
||||
}
|
||||
});
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ 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!;
|
||||
@@ -82,18 +83,35 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
var clientMsg = message switch
|
||||
{
|
||||
Uid = GetNetEntity(clientMsg.Uid)
|
||||
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()
|
||||
};
|
||||
|
||||
DispatchInputCommand(clientMsg, fullMsg);
|
||||
@@ -131,7 +149,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
_conHost.RegisterCommand("incmd",
|
||||
"Inserts an input command into the simulation",
|
||||
"incmd <KeyFunction> <d|u KeyState> <wxPos> <wyPos>",
|
||||
"incmd <KeyFunction> <KeyState> [wxPos] [wyPos]",
|
||||
GenerateInputCommand);
|
||||
}
|
||||
|
||||
@@ -147,17 +165,47 @@ namespace Robust.Client.GameObjects
|
||||
if (_playerManager.LocalEntity is not { } pent)
|
||||
return;
|
||||
|
||||
BoundKeyFunction keyFunction = new BoundKeyFunction(args[0]);
|
||||
BoundKeyState state = args[1] == "u" ? BoundKeyState.Up: BoundKeyState.Down;
|
||||
if (args.Length is not (2 or 4))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString($"cmd-invalid-arg-number-error"));
|
||||
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 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 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 FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
|
||||
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
|
||||
var message = new ClientFullInputCmdMessage(_timing.CurTick,
|
||||
_timing.TickFraction,
|
||||
funcId,
|
||||
coords,
|
||||
new ScreenCoordinates(0, 0, default),
|
||||
state,
|
||||
EntityUid.Invalid);
|
||||
|
||||
HandleInputCommand(_playerManager.LocalSession, keyFunction, message);
|
||||
}
|
||||
|
||||
@@ -603,7 +603,7 @@ namespace Robust.Client.GameStates
|
||||
if (compState != null)
|
||||
{
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
_entities.EventBus.RaiseComponentEvent(entity, comp, ref handleState);
|
||||
}
|
||||
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
@@ -640,7 +640,7 @@ namespace Robust.Client.GameStates
|
||||
if (state != null)
|
||||
{
|
||||
var stateEv = new ComponentHandleState(state, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
|
||||
_entities.EventBus.RaiseComponentEvent(entity, comp, ref stateEv);
|
||||
}
|
||||
|
||||
comp.ClearCreationTick(); // don't undo the re-adding.
|
||||
@@ -1361,7 +1361,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
|
||||
var handleState = new ComponentHandleState(cur, next);
|
||||
bus.RaiseComponentEvent(comp, ref handleState);
|
||||
bus.RaiseComponentEvent(uid, comp, ref handleState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1516,7 +1516,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
|
||||
var handleState = new ComponentHandleState(state, null);
|
||||
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
_entityManager.EventBus.RaiseComponentEvent(uid, comp, ref handleState);
|
||||
}
|
||||
|
||||
// ensure we don't have any extra components
|
||||
|
||||
@@ -101,18 +101,16 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void GetScreenProjectionMatrix(out Matrix3 projMatrix)
|
||||
public void GetScreenProjectionMatrix(out Matrix3x2 projMatrix)
|
||||
{
|
||||
Matrix3 result = default;
|
||||
Matrix3x2 result = default;
|
||||
|
||||
result.R0C0 = PixelsPerMeter;
|
||||
result.R1C1 = -PixelsPerMeter;
|
||||
result.M11 = PixelsPerMeter;
|
||||
result.M22 = -PixelsPerMeter;
|
||||
|
||||
var screenSize = _displayManager.ScreenSize;
|
||||
result.R0C2 = screenSize.X / 2f;
|
||||
result.R1C2 = screenSize.Y / 2f;
|
||||
|
||||
result.R2C2 = 1;
|
||||
result.M31 = screenSize.X / 2f;
|
||||
result.M32 = screenSize.Y / 2f;
|
||||
|
||||
/* column major
|
||||
Sx 0 Tx
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Graphics;
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.Graphics
|
||||
/// to UI screen space.
|
||||
/// </summary>
|
||||
/// <param name="projMatrix"></param>
|
||||
void GetScreenProjectionMatrix(out Matrix3 projMatrix);
|
||||
void GetScreenProjectionMatrix(out Matrix3x2 projMatrix);
|
||||
|
||||
/// <summary>
|
||||
/// Projects a point from world space to UI screen space using the main viewport.
|
||||
|
||||
@@ -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(Matrix3.Identity);
|
||||
_renderHandle.SetModelTransform(Matrix3x2.Identity);
|
||||
|
||||
var rounded = roundedPos - entityPostRenderTarget.Size / 2;
|
||||
|
||||
@@ -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(Matrix3.Identity);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
|
||||
var pos = UIBox2.FromDimensions(viewport.Size / 2 - new Vector2(200, 200), new Vector2(400, 400));
|
||||
_renderHandle.DrawingHandleScreen.DrawTextureRect(FovTexture, pos);
|
||||
}
|
||||
@@ -538,7 +538,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (DebugLayers == ClydeDebugLayers.Light)
|
||||
{
|
||||
_renderHandle.UseShader(null);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity);
|
||||
_renderHandle.DrawingHandleScreen.SetTransform(Matrix3x2.Identity);
|
||||
_renderHandle.DrawingHandleScreen.DrawTextureRect(
|
||||
viewport.WallBleedIntermediateRenderTarget2.Texture,
|
||||
UIBox2.FromDimensions(Vector2.Zero, viewport.Size), new Color(1, 1, 1, 0.5f));
|
||||
|
||||
@@ -98,28 +98,34 @@ namespace Robust.Client.Graphics.Clyde
|
||||
[FieldOffset(20 * sizeof(float))] public Vector3 ViewMatrixC2;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ProjViewMatrices(in Matrix3 projMatrix, in Matrix3 viewMatrix)
|
||||
public ProjViewMatrices(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix)
|
||||
{
|
||||
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);
|
||||
// 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);
|
||||
|
||||
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);
|
||||
ViewMatrixC0 = new Vector3(viewMatrix.M11, viewMatrix.M12, 0);
|
||||
ViewMatrixC1 = new Vector3(viewMatrix.M21, viewMatrix.M22, 0);
|
||||
ViewMatrixC2 = new Vector3(viewMatrix.M31, viewMatrix.M32, 1);
|
||||
}
|
||||
|
||||
public void Apply(Clyde clyde, GLShaderProgram program)
|
||||
{
|
||||
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("projectionMatrix", new Matrix3x2(
|
||||
ProjMatrixC0.X, ProjMatrixC0.Y, // Implicit 0
|
||||
ProjMatrixC1.X, ProjMatrixC1.Y, // Implicit 0
|
||||
ProjMatrixC2.X, ProjMatrixC2.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
|
||||
program.SetUniformMaybe("viewMatrix", new Matrix3x2(
|
||||
ViewMatrixC0.X, ViewMatrixC0.Y, // Implicit 0
|
||||
ViewMatrixC1.X, ViewMatrixC1.Y, // Implicit 0
|
||||
ViewMatrixC2.X, ViewMatrixC2.Y // Implicit 1
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,18 +492,18 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
var offset = new Vector2(component.Radius, component.Radius);
|
||||
|
||||
Matrix3 matrix;
|
||||
Matrix3x2 matrix;
|
||||
if (mask == null)
|
||||
{
|
||||
matrix = Matrix3.Identity;
|
||||
matrix = Matrix3x2.Identity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only apply rotation if a mask is said, because else it doesn't matter.
|
||||
matrix = Matrix3.CreateRotation(rotation);
|
||||
matrix = Matrix3Helpers.CreateRotation(rotation);
|
||||
}
|
||||
|
||||
(matrix.R0C2, matrix.R1C2) = lightPos;
|
||||
(matrix.M31, matrix.M32) = 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, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.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, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.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, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.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, Matrix3.Identity, shader);
|
||||
_drawQuad(Vector2.Zero, viewport.Size, Matrix3x2.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 Matrix3(in uX, in uY, in uZero);
|
||||
var clipToDiff = new Matrix3x2(uX.X, uX.Y, uY.X, uY.Y, uZero.X, uZero.Y);
|
||||
|
||||
fovShader.SetUniformMaybe("clipToDiff", clipToDiff);
|
||||
_drawQuad(Vector2.Zero, Vector2.One, Matrix3.Identity, fovShader);
|
||||
_drawQuad(Vector2.Zero, Vector2.One, Matrix3x2.Identity, fovShader);
|
||||
}
|
||||
|
||||
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3 eyeTransform)
|
||||
private void UpdateOcclusionGeometry(MapId map, Box2 expandedBounds, Matrix3x2 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 = worldTransform.Transform(box.TopLeft);
|
||||
var tr = worldTransform.Transform(box.TopRight);
|
||||
var br = worldTransform.Transform(box.BottomRight);
|
||||
var tl = Vector2.Transform(box.TopLeft, worldTransform);
|
||||
var tr = Vector2.Transform(box.TopRight, worldTransform);
|
||||
var br = Vector2.Transform(box.BottomRight, worldTransform);
|
||||
var bl = tl + br - tr;
|
||||
|
||||
// Faces.
|
||||
@@ -1010,9 +1010,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
//
|
||||
|
||||
// Calculate delta positions from camera.
|
||||
var dTl = eyeTransform.Transform(tl);
|
||||
var dTr = eyeTransform.Transform(tr);
|
||||
var dBl = eyeTransform.Transform(bl);
|
||||
var dTl = Vector2.Transform(tl, eyeTransform);
|
||||
var dTr = Vector2.Transform(tr, eyeTransform);
|
||||
var dBl = Vector2.Transform(bl, eyeTransform);
|
||||
var dBr = dBl + dTr - dTl;
|
||||
|
||||
// Get which neighbors are occluding.
|
||||
|
||||
@@ -33,17 +33,17 @@ namespace Robust.Client.Graphics.Clyde
|
||||
DrawingHandleWorld = new DrawingHandleWorldImpl(white, this);
|
||||
}
|
||||
|
||||
public void SetModelTransform(in Matrix3 matrix)
|
||||
public void SetModelTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_clyde.DrawSetModelTransform(matrix);
|
||||
}
|
||||
|
||||
public Matrix3 GetModelTransform()
|
||||
public Matrix3x2 GetModelTransform()
|
||||
{
|
||||
return _clyde.DrawGetModelTransform();
|
||||
}
|
||||
|
||||
public void SetProjView(in Matrix3 proj, in Matrix3 view)
|
||||
public void SetProjView(in Matrix3x2 proj, in Matrix3x2 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.R0C2, oldModel.R1C2);
|
||||
newModel.R0C2 = 0;
|
||||
newModel.R1C2 = 0;
|
||||
position += new Vector2(oldModel.M31, oldModel.M32);
|
||||
newModel.M31 = 0;
|
||||
newModel.M32 = 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 = Matrix3.CreateTransform(ofsX, ofsY, rot, scale.X, scale.Y);
|
||||
var view = Matrix3Helpers.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 Matrix3 matrix)
|
||||
public override void SetTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_renderHandle.SetModelTransform(matrix);
|
||||
}
|
||||
|
||||
public override Matrix3 GetTransform()
|
||||
public override Matrix3x2 GetTransform()
|
||||
{
|
||||
return _renderHandle.GetModelTransform();
|
||||
}
|
||||
@@ -402,12 +402,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle = renderHandle;
|
||||
}
|
||||
|
||||
public override void SetTransform(in Matrix3 matrix)
|
||||
public override void SetTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_renderHandle.SetModelTransform(matrix);
|
||||
}
|
||||
|
||||
public override Matrix3 GetTransform()
|
||||
public override Matrix3x2 GetTransform()
|
||||
{
|
||||
return _renderHandle.GetModelTransform();
|
||||
}
|
||||
|
||||
@@ -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 Matrix3 _currentMatrixModel = Matrix3.Identity;
|
||||
private Matrix3x2 _currentMatrixModel = Matrix3x2.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 Matrix3 _currentMatrixProj;
|
||||
private Matrix3 _currentMatrixView;
|
||||
private Matrix3x2 _currentMatrixProj;
|
||||
private Matrix3x2 _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 Matrix3 proj, out Matrix3 view)
|
||||
private void CalcScreenMatrices(in Vector2i screenSize, out Matrix3x2 proj, out Matrix3x2 view)
|
||||
{
|
||||
proj = Matrix3.Identity;
|
||||
proj.R0C0 = 2f / screenSize.X;
|
||||
proj.R1C1 = -2f / screenSize.Y;
|
||||
proj.R0C2 = -1;
|
||||
proj.R1C2 = 1;
|
||||
proj = Matrix3x2.Identity;
|
||||
proj.M11 = 2f / screenSize.X;
|
||||
proj.M22 = -2f / screenSize.Y;
|
||||
proj.M31 = -1;
|
||||
proj.M32 = 1;
|
||||
|
||||
if (_currentRenderTarget.FlipY)
|
||||
{
|
||||
proj.R1C1 *= -1;
|
||||
proj.R1C2 *= -1;
|
||||
proj.M22 *= -1;
|
||||
proj.M32 *= -1;
|
||||
}
|
||||
|
||||
view = Matrix3.Identity;
|
||||
view = Matrix3x2.Identity;
|
||||
}
|
||||
|
||||
private void CalcWorldMatrices(in Vector2i screenSize, in Vector2 renderScale, IEye eye,
|
||||
out Matrix3 proj, out Matrix3 view)
|
||||
out Matrix3x2 proj, out Matrix3x2 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 Matrix3 proj)
|
||||
private void CalcWorldProjMatrix(in Vector2i screenSize, out Matrix3x2 proj)
|
||||
{
|
||||
proj = Matrix3.Identity;
|
||||
proj.R0C0 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
|
||||
proj.R1C1 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
|
||||
proj = Matrix3x2.Identity;
|
||||
proj.M11 = EyeManager.PixelsPerMeter * 2f / screenSize.X;
|
||||
proj.M22 = EyeManager.PixelsPerMeter * 2f / screenSize.Y;
|
||||
|
||||
if (_currentRenderTarget.FlipY)
|
||||
{
|
||||
proj.R1C1 *= -1;
|
||||
proj.R1C2 *= -1;
|
||||
proj.M22 *= -1;
|
||||
proj.M32 *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetProjViewBuffer(in Matrix3 proj, in Matrix3 view)
|
||||
private void SetProjViewBuffer(in Matrix3x2 proj, in Matrix3x2 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 Matrix3 proj, in Matrix3 view)
|
||||
private void SetProjViewFull(in Matrix3x2 proj, in Matrix3x2 view)
|
||||
{
|
||||
_currentMatrixProj = proj;
|
||||
_currentMatrixView = view;
|
||||
@@ -285,21 +285,21 @@ namespace Robust.Client.Graphics.Clyde
|
||||
};
|
||||
}
|
||||
|
||||
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3 modelMatrix, GLShaderProgram program)
|
||||
private void _drawQuad(Vector2 a, Vector2 b, in Matrix3x2 modelMatrix, GLShaderProgram program)
|
||||
{
|
||||
DrawQuadWithVao(QuadVAO, a, b, modelMatrix, program);
|
||||
}
|
||||
|
||||
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3 modelMatrix,
|
||||
private void DrawQuadWithVao(GLHandle vao, Vector2 a, Vector2 b, in Matrix3x2 modelMatrix,
|
||||
GLShaderProgram program)
|
||||
{
|
||||
BindVertexArray(vao.Handle);
|
||||
CheckGlError();
|
||||
|
||||
var rectTransform = Matrix3.Identity;
|
||||
(rectTransform.R0C0, rectTransform.R1C1) = b - a;
|
||||
(rectTransform.R0C2, rectTransform.R1C2) = a;
|
||||
rectTransform.Multiply(modelMatrix);
|
||||
var rectTransform = Matrix3x2.Identity;
|
||||
(rectTransform.M11, rectTransform.M22) = b - a;
|
||||
(rectTransform.M31, rectTransform.M32) = a;
|
||||
rectTransform = rectTransform * modelMatrix;
|
||||
program.SetUniformMaybe(UniIModelMatrix, rectTransform);
|
||||
|
||||
_debugStats.LastGLDrawCalls += 1;
|
||||
@@ -315,7 +315,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
FlushBatchQueue();
|
||||
|
||||
// Reset renderer state.
|
||||
_currentMatrixModel = Matrix3.Identity;
|
||||
_currentMatrixModel = Matrix3x2.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 Matrix3 matrix3:
|
||||
case Matrix3x2 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 Matrix3 matrix)
|
||||
private void DrawSetModelTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_currentMatrixModel = matrix;
|
||||
}
|
||||
|
||||
private Matrix3 DrawGetModelTransform()
|
||||
private Matrix3x2 DrawGetModelTransform()
|
||||
{
|
||||
return _currentMatrixModel;
|
||||
}
|
||||
|
||||
private void DrawSetProjViewTransform(in Matrix3 proj, in Matrix3 view)
|
||||
private void DrawSetProjViewTransform(in Matrix3x2 proj, in Matrix3x2 view)
|
||||
{
|
||||
BreakBatch();
|
||||
|
||||
@@ -571,9 +571,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
EnsureBatchSpaceAvailable(4, GetQuadBatchIndexCount());
|
||||
EnsureBatchState(texture, true, GetQuadBatchPrimitiveType(), _queuedShader);
|
||||
|
||||
bl = _currentMatrixModel.Transform(bl);
|
||||
br = _currentMatrixModel.Transform(br);
|
||||
tr = _currentMatrixModel.Transform(tr);
|
||||
bl = Vector2.Transform(bl, _currentMatrixModel);
|
||||
br = Vector2.Transform(br, _currentMatrixModel);
|
||||
tr = Vector2.Transform(tr, _currentMatrixModel);
|
||||
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 = _currentMatrixModel.Transform(a);
|
||||
b = _currentMatrixModel.Transform(b);
|
||||
a = Vector2.Transform(a, _currentMatrixModel);
|
||||
b = Vector2.Transform(b, _currentMatrixModel);
|
||||
|
||||
// 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 = Matrix3.Identity;
|
||||
command.DrawBatch.ModelMatrix = Matrix3x2.Identity;
|
||||
|
||||
_debugStats.LastBatches += 1;
|
||||
}
|
||||
@@ -882,7 +882,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_queuedRenderCommands.Clear();
|
||||
_currentViewport = null;
|
||||
_lightingReady = false;
|
||||
_currentMatrixModel = Matrix3.Identity;
|
||||
_currentMatrixModel = Matrix3x2.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 Matrix3 ModelMatrix;
|
||||
public Matrix3x2 ModelMatrix;
|
||||
}
|
||||
|
||||
private struct RenderCommandProjViewMatrix
|
||||
{
|
||||
public Matrix3 ProjMatrix;
|
||||
public Matrix3 ViewMatrix;
|
||||
public Matrix3x2 ProjMatrix;
|
||||
public Matrix3x2 ViewMatrix;
|
||||
}
|
||||
|
||||
private struct RenderCommandScissor
|
||||
@@ -1056,11 +1056,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private readonly struct FullStoredRendererState
|
||||
{
|
||||
public readonly Matrix3 ProjMatrix;
|
||||
public readonly Matrix3 ViewMatrix;
|
||||
public readonly Matrix3x2 ProjMatrix;
|
||||
public readonly Matrix3x2 ViewMatrix;
|
||||
public readonly LoadedRenderTarget RenderTarget;
|
||||
|
||||
public FullStoredRendererState(in Matrix3 projMatrix, in Matrix3 viewMatrix,
|
||||
public FullStoredRendererState(in Matrix3x2 projMatrix, in Matrix3x2 viewMatrix,
|
||||
LoadedRenderTarget renderTarget)
|
||||
{
|
||||
ProjMatrix = projMatrix;
|
||||
|
||||
@@ -506,7 +506,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix3 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
|
||||
{
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
data.ParametersDirty = true;
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// view matrix
|
||||
vp.Eye.GetViewMatrixInv(out var viewMatrixInv, vp.RenderScale);
|
||||
point = viewMatrixInv * point;
|
||||
point = Vector2.Transform(point, viewMatrixInv);
|
||||
|
||||
return point;
|
||||
}
|
||||
@@ -136,7 +136,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// view matrix
|
||||
Eye.GetViewMatrixInv(out var viewMatrixInv, RenderScale);
|
||||
newPoint = viewMatrixInv * newPoint;
|
||||
newPoint = Vector2.Transform(newPoint, viewMatrixInv);
|
||||
|
||||
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 = viewMatrix * newPoint;
|
||||
newPoint = Vector2.Transform(newPoint, viewMatrix);
|
||||
|
||||
// (inlined version of UiProjMatrix)
|
||||
newPoint *= new Vector2(1, -1) * EyeManager.PixelsPerMeter;
|
||||
@@ -159,14 +159,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
public Matrix3 GetWorldToLocalMatrix()
|
||||
public Matrix3x2 GetWorldToLocalMatrix()
|
||||
{
|
||||
if (Eye == null)
|
||||
return Matrix3.Identity;
|
||||
return Matrix3x2.Identity;
|
||||
|
||||
Eye.GetViewMatrix(out var viewMatrix, RenderScale * new Vector2(EyeManager.PixelsPerMeter, -EyeManager.PixelsPerMeter));
|
||||
viewMatrix.R0C2 += Size.X / 2f;
|
||||
viewMatrix.R1C2 += Size.Y / 2f;
|
||||
viewMatrix.M31 += Size.X / 2f;
|
||||
viewMatrix.M32 += Size.Y / 2f;
|
||||
return viewMatrix;
|
||||
}
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix3 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix3x2 value)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return default;
|
||||
}
|
||||
|
||||
public Matrix3 GetWorldToLocalMatrix() => default;
|
||||
public Matrix3x2 GetWorldToLocalMatrix() => default;
|
||||
|
||||
public Vector2 WorldToLocal(Vector2 point)
|
||||
{
|
||||
|
||||
@@ -247,28 +247,33 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GL.Uniform1(uniformId, singles.Length, singles);
|
||||
}
|
||||
|
||||
public void SetUniform(string uniformName, in Matrix3 matrix)
|
||||
public void SetUniform(string uniformName, in Matrix3x2 matrix)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
SetUniformDirect(uniformId, matrix);
|
||||
}
|
||||
|
||||
public void SetUniform(int uniformName, in Matrix3 matrix)
|
||||
public void SetUniform(int uniformName, in Matrix3x2 matrix)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
SetUniformDirect(uniformId, matrix);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe void SetUniformDirect(int slot, in Matrix3 value, bool transpose=true)
|
||||
private unsafe void SetUniformDirect(int slot, in Matrix3x2 value)
|
||||
{
|
||||
Matrix3 tmpTranspose = value;
|
||||
if (transpose)
|
||||
{
|
||||
// transposition not supported on GLES2, & no access to _hasGLES
|
||||
tmpTranspose.Transpose();
|
||||
}
|
||||
GL.UniformMatrix3(slot, 1, false, (float*) &tmpTranspose);
|
||||
// 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);
|
||||
_clyde.CheckGlError();
|
||||
}
|
||||
|
||||
@@ -474,7 +479,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformMaybe(string uniformName, in Matrix3 value)
|
||||
public void SetUniformMaybe(string uniformName, in Matrix3x2 value)
|
||||
{
|
||||
if (TryGetUniform(uniformName, out var slot))
|
||||
{
|
||||
@@ -490,7 +495,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformMaybe(int uniformName, in Matrix3 value)
|
||||
public void SetUniformMaybe(int uniformName, in Matrix3x2 value)
|
||||
{
|
||||
if (TryGetUniform(uniformName, out var slot))
|
||||
{
|
||||
|
||||
@@ -44,19 +44,19 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
var matrix = Matrix3.CreateTransform(in position, in rotation, in scale);
|
||||
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation, in scale);
|
||||
SetTransform(in matrix);
|
||||
}
|
||||
|
||||
public void SetTransform(in Vector2 position, in Angle rotation)
|
||||
{
|
||||
var matrix = Matrix3.CreateTransform(in position, in rotation);
|
||||
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation);
|
||||
SetTransform(in matrix);
|
||||
}
|
||||
|
||||
public abstract void SetTransform(in Matrix3 matrix);
|
||||
public abstract void SetTransform(in Matrix3x2 matrix);
|
||||
|
||||
public abstract Matrix3 GetTransform();
|
||||
public abstract Matrix3x2 GetTransform();
|
||||
|
||||
public abstract void UseShader(ShaderInstance? shader);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Robust.Client.Graphics
|
||||
/// <summary>
|
||||
/// Matrix equivalent of <see cref="LocalToWorld(Vector2)"/>.
|
||||
/// </summary>
|
||||
Matrix3 GetWorldToLocalMatrix();
|
||||
Matrix3x2 GetWorldToLocalMatrix();
|
||||
|
||||
/// <summary>
|
||||
/// Converts a point in world-space to the viewport's screen coordinates.
|
||||
|
||||
@@ -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 => false;
|
||||
public virtual bool RequestScreenTexture { get; set; } = 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.
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Robust.Client.Graphics
|
||||
SetParameterImpl(name, value);
|
||||
}
|
||||
|
||||
public void SetParameter(string name, in Matrix3 value)
|
||||
public void SetParameter(string name, in Matrix3x2 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 Matrix3 value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix3x2 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);
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Robust.Client.Graphics
|
||||
return node.AsVector4();
|
||||
}
|
||||
case ShaderDataType.Mat3:
|
||||
return node.AsMatrix3();
|
||||
return node.AsMatrix3x2();
|
||||
case ShaderDataType.Mat4:
|
||||
return node.AsMatrix4();
|
||||
default:
|
||||
@@ -219,7 +219,7 @@ namespace Robust.Client.Graphics
|
||||
case bool i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
case Matrix3 i:
|
||||
case Matrix3x2 i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
case Matrix4 i:
|
||||
|
||||
@@ -120,6 +120,6 @@ public sealed class TileEdgeOverlay : GridOverlay
|
||||
}
|
||||
}
|
||||
|
||||
args.WorldHandle.SetTransform(Matrix3.Identity);
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Robust.Client.Physics
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
|
||||
@@ -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 = (CommonSession) CreateAndAddSession(state.UserId, state.Name);
|
||||
newSession.Ping = state.Ping;
|
||||
var newSession = (ICommonSessionInternal)CreateAndAddSession(state.UserId, state.Name);
|
||||
newSession.SetPing(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 = (CommonSession) session;
|
||||
local.Name = state.Name;
|
||||
local.Ping = state.Ping;
|
||||
var local = (ICommonSessionInternal)session;
|
||||
local.SetName(state.Name);
|
||||
local.SetPing(state.Ping);
|
||||
SetStatus(local, state.Status);
|
||||
SetAttachedEntity(local, controlled, out _, true);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,31 @@ 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,
|
||||
@@ -138,6 +163,7 @@ public sealed partial class ReplayLoadManager
|
||||
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)
|
||||
@@ -148,10 +174,10 @@ public sealed partial class ReplayLoadManager
|
||||
DebugTools.Assert(curState.FromSequence <= lastState.ToSequence);
|
||||
|
||||
UpdatePlayerStates(curState.PlayerStates.Span, playerStates);
|
||||
UpdateEntityStates(curState.EntityStates.Span, entStates, ref spawnedTracker, ref stateTracker, detached);
|
||||
UpdateEntityStates(curState.EntityStates.Span, entStates, modifiedEntities, 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);
|
||||
UpdateDeletions(curState.EntityDeletions, entStates, detached, modifiedEntities);
|
||||
serverTime[i] = GetTime(curState.ToSequence) - initialTime;
|
||||
ticksSinceLastCheckpoint++;
|
||||
|
||||
@@ -182,6 +208,8 @@ public sealed partial class ReplayLoadManager
|
||||
ticksSinceLastCheckpoint = 0;
|
||||
spawnedTracker = 0;
|
||||
stateTracker = 0;
|
||||
ApplyModifiedEntities(entStates, modifiedEntities);
|
||||
|
||||
var newState = new GameState(GameTick.Zero,
|
||||
curState.ToSequence,
|
||||
default,
|
||||
@@ -339,16 +367,18 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
private void UpdateDeletions(NetListAsArray<NetEntity> entityDeletions,
|
||||
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached)
|
||||
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached, Dictionary<NetEntity, UpdateScratchData> modifiedEntities)
|
||||
{
|
||||
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)
|
||||
@@ -369,9 +399,22 @@ public sealed partial class ReplayLoadManager
|
||||
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);
|
||||
entStates[entState.NetEntity] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
|
||||
// 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;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
foreach (var state in entStates[entState.NetEntity].ComponentChanges.Span)
|
||||
@@ -382,6 +425,53 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -420,7 +510,7 @@ public sealed partial class ReplayLoadManager
|
||||
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 delta);
|
||||
DebugTools.Assert(compChange.State is not IComponentDeltaState);
|
||||
combined.Add(compChange);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
public void ResetToNearestCheckpoint(int index, bool flushEntities, CheckpointState? checkpoint = null)
|
||||
{
|
||||
if (Replay == null)
|
||||
throw new Exception("Not currently playing a replay");
|
||||
@@ -25,7 +25,8 @@ internal sealed partial class ReplayPlaybackManager
|
||||
if (flushEntities)
|
||||
_entMan.FlushEntities();
|
||||
|
||||
var checkpoint = GetLastCheckpoint(Replay, index);
|
||||
// Look up the desired checkpoint, unless our caller kindly provided one to us.
|
||||
checkpoint ??= GetLastCheckpoint(Replay, index);
|
||||
|
||||
_sawmill.Info($"Resetting to checkpoint. From {Replay.CurrentIndex} to {checkpoint.Index}");
|
||||
var st = new Stopwatch();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Replays.Playback;
|
||||
@@ -27,14 +28,19 @@ 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)
|
||||
{
|
||||
@@ -45,9 +51,12 @@ internal sealed partial class ReplayPlaybackManager
|
||||
{
|
||||
// If we are skipping many ticks into the future, we try to skip directly to a checkpoint instead of
|
||||
// applying every tick.
|
||||
var nextCheckpoint = GetNextCheckpoint(Replay, Replay.CurrentIndex);
|
||||
if (nextCheckpoint.Index < value && nextCheckpoint.Index > Replay.CurrentIndex)
|
||||
ResetToNearestCheckpoint(value, false);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
_entMan.EntitySysManager.GetEntitySystem<ClientDirtySystem>().Reset();
|
||||
@@ -75,6 +84,23 @@ 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();
|
||||
|
||||
@@ -52,6 +52,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 _visualEventThreshold;
|
||||
public uint? AutoPauseCountdown { get; set; }
|
||||
public int? ScrubbingTarget { get; set; }
|
||||
@@ -94,6 +95,7 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
|
||||
_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.ReplaySkipThreshold, (value) => _visualEventThreshold = value, true);
|
||||
_client.RunLevelChanged += OnRunLevelChanged;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Robust.Client.ResourceManagement
|
||||
{
|
||||
sawmill.Debug("Preloading textures...");
|
||||
var sw = Stopwatch.StartNew();
|
||||
var resList = GetTypeDict<TextureResource>();
|
||||
var resList = GetTypeData<TextureResource>().Resources;
|
||||
|
||||
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 = GetTypeDict<RSIResource>();
|
||||
var resList = GetTypeData<RSIResource>().Resources;
|
||||
|
||||
var rsiList = _manager.ContentFindFiles("/Textures/")
|
||||
.Where(p => p.ToString().EndsWith(".rsi/meta.json"))
|
||||
|
||||
@@ -17,9 +17,7 @@ namespace Robust.Client.ResourceManagement;
|
||||
/// </summary>
|
||||
internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInternal, IDisposable
|
||||
{
|
||||
private readonly Dictionary<Type, Dictionary<ResPath, BaseResource>> _cachedResources =
|
||||
new();
|
||||
|
||||
private readonly Dictionary<Type, TypeData> _cachedResources = new();
|
||||
private readonly Dictionary<Type, BaseResource> _fallbacks = new();
|
||||
|
||||
public T GetResource<T>(string path, bool useFallback = true) where T : BaseResource, new()
|
||||
@@ -29,8 +27,8 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
|
||||
public T GetResource<T>(ResPath path, bool useFallback = true) where T : BaseResource, new()
|
||||
{
|
||||
var cache = GetTypeDict<T>();
|
||||
if (cache.TryGetValue(path, out var cached))
|
||||
var cache = GetTypeData<T>();
|
||||
if (cache.Resources.TryGetValue(path, out var cached))
|
||||
{
|
||||
return (T) cached;
|
||||
}
|
||||
@@ -40,7 +38,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
{
|
||||
var dependencies = IoCManager.Instance!;
|
||||
resource.Load(dependencies, path);
|
||||
cache[path] = resource;
|
||||
cache.Resources[path] = resource;
|
||||
return resource;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -67,24 +65,31 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
|
||||
public bool TryGetResource<T>(ResPath path, [NotNullWhen(true)] out T? resource) where T : BaseResource, new()
|
||||
{
|
||||
var cache = GetTypeDict<T>();
|
||||
if (cache.TryGetValue(path, out var cached))
|
||||
var cache = GetTypeData<T>();
|
||||
if (cache.Resources.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[path] = resource;
|
||||
cache.Resources[path] = resource;
|
||||
return true;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
cache.NonExistent.Add(path);
|
||||
resource = null;
|
||||
return false;
|
||||
}
|
||||
@@ -109,9 +114,9 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
|
||||
public void ReloadResource<T>(ResPath path) where T : BaseResource, new()
|
||||
{
|
||||
var cache = GetTypeDict<T>();
|
||||
var cache = GetTypeData<T>();
|
||||
|
||||
if (!cache.TryGetValue(path, out var res))
|
||||
if (!cache.Resources.TryGetValue(path, out var res))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -145,7 +150,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
|
||||
public void CacheResource<T>(ResPath path, T resource) where T : BaseResource, new()
|
||||
{
|
||||
GetTypeDict<T>()[path] = resource;
|
||||
GetTypeData<T>().Resources[path] = resource;
|
||||
}
|
||||
|
||||
public T GetFallback<T>() where T : BaseResource, new()
|
||||
@@ -168,7 +173,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
|
||||
public IEnumerable<KeyValuePair<ResPath, T>> GetAllResources<T>() where T : BaseResource, new()
|
||||
{
|
||||
return GetTypeDict<T>().Select(p => new KeyValuePair<ResPath, T>(p.Key, (T) p.Value));
|
||||
return GetTypeData<T>().Resources.Select(p => new KeyValuePair<ResPath, T>(p.Key, (T) p.Value));
|
||||
}
|
||||
|
||||
public event Action<TextureLoadedEventArgs>? OnRawTextureLoaded;
|
||||
@@ -193,7 +198,7 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var res in _cachedResources.Values.SelectMany(dict => dict.Values))
|
||||
foreach (var res in _cachedResources.Values.SelectMany(dict => dict.Resources.Values))
|
||||
{
|
||||
res.Dispose();
|
||||
}
|
||||
@@ -210,15 +215,9 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
|
||||
#endregion IDisposable Members
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected Dictionary<ResPath, BaseResource> GetTypeDict<T>()
|
||||
private TypeData GetTypeData<T>()
|
||||
{
|
||||
if (!_cachedResources.TryGetValue(typeof(T), out var ret))
|
||||
{
|
||||
ret = new Dictionary<ResPath, BaseResource>();
|
||||
_cachedResources.Add(typeof(T), ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return _cachedResources.GetOrNew(typeof(T));
|
||||
}
|
||||
|
||||
public void TextureLoaded(TextureLoadedEventArgs eventArgs)
|
||||
@@ -230,4 +229,13 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,9 +212,18 @@ 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);
|
||||
@@ -567,7 +576,7 @@ namespace Robust.Client.UserInterface
|
||||
public Vector2i Position;
|
||||
public Color Modulate;
|
||||
public UIBox2i? ScissorBox;
|
||||
public ref Matrix3 CoordinateTransform;
|
||||
public ref Matrix3x2 CoordinateTransform;
|
||||
}
|
||||
|
||||
protected void RenderControl(ref ControlRenderArguments args, int childIndex, Vector2i position)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -20,19 +21,19 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
/// <summary>
|
||||
/// Base background colour.
|
||||
/// </summary>
|
||||
public Vector4 BaseColor;
|
||||
public Robust.Shared.Maths.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 Vector4 XAxis;
|
||||
public Robust.Shared.Maths.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 Vector4 YAxis;
|
||||
public Robust.Shared.Maths.Vector4 YAxis;
|
||||
|
||||
/// <summary>
|
||||
/// If true, then <see cref="BaseColor"/>, <see cref="XAxis"/>, and <see cref="YAxis"/> will be interpreted as HSVa
|
||||
@@ -53,7 +54,7 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
var old = handle.GetShader();
|
||||
handle.UseShader(_shader);
|
||||
|
||||
var globalPixelPos = handle.GetTransform().Transform(default);
|
||||
var globalPixelPos = Vector2.Transform(default, handle.GetTransform());
|
||||
_shader.SetParameter("size", box.Size);
|
||||
_shader.SetParameter("offset", globalPixelPos);
|
||||
_shader.SetParameter("xAxis", XAxis);
|
||||
@@ -92,14 +93,14 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
{
|
||||
var colorData = Hsv
|
||||
? Color.ToHsv(color)
|
||||
: new Vector4(color.R, color.G, color.B, color.A);
|
||||
: new Robust.Shared.Maths.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(Vector4 colorData)
|
||||
public void SetBaseColor(Robust.Shared.Maths.Vector4 colorData)
|
||||
{
|
||||
BaseColor = colorData - colorData * XAxis - colorData * YAxis;
|
||||
}
|
||||
|
||||
64
Robust.Client/UserInterface/Controls/EntityPrototypeView.cs
Normal file
64
Robust.Client/UserInterface/Controls/EntityPrototypeView.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
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;
|
||||
@@ -20,7 +19,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
public const string StylePropertyStyleBox = "stylebox";
|
||||
|
||||
private readonly List<RichTextEntry> _entries = new();
|
||||
private readonly RingBufferList<RichTextEntry> _entries = new();
|
||||
private bool _isAtBottom = true;
|
||||
|
||||
private int _totalContentHeight;
|
||||
@@ -30,6 +29,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
public bool ScrollFollowing { get; set; } = true;
|
||||
|
||||
private bool _invalidOnVisible;
|
||||
|
||||
public OutputPanel()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -45,6 +46,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
_scrollBar.OnValueChanged += _ => _isAtBottom = _scrollBar.IsAtEnd;
|
||||
}
|
||||
|
||||
public int EntryCount => _entries.Count;
|
||||
|
||||
public StyleBox? StyleBoxOverride
|
||||
{
|
||||
get => _styleBoxOverride;
|
||||
@@ -91,7 +94,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
var entry = new RichTextEntry(message, this, _tagManager, null);
|
||||
|
||||
entry.Update(_getFont(), _getContentBox().Width, UIScale);
|
||||
entry.Update(_tagManager, _getFont(), _getContentBox().Width, UIScale);
|
||||
|
||||
_entries.Add(entry);
|
||||
var font = _getFont();
|
||||
@@ -134,7 +137,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 CollectionsMarshal.AsSpan(_entries))
|
||||
foreach (ref var entry in _entries)
|
||||
{
|
||||
if (entryOffset + entry.Height < 0)
|
||||
{
|
||||
@@ -147,7 +150,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
break;
|
||||
}
|
||||
|
||||
entry.Draw(handle, font, contentBox, entryOffset, context, UIScale);
|
||||
entry.Draw(_tagManager, handle, font, contentBox, entryOffset, context, UIScale);
|
||||
|
||||
entryOffset += entry.Height + font.GetLineSeparation(UIScale);
|
||||
}
|
||||
@@ -185,9 +188,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
_totalContentHeight = 0;
|
||||
var font = _getFont();
|
||||
var sizeX = _getContentBox().Width;
|
||||
foreach (ref var entry in CollectionsMarshal.AsSpan(_entries))
|
||||
foreach (ref var entry in _entries)
|
||||
{
|
||||
entry.Update(font, sizeX, UIScale);
|
||||
entry.Update(_tagManager, font, sizeX, UIScale);
|
||||
_totalContentHeight += entry.Height + font.GetLineSeparation(UIScale);
|
||||
}
|
||||
|
||||
@@ -239,7 +242,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
protected internal override void UIScaleChanged()
|
||||
{
|
||||
_invalidateEntries();
|
||||
// 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();
|
||||
|
||||
base.UIScaleChanged();
|
||||
}
|
||||
@@ -257,5 +266,14 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
|
||||
var font = _getFont();
|
||||
_entry.Update(font, availableSize.X * UIScale, UIScale, LineHeightScale);
|
||||
_entry.Update(_tagManager, 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(handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale, LineHeightScale);
|
||||
_entry.Draw(_tagManager, handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale, LineHeightScale);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
[Virtual]
|
||||
public class SpriteView : Control
|
||||
{
|
||||
private SpriteSystem? _sprite;
|
||||
protected SpriteSystem? SpriteSystem;
|
||||
private SharedTransformSystem? _transform;
|
||||
private readonly IEntityManager _entMan;
|
||||
protected readonly IEntityManager EntMan;
|
||||
|
||||
[ViewVariables]
|
||||
public SpriteComponent? Sprite => Entity?.Comp1;
|
||||
@@ -120,20 +120,26 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
public SpriteView()
|
||||
{
|
||||
IoCManager.Resolve(ref _entMan);
|
||||
IoCManager.Resolve(ref EntMan);
|
||||
RectClipContent = true;
|
||||
}
|
||||
|
||||
public SpriteView(IEntityManager entMan)
|
||||
{
|
||||
EntMan = 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);
|
||||
}
|
||||
@@ -154,8 +160,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;
|
||||
@@ -163,7 +169,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)
|
||||
@@ -223,11 +229,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
if (!ResolveEntity(out var uid, out var sprite, out var xform))
|
||||
return;
|
||||
|
||||
_sprite ??= _entMan.System<SpriteSystem>();
|
||||
_transform ??= _entMan.System<TransformSystem>();
|
||||
SpriteSystem ??= EntMan.System<SpriteSystem>();
|
||||
_transform ??= EntMan.System<TransformSystem>();
|
||||
|
||||
// Ensure the sprite is animated despite possible not being visible in any viewport.
|
||||
_sprite.ForceUpdate(uid);
|
||||
SpriteSystem.ForceUpdate(uid);
|
||||
|
||||
var stretchVec = Stretch switch
|
||||
{
|
||||
@@ -258,13 +264,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;
|
||||
|
||||
@@ -13,6 +13,12 @@ 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>
|
||||
|
||||
@@ -7,6 +7,7 @@ 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;
|
||||
@@ -51,6 +52,8 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
private readonly ConcurrentQueue<FormattedMessage> _messageQueue = new();
|
||||
private readonly ISawmill _logger;
|
||||
|
||||
private int _maxEntries;
|
||||
|
||||
public DebugConsole()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -78,6 +81,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
_consoleHost.AddString += OnAddString;
|
||||
_consoleHost.AddFormatted += OnAddFormatted;
|
||||
_consoleHost.ClearText += OnClearText;
|
||||
_cfg.OnValueChanged(CVars.ConMaxEntries, MaxEntriesChanged, true);
|
||||
|
||||
UserInterfaceManager.ModalRoot.AddChild(_compPopup);
|
||||
}
|
||||
@@ -89,10 +93,17 @@ 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();
|
||||
@@ -165,6 +176,15 @@ 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()
|
||||
|
||||
@@ -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>
|
||||
Matrix3 GetWorldToScreenMatrix();
|
||||
Matrix3x2 GetWorldToScreenMatrix();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a matrix that can be used to transform from view-port local to screen coordinates.
|
||||
/// </summary>
|
||||
Matrix3 GetLocalToScreenMatrix();
|
||||
Matrix3x2 GetLocalToScreenMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,17 +155,17 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
return WorldToLocalPixel(point) + GlobalPixelPosition;
|
||||
}
|
||||
|
||||
public Matrix3 GetWorldToScreenMatrix()
|
||||
public Matrix3x2 GetWorldToScreenMatrix()
|
||||
{
|
||||
if (Viewport == null)
|
||||
return Matrix3.Identity;
|
||||
return Matrix3x2.Identity;
|
||||
|
||||
return Viewport.GetWorldToLocalMatrix() * GetLocalToScreenMatrix();
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalToScreenMatrix()
|
||||
public Matrix3x2 GetLocalToScreenMatrix()
|
||||
{
|
||||
return Matrix3.CreateTransform(GlobalPixelPosition, 0, Vector2.One / _viewportResolution);
|
||||
return Matrix3Helpers.CreateTransform(GlobalPixelPosition, 0, Vector2.One / _viewportResolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, Matrix3 coordinateTransform);
|
||||
UIBox2i? scissorBox, Matrix3x2 coordinateTransform);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace Robust.Client.UserInterface
|
||||
internal struct RichTextEntry
|
||||
{
|
||||
private readonly Color _defaultColor;
|
||||
private readonly MarkupTagManager _tagManager;
|
||||
private readonly Type[]? _tagsAllowed;
|
||||
|
||||
public readonly FormattedMessage Message;
|
||||
@@ -37,7 +36,7 @@ namespace Robust.Client.UserInterface
|
||||
/// </summary>
|
||||
public ValueList<int> LineBreaks;
|
||||
|
||||
private readonly Dictionary<int, Control> _tagControls = new();
|
||||
private readonly Dictionary<int, Control>? _tagControls;
|
||||
|
||||
public RichTextEntry(FormattedMessage message, Control parent, MarkupTagManager tagManager, Type[]? tagsAllowed = null, Color? defaultColor = null)
|
||||
{
|
||||
@@ -46,23 +45,26 @@ 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.Nodes)
|
||||
foreach (var node in Message)
|
||||
{
|
||||
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.Add(nodeIndex, control);
|
||||
tagControls ??= new Dictionary<int, Control>();
|
||||
tagControls.Add(nodeIndex, control);
|
||||
}
|
||||
|
||||
_tagControls = tagControls;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -72,7 +74,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(Font defaultFont, float maxSizeX, float uiScale, float lineHeightScale = 1)
|
||||
public void Update(MarkupTagManager tagManager, Font defaultFont, float maxSizeX, float uiScale, float lineHeightScale = 1)
|
||||
{
|
||||
// This method is gonna suck due to complexity.
|
||||
// Bear with me here.
|
||||
@@ -91,10 +93,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.Nodes)
|
||||
foreach (var node in Message)
|
||||
{
|
||||
nodeIndex++;
|
||||
var text = ProcessNode(node, context);
|
||||
var text = ProcessNode(tagManager, node, context);
|
||||
|
||||
if (!context.Font.TryPeek(out var font))
|
||||
font = defaultFont;
|
||||
@@ -113,7 +115,7 @@ namespace Robust.Client.UserInterface
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_tagControls.TryGetValue(nodeIndex, out var control))
|
||||
if (_tagControls == null || !_tagControls.TryGetValue(nodeIndex, out var control))
|
||||
continue;
|
||||
|
||||
if (ProcessRune(ref this, new Rune(' '), out breakLine))
|
||||
@@ -166,6 +168,7 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
|
||||
public readonly void Draw(
|
||||
MarkupTagManager tagManager,
|
||||
DrawingHandleScreen handle,
|
||||
Font defaultFont,
|
||||
UIBox2 drawBox,
|
||||
@@ -184,10 +187,10 @@ namespace Robust.Client.UserInterface
|
||||
var controlYAdvance = 0f;
|
||||
|
||||
var nodeIndex = -1;
|
||||
foreach (var node in Message.Nodes)
|
||||
foreach (var node in Message)
|
||||
{
|
||||
nodeIndex++;
|
||||
var text = ProcessNode(node, context);
|
||||
var text = ProcessNode(tagManager, node, context);
|
||||
if (!context.Color.TryPeek(out var color) || !context.Font.TryPeek(out var font))
|
||||
{
|
||||
color = _defaultColor;
|
||||
@@ -210,7 +213,7 @@ namespace Robust.Client.UserInterface
|
||||
globalBreakCounter += 1;
|
||||
}
|
||||
|
||||
if (!_tagControls.TryGetValue(nodeIndex, out var control))
|
||||
if (_tagControls == null || !_tagControls.TryGetValue(nodeIndex, out var control))
|
||||
continue;
|
||||
|
||||
var invertedScale = 1f / uiScale;
|
||||
@@ -223,24 +226,22 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string ProcessNode(MarkupNode node, MarkupDrawingContext context)
|
||||
private readonly string ProcessNode(MarkupTagManager tagManager, 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);
|
||||
}
|
||||
|
||||
@@ -130,9 +130,9 @@ internal sealed partial class UserInterfaceManager
|
||||
{
|
||||
var total = 0;
|
||||
var drawingHandle = renderHandle.DrawingHandleScreen;
|
||||
drawingHandle.SetTransform(Matrix3.Identity);
|
||||
RenderControl(renderHandle, ref total, root, Vector2i.Zero, Color.White, null, Matrix3.Identity);
|
||||
drawingHandle.SetTransform(Matrix3.Identity);
|
||||
drawingHandle.SetTransform(Matrix3x2.Identity);
|
||||
RenderControl(renderHandle, ref total, root, Vector2i.Zero, Color.White, null, Matrix3x2.Identity);
|
||||
drawingHandle.SetTransform(Matrix3x2.Identity);
|
||||
OnPostDrawUIRoot?.Invoke(new PostDrawUIRootEventArgs(root, drawingHandle));
|
||||
|
||||
_prof.WriteValue("Controls rendered", ProfData.Int32(total));
|
||||
|
||||
@@ -123,7 +123,12 @@ internal partial class UserInterfaceManager
|
||||
|
||||
private void UpdateUIScale(WindowRoot root)
|
||||
{
|
||||
root.UIScaleSet = CalculateAutoScale(root);
|
||||
var newScale = CalculateAutoScale(root);
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
if (newScale == root.UIScaleSet)
|
||||
return;
|
||||
|
||||
root.UIScaleSet = newScale;
|
||||
_propagateUIScaleChanged(root);
|
||||
root.InvalidateMeasure();
|
||||
}
|
||||
@@ -142,7 +147,21 @@ internal partial class UserInterfaceManager
|
||||
{
|
||||
if (!_windowsToRoot.TryGetValue(windowResizedEventArgs.Window.Id, out var root))
|
||||
return;
|
||||
UpdateUIScale(root);
|
||||
|
||||
root.UIScaleUpdateNeeded = true;
|
||||
root.InvalidateMeasure();
|
||||
}
|
||||
|
||||
private void CheckRootUIScaleUpdate(WindowRoot root)
|
||||
{
|
||||
if (!root.UIScaleUpdateNeeded)
|
||||
return;
|
||||
|
||||
using (_prof.Group("UIScaleUpdate"))
|
||||
{
|
||||
UpdateUIScale(root);
|
||||
}
|
||||
|
||||
root.UIScaleUpdateNeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +216,8 @@ namespace Robust.Client.UserInterface
|
||||
{
|
||||
foreach (var root in _roots)
|
||||
{
|
||||
CheckRootUIScaleUpdate(root);
|
||||
|
||||
using (_prof.Group("Root"))
|
||||
{
|
||||
var totalUpdated = root.DoFrameUpdateRecursive(args);
|
||||
@@ -330,7 +332,7 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
|
||||
public void RenderControl(IRenderHandle renderHandle, ref int total, Control control, Vector2i position, Color modulate,
|
||||
UIBox2i? scissorBox, Matrix3 coordinateTransform)
|
||||
UIBox2i? scissorBox, Matrix3x2 coordinateTransform)
|
||||
{
|
||||
if (!control.Visible)
|
||||
{
|
||||
@@ -378,8 +380,7 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
var handle = renderHandle.DrawingHandleScreen;
|
||||
var oldXform = handle.GetTransform();
|
||||
var xform = oldXform;
|
||||
xform.Multiply(Matrix3.CreateTransform(position, Angle.Zero, Vector2.One));
|
||||
var xform = oldXform * Matrix3Helpers.CreateTransform(position, Angle.Zero, Vector2.One);
|
||||
handle.SetTransform(xform);
|
||||
modulate *= control.Modulate;
|
||||
|
||||
@@ -406,7 +407,7 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
foreach (var child in control.Children)
|
||||
{
|
||||
var pos = position + (Vector2i) coordinateTransform.Transform(child.PixelPosition);
|
||||
var pos = position + (Vector2i)Vector2.Transform(child.PixelPosition, coordinateTransform);
|
||||
control.RenderChildOverride(ref args, child.GetPositionInParent(), pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,22 +4,6 @@ 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);
|
||||
|
||||
Submodule Robust.LoaderApi updated: 99a2f4b880...86a02eef16
@@ -105,10 +105,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(uid))
|
||||
{
|
||||
Log.Error($"Tried to play audio on a terminating / deleted entity {ToPrettyString(uid)}. Trace: {Environment.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
// Move it after setting it up
|
||||
@@ -125,10 +122,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(uid))
|
||||
{
|
||||
Log.Error($"Tried to play audio on a terminating / deleted entity {ToPrettyString(uid)}. Trace: {Environment.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
XformSystem.SetCoordinates(entity, new EntityCoordinates(uid, Vector2.Zero));
|
||||
|
||||
@@ -844,7 +844,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
if (xformQuery.TryGetComponent(rootEntity, out var xform) && IsRoot(xform, mapQuery) && !HasComp<MapComponent>(rootEntity))
|
||||
{
|
||||
_transform.SetLocalPosition(xform, data.Options.TransformMatrix.Transform(xform.LocalPosition));
|
||||
_transform.SetLocalPosition(xform, Vector2.Transform(xform.LocalPosition, data.Options.TransformMatrix));
|
||||
xform.LocalRotation += data.Options.Rotation;
|
||||
}
|
||||
}
|
||||
@@ -856,7 +856,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
if (xformQuery.TryGetComponent(entity, out var xform) && IsRoot(xform, mapQuery))
|
||||
{
|
||||
// Don't want to trigger events
|
||||
xform._localPosition = data.Options.TransformMatrix.Transform(xform.LocalPosition);
|
||||
xform._localPosition = Vector2.Transform(xform.LocalPosition, data.Options.TransformMatrix);
|
||||
if (!xform.NoLocalRotation)
|
||||
xform._localRotation += data.Options.Rotation;
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
public sealed class ServerMetaDataSystem : MetaDataSystem
|
||||
{
|
||||
[Dependency] private readonly PvsSystem _pvsSystem = default!;
|
||||
private EntityQuery<MetaDataComponent> _mQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -18,7 +13,6 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
|
||||
|
||||
EntityManager.ComponentAdded += OnComponentAdded;
|
||||
EntityManager.ComponentRemoved += OnComponentRemoved;
|
||||
_mQuery = GetEntityQuery<MetaDataComponent>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<UserInterfaceUserComponent, ExpandPvsEvent>(OnBuiUserExpand);
|
||||
}
|
||||
|
||||
private void OnBuiUserExpand(Entity<UserInterfaceUserComponent> ent, ref ExpandPvsEvent args)
|
||||
{
|
||||
var buis = ent.Comp.OpenInterfaces.Keys;
|
||||
|
||||
if (buis.Count == 0)
|
||||
return;
|
||||
|
||||
args.Entities ??= new List<EntityUid>(buis.Count);
|
||||
|
||||
foreach (var ui in buis)
|
||||
{
|
||||
DebugTools.Assert(ent.Comp.OpenInterfaces[ui].Count > 0);
|
||||
DebugTools.Assert(HasComp<UserInterfaceComponent>(ui));
|
||||
args.Entities.Add(ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ internal sealed class PvsChunk
|
||||
/// <summary>
|
||||
/// The <see cref="Root"/>'s inverse world matrix.
|
||||
/// </summary>
|
||||
public Matrix3 InvWorldMatrix { get; set; }
|
||||
public Matrix3x2 InvWorldMatrix { get; set; }
|
||||
|
||||
// These are only used while populating the chunk. They aren't local variables because the chunks are pooled, so
|
||||
// the same chunk can be repopulated more than once.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -115,6 +117,16 @@ internal sealed class PvsSession(ICommonSession session, ResizableMemoryRegion<P
|
||||
/// </summary>
|
||||
public GameState? State;
|
||||
|
||||
/// <summary>
|
||||
/// The serialized <see cref="State"/> object.
|
||||
/// </summary>
|
||||
public MemoryStream? StateStream;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we should force reliable sending of the <see cref="MsgState"/>.
|
||||
/// </summary>
|
||||
public bool ForceSendReliably { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears all stored game state data. This should only be used after the game state has been serialized.
|
||||
/// </summary>
|
||||
|
||||
@@ -72,7 +72,7 @@ internal sealed partial class PvsSystem
|
||||
var xform = Transform(chunk.Root);
|
||||
DebugTools.AssertEqual(chunk.Map.Owner, xform.MapUid);
|
||||
chunk.InvWorldMatrix = xform.InvLocalMatrix;
|
||||
var worldPos = xform.LocalMatrix.Transform(chunk.Centre);
|
||||
var worldPos = Vector2.Transform(chunk.Centre, xform.LocalMatrix);
|
||||
chunk.Position = new(worldPos, xform.MapID);
|
||||
chunk.UpdateQueued = false;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ internal sealed partial class PvsSystem
|
||||
|
||||
foreach (var (grid, _) in _grids)
|
||||
{
|
||||
var localPos = _transform.GetInvWorldMatrix(grid).Transform(viewPos);
|
||||
var localPos = Vector2.Transform(viewPos, _transform.GetInvWorldMatrix(grid));
|
||||
var gridChunkEnumerator = new ChunkIndicesEnumerator(localPos, range, ChunkSize);
|
||||
while (gridChunkEnumerator.MoveNext(out var gridChunkIndices))
|
||||
{
|
||||
|
||||
@@ -75,15 +75,15 @@ namespace Robust.Server.GameStates
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CleanupDirty(ICommonSession[] sessions)
|
||||
private void CleanupDirty()
|
||||
{
|
||||
using var _ = Histogram.WithLabels("Clean Dirty").NewTimer();
|
||||
if (!CullingEnabled)
|
||||
{
|
||||
_seenAllEnts.Clear();
|
||||
foreach (var player in sessions)
|
||||
foreach (var player in _sessions)
|
||||
{
|
||||
_seenAllEnts.Add(player);
|
||||
_seenAllEnts.Add(player.Session);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,12 @@ internal sealed partial class PvsSystem
|
||||
{
|
||||
private WaitHandle? _leaveTask;
|
||||
|
||||
private void ProcessLeavePvs(ICommonSession[] sessions)
|
||||
private void ProcessLeavePvs()
|
||||
{
|
||||
if (!CullingEnabled || sessions.Length == 0)
|
||||
if (!CullingEnabled || _sessions.Length == 0)
|
||||
return;
|
||||
|
||||
DebugTools.AssertNull(_leaveTask);
|
||||
_leaveJob.Setup(sessions);
|
||||
|
||||
if (_async)
|
||||
{
|
||||
@@ -76,29 +75,19 @@ internal sealed partial class PvsSystem
|
||||
{
|
||||
public int BatchSize => 2;
|
||||
private PvsSystem _pvs = _pvs;
|
||||
public int Count => _sessions.Length;
|
||||
private PvsSession[] _sessions;
|
||||
public int Count => _pvs._sessions.Length;
|
||||
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pvs.ProcessLeavePvs(_sessions[index]);
|
||||
_pvs.ProcessLeavePvs(_pvs._sessions[index]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_pvs.Log.Log(LogLevel.Error, e, $"Caught exception while processing pvs-leave messages.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(ICommonSession[] sessions)
|
||||
{
|
||||
// Copy references to PvsSession, in case players disconnect while the job is running.
|
||||
Array.Resize(ref _sessions, sessions.Length);
|
||||
for (var i = 0; i < sessions.Length; i++)
|
||||
{
|
||||
_sessions[i] = _pvs.PlayerData[sessions[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,13 @@ internal sealed partial class PvsSystem
|
||||
/// </summary>
|
||||
private bool RecursivelyAddOverride(PvsSession session, EntityUid uid, GameTick fromTick, bool addChildren)
|
||||
{
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
if (!_xformQuery.TryGetComponent(uid, out var xform))
|
||||
{
|
||||
// Can happen if systems add deleted entities to PVS move event.
|
||||
Log.Error($"Attempted to add non-existent entity {uid} to PVS override for session {session.Session}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var parent = xform.ParentUid;
|
||||
|
||||
// First we process all parents. This is because while this entity may already have been added
|
||||
|
||||
85
Robust.Server/GameStates/PvsSystem.Send.cs
Normal file
85
Robust.Server/GameStates/PvsSystem.Send.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server.GameStates;
|
||||
|
||||
internal sealed partial class PvsSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Compress and send game states to connected clients.
|
||||
/// </summary>
|
||||
private void SendStates()
|
||||
{
|
||||
// TODO PVS make this async
|
||||
// AFAICT ForEachAsync doesn't support using a threadlocal PvsThreadResources.
|
||||
// Though if it is getting pooled, does it really matter?
|
||||
|
||||
// If this does get run async, then ProcessDisconnections() has to ensure that the job has finished before modifying
|
||||
// the sessions array
|
||||
|
||||
using var _ = Histogram.WithLabels("Send States").NewTimer();
|
||||
var opts = new ParallelOptions {MaxDegreeOfParallelism = _parallelMgr.ParallelProcessCount};
|
||||
Parallel.ForEach(_sessions, opts, _threadResourcesPool.Get, SendSessionState, _threadResourcesPool.Return);
|
||||
}
|
||||
|
||||
private PvsThreadResources SendSessionState(PvsSession data, ParallelLoopState state, PvsThreadResources resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendSessionState(data, resource.CompressionContext);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Log(LogLevel.Error, e, $"Caught exception while sending mail for {data.Session}.");
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
private void SendSessionState(PvsSession data, ZStdCompressionContext ctx)
|
||||
{
|
||||
DebugTools.AssertEqual(data.State, null);
|
||||
|
||||
// PVS benchmarks use dummy sessions.
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (data.Session.Channel is not DummyChannel)
|
||||
{
|
||||
DebugTools.AssertNotEqual(data.StateStream, null);
|
||||
var msg = new MsgState
|
||||
{
|
||||
StateStream = data.StateStream,
|
||||
ForceSendReliably = data.ForceSendReliably,
|
||||
CompressionContext = ctx
|
||||
};
|
||||
|
||||
_netMan.ServerSendMessage(msg, data.Session.Channel);
|
||||
if (msg.ShouldSendReliably())
|
||||
{
|
||||
data.RequestedFull = false;
|
||||
data.LastReceivedAck = _gameTiming.CurTick;
|
||||
lock (PendingAcks)
|
||||
{
|
||||
PendingAcks.Add(data.Session);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Always "ack" dummy sessions.
|
||||
data.LastReceivedAck = _gameTiming.CurTick;
|
||||
data.RequestedFull = false;
|
||||
lock (PendingAcks)
|
||||
{
|
||||
PendingAcks.Add(data.Session);
|
||||
}
|
||||
}
|
||||
|
||||
data.StateStream?.Dispose();
|
||||
data.StateStream = null;
|
||||
}
|
||||
}
|
||||
73
Robust.Server/GameStates/PvsSystem.Serialize.cs
Normal file
73
Robust.Server/GameStates/PvsSystem.Serialize.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Prometheus;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server.GameStates;
|
||||
|
||||
internal sealed partial class PvsSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustSerializer _serializer = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Get and serialize <see cref="GameState"/> objects for each player. Compressing & sending the states is done later.
|
||||
/// </summary>
|
||||
private void SerializeStates()
|
||||
{
|
||||
using var _ = Histogram.WithLabels("Serialize States").NewTimer();
|
||||
var opts = new ParallelOptions {MaxDegreeOfParallelism = _parallelMgr.ParallelProcessCount};
|
||||
_oldestAck = GameTick.MaxValue.Value;
|
||||
Parallel.For(-1, _sessions.Length, opts, SerializeState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get and serialize a <see cref="GameState"/> for a single session (or the current replay).
|
||||
/// </summary>
|
||||
private void SerializeState(int i)
|
||||
{
|
||||
try
|
||||
{
|
||||
var guid = i >= 0 ? _sessions[i].Session.UserId.UserId : default;
|
||||
ServerGameStateManager.PvsEventSource.Log.WorkStart(_gameTiming.CurTick.Value, i, guid);
|
||||
|
||||
if (i >= 0)
|
||||
SerializeSessionState(_sessions[i]);
|
||||
else
|
||||
_replay.Update();
|
||||
|
||||
ServerGameStateManager.PvsEventSource.Log.WorkStop(_gameTiming.CurTick.Value, i, guid);
|
||||
}
|
||||
catch (Exception e) // Catch EVERY exception
|
||||
{
|
||||
var source = i >= 0 ? _sessions[i].Session.ToString() : "replays";
|
||||
Log.Log(LogLevel.Error, e, $"Caught exception while serializing game state for {source}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get and serialize a <see cref="GameState"/> for a single session.
|
||||
/// </summary>
|
||||
private void SerializeSessionState(PvsSession data)
|
||||
{
|
||||
ComputeSessionState(data);
|
||||
InterlockedHelper.Min(ref _oldestAck, data.FromTick.Value);
|
||||
DebugTools.AssertEqual(data.StateStream, null);
|
||||
|
||||
// PVS benchmarks use dummy sessions.
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (data.Session.Channel is not DummyChannel)
|
||||
{
|
||||
data.StateStream = RobustMemoryManager.GetMemoryStream();
|
||||
_serializer.SerializeDirect(data.StateStream, data.State);
|
||||
}
|
||||
|
||||
data.ClearState();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -26,49 +25,6 @@ internal sealed partial class PvsSystem
|
||||
|
||||
private List<ICommonSession> _disconnected = new();
|
||||
|
||||
private void SendStateUpdate(ICommonSession session, PvsThreadResources resources)
|
||||
{
|
||||
var data = GetOrNewPvsSession(session);
|
||||
ComputeSessionState(data);
|
||||
|
||||
InterlockedHelper.Min(ref _oldestAck, data.FromTick.Value);
|
||||
|
||||
// actually send the state
|
||||
var msg = new MsgState
|
||||
{
|
||||
State = data.State,
|
||||
CompressionContext = resources.CompressionContext
|
||||
};
|
||||
|
||||
// PVS benchmarks use dummy sessions.
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (session.Channel != null)
|
||||
{
|
||||
_netMan.ServerSendMessage(msg, session.Channel);
|
||||
if (msg.ShouldSendReliably())
|
||||
{
|
||||
data.RequestedFull = false;
|
||||
data.LastReceivedAck = _gameTiming.CurTick;
|
||||
lock (PendingAcks)
|
||||
{
|
||||
PendingAcks.Add(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Always "ack" dummy sessions.
|
||||
data.LastReceivedAck = _gameTiming.CurTick;
|
||||
data.RequestedFull = false;
|
||||
lock (PendingAcks)
|
||||
{
|
||||
PendingAcks.Add(session);
|
||||
}
|
||||
}
|
||||
|
||||
data.ClearState();
|
||||
}
|
||||
|
||||
private PvsSession GetOrNewPvsSession(ICommonSession session)
|
||||
{
|
||||
if (!PlayerData.TryGetValue(session, out var pvsSession))
|
||||
@@ -103,7 +59,7 @@ internal sealed partial class PvsSystem
|
||||
session.PlayerStates,
|
||||
_deletedEntities);
|
||||
|
||||
session.State.ForceSendReliably = session.RequestedFull
|
||||
session.ForceSendReliably = session.RequestedFull
|
||||
|| _gameTiming.CurTick > session.LastReceivedAck + (uint) ForceAckThreshold;
|
||||
}
|
||||
|
||||
@@ -163,7 +119,7 @@ internal sealed partial class PvsSystem
|
||||
|
||||
dist = Math.Min(dist, (pos.Position - chunk.Position.Position).LengthSquared());
|
||||
|
||||
var relative = chunk.InvWorldMatrix.Transform(pos.Position) - chunk.Centre;
|
||||
var relative = Vector2.Transform(pos.Position, chunk.InvWorldMatrix) - chunk.Centre;
|
||||
relative = Vector2.Abs(relative);
|
||||
chebDist = Math.Min(chebDist, Math.Max(relative.X, relative.Y));
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Prometheus;
|
||||
using Robust.Server.Configuration;
|
||||
@@ -16,9 +14,6 @@ using Robust.Server.Replays;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
@@ -99,6 +94,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
/// </summary>
|
||||
private readonly List<GameTick> _deletedTick = new();
|
||||
|
||||
/// <summary>
|
||||
/// The sessions that are currently being processed. Note that this is in general used by parallel & async tasks.
|
||||
/// Hence player disconnection processing is deferred and only run via <see cref="ProcessDisconnections"/>.
|
||||
/// </summary>
|
||||
private PvsSession[] _sessions = default!;
|
||||
|
||||
private bool _async;
|
||||
@@ -183,52 +182,25 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
/// </summary>
|
||||
internal void SendGameStates(ICommonSession[] players)
|
||||
{
|
||||
// Wait for pending jobs and process disconnected players
|
||||
ProcessDisconnections();
|
||||
|
||||
// Ensure each session has a PvsSession entry before starting any parallel jobs.
|
||||
CacheSessionData(players);
|
||||
|
||||
// Get visible chunks, and update any dirty chunks.
|
||||
BeforeSendState();
|
||||
BeforeSerializeStates();
|
||||
|
||||
// Construct & send the game state to each player.
|
||||
SendStates(players);
|
||||
// Construct & serialize the game state for each player (and for the replay).
|
||||
SerializeStates();
|
||||
|
||||
// Compress & send the states.
|
||||
SendStates();
|
||||
|
||||
// Cull deletion history
|
||||
AfterSendState(players);
|
||||
AfterSerializeStates();
|
||||
|
||||
ProcessLeavePvs(players);
|
||||
}
|
||||
|
||||
private void SendStates(ICommonSession[] players)
|
||||
{
|
||||
using var _ = Histogram.WithLabels("Send States").NewTimer();
|
||||
|
||||
var opts = new ParallelOptions {MaxDegreeOfParallelism = _parallelMgr.ParallelProcessCount};
|
||||
_oldestAck = GameTick.MaxValue.Value;
|
||||
|
||||
// Replays process game states in parallel with players
|
||||
Parallel.For(-1, players.Length, opts, _threadResourcesPool.Get, SendPlayer, _threadResourcesPool.Return);
|
||||
|
||||
PvsThreadResources SendPlayer(int i, ParallelLoopState state, PvsThreadResources resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
var guid = i >= 0 ? players[i].UserId.UserId : default;
|
||||
ServerGameStateManager.PvsEventSource.Log.WorkStart(_gameTiming.CurTick.Value, i, guid);
|
||||
|
||||
if (i >= 0)
|
||||
SendStateUpdate(players[i], resource);
|
||||
else
|
||||
_replay.Update();
|
||||
|
||||
ServerGameStateManager.PvsEventSource.Log.WorkStop(_gameTiming.CurTick.Value, i, guid);
|
||||
}
|
||||
catch (Exception e) // Catch EVERY exception
|
||||
{
|
||||
var source = i >= 0 ? players[i].ToString() : "replays";
|
||||
Log.Log(LogLevel.Error, e, $"Caught exception while generating mail for {source}.");
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
ProcessLeavePvs();
|
||||
}
|
||||
|
||||
private void ResetParallelism(int _) => ResetParallelism();
|
||||
@@ -414,23 +386,11 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void BeforeSendState()
|
||||
private void BeforeSerializeStates()
|
||||
{
|
||||
DebugTools.Assert(_chunks.Values.All(x => Exists(x.Map) && Exists(x.Root)));
|
||||
DebugTools.Assert(_chunkSets.Keys.All(Exists));
|
||||
|
||||
_leaveTask?.WaitOne();
|
||||
_leaveTask = null;
|
||||
|
||||
foreach (var session in _disconnected)
|
||||
{
|
||||
if (PlayerData.Remove(session, out var pvsSession))
|
||||
{
|
||||
ClearSendHistory(pvsSession);
|
||||
FreeSessionDataMemory(pvsSession);
|
||||
}
|
||||
}
|
||||
|
||||
var ackJob = ProcessQueuedAcks();
|
||||
|
||||
// Figure out what chunks players can see and cache some chunk data.
|
||||
@@ -443,6 +403,21 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ackJob?.WaitOne();
|
||||
}
|
||||
|
||||
internal void ProcessDisconnections()
|
||||
{
|
||||
_leaveTask?.WaitOne();
|
||||
_leaveTask = null;
|
||||
|
||||
foreach (var session in _disconnected)
|
||||
{
|
||||
if (PlayerData.Remove(session, out var pvsSession))
|
||||
{
|
||||
ClearSendHistory(pvsSession);
|
||||
FreeSessionDataMemory(pvsSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void CacheSessionData(ICommonSession[] players)
|
||||
{
|
||||
Array.Resize(ref _sessions, players.Length);
|
||||
@@ -452,9 +427,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void AfterSendState(ICommonSession[] players)
|
||||
private void AfterSerializeStates()
|
||||
{
|
||||
CleanupDirty(players);
|
||||
CleanupDirty();
|
||||
|
||||
if (_oldestAck == GameTick.MaxValue.Value)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Server.Maps
|
||||
get => _offset;
|
||||
set
|
||||
{
|
||||
TransformMatrix = Matrix3.CreateTransform(value, Rotation);
|
||||
TransformMatrix = Matrix3Helpers.CreateTransform(value, Rotation);
|
||||
_offset = value;
|
||||
}
|
||||
}
|
||||
@@ -37,14 +37,14 @@ namespace Robust.Server.Maps
|
||||
get => _rotation;
|
||||
set
|
||||
{
|
||||
TransformMatrix = Matrix3.CreateTransform(Offset, value);
|
||||
TransformMatrix = Matrix3Helpers.CreateTransform(Offset, value);
|
||||
_rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Angle _rotation = Angle.Zero;
|
||||
|
||||
public Matrix3 TransformMatrix { get; set; } = Matrix3.Identity;
|
||||
public Matrix3x2 TransformMatrix { get; set; } = Matrix3x2.Identity;
|
||||
|
||||
/// <summary>
|
||||
/// If there is a map entity serialized should we also load it.
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed partial class GridFixtureSystem
|
||||
TransformComponent? xformA = null,
|
||||
TransformComponent? xformB = null)
|
||||
{
|
||||
var matrix = Matrix3.CreateTransform(offset, rotation);
|
||||
var matrix = Matrix3Helpers.CreateTransform(offset, rotation);
|
||||
Merge(gridAUid, gridBUid, matrix, gridA, gridB, xformA, xformB);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed partial class GridFixtureSystem
|
||||
public void Merge(
|
||||
EntityUid gridAUid,
|
||||
EntityUid gridBUid,
|
||||
Matrix3 matrix,
|
||||
Matrix3x2 matrix,
|
||||
MapGridComponent? gridA = null,
|
||||
MapGridComponent? gridB = null,
|
||||
TransformComponent? xformA = null,
|
||||
@@ -63,7 +63,7 @@ public sealed partial class GridFixtureSystem
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
var offsetTile = matrix.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector);
|
||||
var offsetTile = Vector2.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector, matrix);
|
||||
tiles.Add((offsetTile.Floored(), tileRef.Value.Tile));
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public sealed partial class GridFixtureSystem
|
||||
if (snapgrid == null || snapgrid.Count == 0)
|
||||
continue;
|
||||
|
||||
var offsetTile = matrix.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector);
|
||||
var offsetTile = Vector2.Transform(new Vector2(tileRef.Value.GridIndices.X, tileRef.Value.GridIndices.Y) + gridA.TileSizeHalfVector, matrix);
|
||||
var tileIndex = offsetTile.Floored();
|
||||
|
||||
for (var j = snapgrid.Count - 1; j >= 0; j--)
|
||||
@@ -124,7 +124,7 @@ public sealed partial class GridFixtureSystem
|
||||
if (entXform.ParentUid != gridBUid ||
|
||||
!bounds.Contains(entXform.LocalPosition)) continue;
|
||||
|
||||
var newPos = matrix.Transform(entXform.LocalPosition);
|
||||
var newPos = Vector2.Transform(entXform.LocalPosition, matrix);
|
||||
|
||||
_xformSystem.SetCoordinates(ent, entXform, new EntityCoordinates(gridAUid, newPos), entXform.LocalRotation + rotationDiff, oldParent: xformB, newParent: xformA);
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Robust.Server.Placement
|
||||
}
|
||||
}
|
||||
|
||||
var created = _entityManager.Spawn(entityTemplateName, _xformSystem.ToMapCoordinates(coordinates), rotation: dirRcv.ToAngle());
|
||||
var created = _entityManager.SpawnAttachedTo(entityTemplateName, coordinates, rotation: dirRcv.ToAngle());
|
||||
|
||||
var placementCreateEvent = new PlacementEntityEvent(created, coordinates, PlacementEventAction.Create, msg.MsgChannel.UserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementCreateEvent);
|
||||
|
||||
@@ -90,18 +90,18 @@ namespace Robust.Server.Player
|
||||
_cfg.SyncConnectingClient(args.Channel);
|
||||
}
|
||||
|
||||
private void EndSession(object? sender, NetChannelArgs args)
|
||||
{
|
||||
EndSession(args.Channel.UserId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends a clients session, and disconnects them.
|
||||
/// </summary>
|
||||
private void EndSession(object? sender, NetChannelArgs args)
|
||||
internal void EndSession(NetUserId user)
|
||||
{
|
||||
if (!TryGetSessionByChannel(args.Channel, out var session))
|
||||
{
|
||||
if (!TryGetSessionById(user, out var session))
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure nothing got messed up during the life of the session
|
||||
DebugTools.Assert(session.Channel == args.Channel);
|
||||
|
||||
SetStatus(session, SessionStatus.Disconnected);
|
||||
SetAttachedEntity(session, null, out _, true);
|
||||
@@ -143,7 +143,6 @@ namespace Robust.Server.Player
|
||||
list.Add(info);
|
||||
}
|
||||
netMsg.Plyrs = list;
|
||||
netMsg.PlyCount = (byte)list.Count;
|
||||
|
||||
channel.SendMessage(netMsg);
|
||||
}
|
||||
@@ -159,5 +158,32 @@ namespace Robust.Server.Player
|
||||
session = actor.PlayerSession;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal ICommonSession AddDummySession(NetUserId user, string name)
|
||||
{
|
||||
#if FULL_RELEASE
|
||||
// Lets not make it completely trivial to fake player counts.
|
||||
throw new NotSupportedException();
|
||||
#endif
|
||||
Lock.EnterWriteLock();
|
||||
DummySession session;
|
||||
try
|
||||
{
|
||||
UserIdMap[name] = user;
|
||||
if (!PlayerData.TryGetValue(user, out var data))
|
||||
PlayerData[user] = data = new(user, name);
|
||||
|
||||
session = new DummySession(user, name, data);
|
||||
InternalSessions.Add(user, session);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Lock.ExitWriteLock();
|
||||
}
|
||||
|
||||
UpdateState(session);
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ namespace Robust.Server
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPool.SetMinThreads(Environment.ProcessorCount * 2, Environment.ProcessorCount);
|
||||
|
||||
ParsedMain(parsed, contentStart, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ internal sealed class HubManager
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorText = await response.Content.ReadAsStringAsync();
|
||||
_sawmill.Error("Error status while advertising server: [{StatusCode}] {ErrorText}, to {HubUrl}",
|
||||
_sawmill.Error("Error status while advertising server: [{StatusCode}] {ErrorText}, from {HubUrl}",
|
||||
response.StatusCode,
|
||||
errorText,
|
||||
hubUrl);
|
||||
|
||||
@@ -312,14 +312,14 @@ namespace Robust.Server.ServerStatus
|
||||
}
|
||||
|
||||
// Only call this if the download URL is not available!
|
||||
private async Task<AczManifestInfo?> PrepareAcz()
|
||||
private async Task<AczManifestInfo?> PrepareAcz(bool optional = false)
|
||||
{
|
||||
// Take the ACZ lock asynchronously
|
||||
await _aczLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
// Setting this now ensures that it won't fail repeatedly on exceptions/etc.
|
||||
if (_aczPrepareAttempted)
|
||||
if (_aczPrepareAttempted || optional)
|
||||
return _aczPrepared;
|
||||
|
||||
_aczPrepareAttempted = true;
|
||||
|
||||
@@ -80,8 +80,7 @@ namespace Robust.Server.ServerStatus
|
||||
if (string.IsNullOrEmpty(downloadUrl))
|
||||
{
|
||||
var query = HttpUtility.ParseQueryString(context.Url.Query);
|
||||
var optional = query.Keys;
|
||||
buildInfo = await PrepareACZBuildInfo();
|
||||
buildInfo = await PrepareACZBuildInfo(optional: query.Get("can_skip_build") == "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -129,9 +128,9 @@ namespace Robust.Server.ServerStatus
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<JsonObject?> PrepareACZBuildInfo()
|
||||
private async Task<JsonObject?> PrepareACZBuildInfo(bool optional)
|
||||
{
|
||||
var acm = await PrepareAcz();
|
||||
var acm = await PrepareAcz(optional);
|
||||
if (acm == null) return null;
|
||||
|
||||
// Fork ID is an interesting case, we don't want to cause too many redownloads but we also don't want to pollute disk.
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
{
|
||||
eventRaise = @"
|
||||
var ev = new AfterAutoHandleStateEvent(args.Current);
|
||||
EntityManager.EventBus.RaiseComponentEvent(component, ref ev);";
|
||||
EntityManager.EventBus.RaiseComponentEvent(uid, component, ref ev);";
|
||||
}
|
||||
|
||||
return $@"// <auto-generated />
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Robust.Shared.Maths
|
||||
public readonly Vector2 BottomLeft => Origin + Rotation.RotateVec(Box.BottomLeft - Origin);
|
||||
public readonly Vector2 Center => Origin + Rotation.RotateVec((Box.BottomLeft + Box.TopRight)/2 - Origin);
|
||||
|
||||
public Matrix3 Transform => Matrix3.CreateTransform(Origin - Rotation.RotateVec(Origin), Rotation);
|
||||
public Matrix3x2 Transform => Matrix3Helpers.CreateTransform(Origin - Rotation.RotateVec(Origin), Rotation);
|
||||
|
||||
public Box2Rotated(Vector2 bottomLeft, Vector2 topRight)
|
||||
: this(new Box2(bottomLeft, topRight))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
193
Robust.Shared.Maths/Matrix3Helpers.cs
Normal file
193
Robust.Shared.Maths/Matrix3Helpers.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
|
||||
namespace Robust.Shared.Maths;
|
||||
|
||||
public static class Matrix3Helpers
|
||||
{
|
||||
public static bool EqualsApprox(this Matrix3x2 a, Matrix3x2 b, float tolerance = 1e-6f)
|
||||
{
|
||||
return
|
||||
Math.Abs(a.M11 - b.M11) <= tolerance &&
|
||||
Math.Abs(a.M12 - b.M12) <= tolerance &&
|
||||
Math.Abs(a.M21 - b.M21) <= tolerance &&
|
||||
Math.Abs(a.M22 - b.M22) <= tolerance &&
|
||||
Math.Abs(a.M31 - b.M31) <= tolerance &&
|
||||
Math.Abs(a.M32 - b.M32) <= tolerance;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsApprox(this Matrix3x2 a, Matrix3x2 b, double tolerance)
|
||||
{
|
||||
return a.EqualsApprox(b, (float) tolerance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Box2 TransformBox(this Matrix3x2 refFromBox, Box2Rotated box)
|
||||
{
|
||||
return (box.Transform * refFromBox).TransformBox(box.Box);
|
||||
}
|
||||
|
||||
public static Box2 TransformBox(this Matrix3x2 refFromBox, in Box2 box)
|
||||
{
|
||||
// Do transformation on all 4 corners of the box at once.
|
||||
// Then min/max the results to get the new AABB.
|
||||
|
||||
var boxVec = Unsafe.As<Box2, Vector128<float>>(ref Unsafe.AsRef(in box));
|
||||
|
||||
// Convert box into list of X and Y values for each of the 4 corners
|
||||
var allX = Vector128.Shuffle(boxVec, Vector128.Create(0, 0, 2, 2));
|
||||
var allY = Vector128.Shuffle(boxVec, Vector128.Create(1, 3, 3, 1));
|
||||
|
||||
// Transform coordinates
|
||||
var modX = allX * Vector128.Create(refFromBox.M11);
|
||||
var modY = allX * Vector128.Create(refFromBox.M12);
|
||||
modX += allY * Vector128.Create(refFromBox.M21);
|
||||
modY += allY * Vector128.Create(refFromBox.M22);
|
||||
modX += Vector128.Create(refFromBox.M31);
|
||||
modY += Vector128.Create(refFromBox.M32);
|
||||
|
||||
// Get bounding box by finding the min and max X and Y values.
|
||||
var l = SimdHelpers.MinHorizontal128(modX);
|
||||
var b = SimdHelpers.MinHorizontal128(modY);
|
||||
var r = SimdHelpers.MaxHorizontal128(modX);
|
||||
var t = SimdHelpers.MaxHorizontal128(modY);
|
||||
|
||||
var lbrt = SimdHelpers.MergeRows128(l, b, r, t);
|
||||
return Unsafe.As<Vector128<float>, Box2>(ref lbrt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation of the Matrix. Will have some precision loss.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Angle Rotation(this Matrix3x2 t)
|
||||
{
|
||||
return new Vector2(t.M11, t.M12).ToAngle();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateTransform(float posX, float posY, double angle, float scaleX = 1, float scaleY = 1)
|
||||
{
|
||||
// returns a matrix that is equivalent to returning CreateScale(scale) * CreateRotation(angle) * CreateTranslation(posX, posY)
|
||||
|
||||
var sin = (float) Math.Sin(angle);
|
||||
var cos = (float) Math.Cos(angle);
|
||||
|
||||
return new Matrix3x2
|
||||
{
|
||||
M11 = cos * scaleX,
|
||||
M21 = -sin * scaleY,
|
||||
M31 = posX,
|
||||
M12 = sin * scaleX,
|
||||
M22 = cos * scaleY,
|
||||
M32 = posY,
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateTransform(in Vector2 position, in Angle angle)
|
||||
{
|
||||
// Rounding moment
|
||||
return angle.Theta switch
|
||||
{
|
||||
-Math.PI / 2 => new Matrix3x2(0f, -1f, 1, 0, position.X, position.Y),
|
||||
Math.PI / 2 => new Matrix3x2(0f, 1f, -1f, 0f, position.X, position.Y),
|
||||
Math.PI => new Matrix3x2(-1f, 0f, 0f, -1f, position.X, position.Y),
|
||||
_ => CreateTransform(position.X, position.Y, (float) angle.Theta)
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateTransform(in Vector2 position, in Angle angle, in Vector2 scale)
|
||||
{
|
||||
return CreateTransform(position.X, position.Y, (float)angle.Theta, scale.X, scale.Y);
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateInverseTransform(float posX, float posY, double angle, float scaleX = 1, float scaleY = 1)
|
||||
{
|
||||
// returns a matrix that is equivalent to returning CreateTranslation(-posX, -posY) * CreateRotation(-angle) * CreateScale(1/scaleX, 1/scaleY)
|
||||
|
||||
var sin = (float) Math.Sin(angle);
|
||||
var cos = (float) Math.Cos(angle);
|
||||
|
||||
return new Matrix3x2
|
||||
{
|
||||
M11 = cos / scaleX,
|
||||
M21 = sin / scaleX,
|
||||
M31 = - (posX * cos + posY * sin) / scaleX,
|
||||
M12 = -sin / scaleY,
|
||||
M22 = cos / scaleY,
|
||||
M32 = (posX * sin - posY * cos) / scaleY,
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateInverseTransform(in Vector2 position, in Angle angle)
|
||||
{
|
||||
return CreateInverseTransform(position.X, position.Y, (float)angle.Theta);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateInverseTransform(in Vector2 position, in Angle angle, in Vector2 scale)
|
||||
{
|
||||
return CreateInverseTransform(position.X, position.Y, (float)angle.Theta, scale.X, scale.Y);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateTranslation(float x, float y)
|
||||
{
|
||||
return new Matrix3x2 {
|
||||
M11 = 1,
|
||||
M12 = 0,
|
||||
M21 = 0,
|
||||
M22 = 1,
|
||||
M31 = x,
|
||||
M32 = y
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateTranslation(Vector2 vector)
|
||||
{
|
||||
return CreateTranslation(vector.X, vector.Y);
|
||||
}
|
||||
|
||||
public static Matrix3x2 CreateRotation(double angle)
|
||||
{
|
||||
var cos = (float) Math.Cos(angle);
|
||||
var sin = (float) Math.Sin(angle);
|
||||
return new Matrix3x2 {
|
||||
M11 = cos,
|
||||
M12 = sin,
|
||||
M21 = -sin,
|
||||
M22 = cos,
|
||||
M31 = 0,
|
||||
M32 = 0
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateScale(float x, float y)
|
||||
{
|
||||
return new Matrix3x2 {
|
||||
M11 = x,
|
||||
M12 = 0,
|
||||
M21 = 0,
|
||||
M22 = y,
|
||||
M31 = 0,
|
||||
M32 = 0
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateScale(in Vector2 scale)
|
||||
{
|
||||
return CreateScale(scale.X, scale.Y);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#region --- License ---
|
||||
#region --- License ---
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 - 2008 The Open Toolkit library.
|
||||
@@ -26,6 +26,7 @@ SOFTWARE.
|
||||
#endregion --- License ---
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Xml.Serialization;
|
||||
@@ -73,9 +74,9 @@ namespace Robust.Shared.Maths
|
||||
: this(new Vector3(x, y, z), w) { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Quaternion(ref Matrix3 matrix)
|
||||
public Quaternion(ref Matrix3x2 matrix)
|
||||
{
|
||||
var scale = Math.Pow(matrix.Determinant, 1.0d / 3.0d);
|
||||
var scale = Math.Pow(matrix.GetDeterminant(), 1.0d / 3.0d);
|
||||
float x, y, z;
|
||||
|
||||
w = (float) (Math.Sqrt(Math.Max(0, scale + matrix[0, 0] + matrix[1, 1] + matrix[2, 2])) / 2);
|
||||
|
||||
@@ -39,5 +39,5 @@ public sealed class AutoNetworkedFieldAttribute : Attribute
|
||||
/// <see cref="AutoGenerateComponentStateAttribute.RaiseAfterAutoHandleState"/> is true, so that other systems
|
||||
/// can have effects after handling state without having to redefine all replication.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
[ByRefEvent, ComponentEvent]
|
||||
public record struct AfterAutoHandleStateEvent(IComponentState State);
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Robust.Shared
|
||||
/// <seealso cref="NetMtuExpand"/>
|
||||
/// <seealso cref="NetMtuIpv6"/>
|
||||
public static readonly CVarDef<int> NetMtu =
|
||||
CVarDef.Create("net.mtu", 900, CVar.ARCHIVE);
|
||||
CVarDef.Create("net.mtu", 700, CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum UDP payload size to send by default, for IPv6.
|
||||
@@ -1374,7 +1374,7 @@ namespace Robust.Shared
|
||||
/// the purpose of using an atlas if it gets too small.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> ResRSIAtlasSize =
|
||||
CVarDef.Create("res.rsi_atlas_size", 8192, CVar.CLIENTONLY);
|
||||
CVarDef.Create("res.rsi_atlas_size", 12288, CVar.CLIENTONLY);
|
||||
|
||||
// TODO: Currently unimplemented.
|
||||
/// <summary>
|
||||
@@ -1560,6 +1560,12 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<int> ConCompletionMargin =
|
||||
CVarDef.Create("con.completion_margin", 3, CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of entries stored by the debug console.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> ConMaxEntries =
|
||||
CVarDef.Create("con.max_entries", 3_000, CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* THREAD
|
||||
*/
|
||||
@@ -1641,6 +1647,13 @@ namespace Robust.Shared
|
||||
true,
|
||||
CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// How many milliseconds we will spend moving forward from the nearest checkpoint or current position.
|
||||
/// We will spend this time when scrubbing the timeline per game tick. This limits CPU usage / locking up and
|
||||
/// improves responsiveness
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> ReplayMaxScrubTime = CVarDef.Create("replay.max_scrub_time", 10);
|
||||
|
||||
/// <summary>
|
||||
/// Determines the threshold before visual events (muzzle flashes, chat pop-ups, etc) are suppressed when
|
||||
/// jumping forward in time. Jumps larger than this will simply skip directly to the target tick.
|
||||
|
||||
304
Robust.Shared/Collections/RingBufferList.cs
Normal file
304
Robust.Shared/Collections/RingBufferList.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Utility;
|
||||
using ArgumentNullException = System.ArgumentNullException;
|
||||
|
||||
namespace Robust.Shared.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Datastructure that acts like a <see cref="List{T}"/>, but is actually stored as a ring buffer internally.
|
||||
/// This facilitates efficient removal from the start.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of item contained in the collection.</typeparam>
|
||||
internal sealed class RingBufferList<T> : IList<T>
|
||||
{
|
||||
private T[] _items;
|
||||
private int _read;
|
||||
private int _write;
|
||||
|
||||
public RingBufferList(int capacity)
|
||||
{
|
||||
_items = new T[capacity];
|
||||
}
|
||||
|
||||
public RingBufferList()
|
||||
{
|
||||
_items = [];
|
||||
}
|
||||
|
||||
public int Capacity => _items.Length;
|
||||
|
||||
private bool IsFull => _items.Length == 0 || NextIndex(_write) == _read;
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
if (IsFull)
|
||||
Expand();
|
||||
|
||||
DebugTools.Assert(!IsFull);
|
||||
|
||||
_items[_write] = item;
|
||||
_write = NextIndex(_write);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_read = 0;
|
||||
_write = 0;
|
||||
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||
Array.Clear(_items);
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return IndexOf(item) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(array);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex);
|
||||
|
||||
CopyTo(array.AsSpan(arrayIndex));
|
||||
}
|
||||
|
||||
private void CopyTo(Span<T> dest)
|
||||
{
|
||||
if (dest.Length < Count)
|
||||
throw new ArgumentException("Not enough elements in destination!");
|
||||
|
||||
var i = 0;
|
||||
foreach (var item in this)
|
||||
{
|
||||
dest[i++] = item;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
var index = IndexOf(item);
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
var length = _write - _read;
|
||||
if (length >= 0)
|
||||
return length;
|
||||
|
||||
return length + _items.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var containedItem in this)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(item, containedItem))
|
||||
return i;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
var length = Count;
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, length);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||
_items[_read] = default!;
|
||||
|
||||
_read = NextIndex(_read);
|
||||
}
|
||||
else if (index == length - 1)
|
||||
{
|
||||
_write = WrapInv(_write - 1);
|
||||
|
||||
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||
_items[_write] = default!;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If past me had better foresight I wouldn't be spending so much effort writing this right now.
|
||||
|
||||
var realIdx = RealIndex(index);
|
||||
var origValue = _items[realIdx];
|
||||
T result;
|
||||
|
||||
if (realIdx < _read)
|
||||
{
|
||||
// Scenario one: to-remove index is after break.
|
||||
// One shift is needed.
|
||||
// v
|
||||
// X X X O X X
|
||||
// W R
|
||||
DebugTools.Assert(_write < _read);
|
||||
|
||||
result = ShiftDown(_items.AsSpan()[realIdx.._write], default!);
|
||||
}
|
||||
else if (_write < _read)
|
||||
{
|
||||
// Scenario two: to-remove index is before break, but write is after.
|
||||
// Two shifts are needed.
|
||||
// v
|
||||
// X O X X X X
|
||||
// W R
|
||||
|
||||
var fromEnd = ShiftDown(_items.AsSpan(0, _write), default!);
|
||||
result = ShiftDown(_items.AsSpan(realIdx), fromEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scenario two: array is contiguous.
|
||||
// One shift is needed.
|
||||
// v
|
||||
// X X X X O O
|
||||
// R W
|
||||
|
||||
result = ShiftDown(_items.AsSpan()[realIdx.._write], default!);
|
||||
}
|
||||
|
||||
// Just make sure we didn't bulldozer something.
|
||||
DebugTools.Assert(EqualityComparer<T>.Default.Equals(origValue, result));
|
||||
|
||||
_write = WrapInv(_write - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static T ShiftDown(Span<T> span, T substitution)
|
||||
{
|
||||
if (span.Length == 0)
|
||||
return substitution;
|
||||
|
||||
var first = span[0];
|
||||
span[1..].CopyTo(span[..^1]);
|
||||
span[^1] = substitution!;
|
||||
return first;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => GetSlot(index);
|
||||
set => GetSlot(index) = value;
|
||||
}
|
||||
|
||||
private ref T GetSlot(int index)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, Count);
|
||||
|
||||
return ref _items[RealIndex(index)];
|
||||
}
|
||||
|
||||
private int RealIndex(int index)
|
||||
{
|
||||
return Wrap(index + _read);
|
||||
}
|
||||
|
||||
private int NextIndex(int index) => Wrap(index + 1);
|
||||
|
||||
private int Wrap(int index)
|
||||
{
|
||||
if (index >= _items.Length)
|
||||
index -= _items.Length;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private int WrapInv(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
index = _items.Length - 1;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private void Expand()
|
||||
{
|
||||
var prevSize = _items.Length;
|
||||
var newSize = Math.Max(4, prevSize * 2);
|
||||
Array.Resize(ref _items, newSize);
|
||||
|
||||
if (_write >= _read)
|
||||
return;
|
||||
|
||||
// Write is behind read pointer, so we need to copy the items to be after the read pointer.
|
||||
var toCopy = _items.AsSpan(0, _write);
|
||||
var copyDest = _items.AsSpan(prevSize);
|
||||
toCopy.CopyTo(copyDest);
|
||||
|
||||
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||
toCopy.Clear();
|
||||
|
||||
_write += prevSize;
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
private readonly RingBufferList<T> _ringBufferList;
|
||||
private int _readPos;
|
||||
|
||||
internal Enumerator(RingBufferList<T> ringBufferList)
|
||||
{
|
||||
_ringBufferList = ringBufferList;
|
||||
_readPos = _ringBufferList._read - 1;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_readPos = _ringBufferList.NextIndex(_readPos);
|
||||
return _readPos != _ringBufferList._write;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this = new Enumerator(_ringBufferList);
|
||||
}
|
||||
|
||||
public ref T Current => ref _ringBufferList._items[_readPos];
|
||||
|
||||
T IEnumerator<T>.Current => Current;
|
||||
object? IEnumerator.Current => Current;
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,7 +357,7 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
{
|
||||
var (_, treeRot, matrix) = XformSystem.GetWorldPositionRotationInvMatrix(treeUid);
|
||||
var relativeAngle = new Angle(-treeRot.Theta).RotateVec(ray.Direction);
|
||||
var treeRay = new Ray(matrix.Transform(ray.Position), relativeAngle);
|
||||
var treeRay = new Ray(Vector2.Transform(ray.Position, matrix), relativeAngle);
|
||||
comp.Tree.QueryRay(ref queryState, QueryCallback, treeRay);
|
||||
if (returnOnFirstHit && queryState.List.Count > 0)
|
||||
break;
|
||||
|
||||
@@ -140,4 +140,47 @@ namespace Robust.Shared.Configuration
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CVarSubsCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override string Command => "cvar_subs";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-cvar_subs-invalid-args"));
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args[0];
|
||||
var subs = ((ConfigurationManager)_cfg).GetSubs(name);
|
||||
|
||||
foreach (var @delegate in subs)
|
||||
{
|
||||
shell.WriteLine(ShowDelegateInfo(@delegate));
|
||||
}
|
||||
}
|
||||
|
||||
private static string ShowDelegateInfo(Delegate del)
|
||||
{
|
||||
return $"{del}: {del.Method} -> {del.Target}";
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
return CompletionResult.FromHintOptions(
|
||||
_cfg.GetRegisteredCVars()
|
||||
.Select(c => new CompletionOption(c))
|
||||
.OrderBy(c => c.Value),
|
||||
Loc.GetString("cmd-cvar_subs-arg-name"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +97,23 @@ namespace Robust.Shared.Configuration
|
||||
{
|
||||
// overwrite the value with the saved one
|
||||
var oldValue = GetConfigVarValue(cfgVar);
|
||||
changedInvokes.Add(SetupInvokeValueChanged(cfgVar, value, oldValue));
|
||||
cfgVar.Value = value;
|
||||
|
||||
var convertedValue = value;
|
||||
if (cfgVar.Type != value.GetType())
|
||||
{
|
||||
try
|
||||
{
|
||||
convertedValue = ConvertToCVarType(value, cfgVar.Type!);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_sawmill.Error($"TOML parsed cvar does not match registered cvar type. Name: {cvar}. Code Type: {cfgVar.Type}. Toml type: {value.GetType()}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
changedInvokes.Add(SetupInvokeValueChanged(cfgVar, convertedValue, oldValue));
|
||||
cfgVar.Value = convertedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -777,6 +792,24 @@ namespace Robust.Shared.Configuration
|
||||
return Convert.ChangeType(value, cVar);
|
||||
}
|
||||
|
||||
internal List<Delegate> GetSubs(string name)
|
||||
{
|
||||
using (Lock.ReadGuard())
|
||||
{
|
||||
var list = new List<Delegate>();
|
||||
|
||||
if (!_configVars.TryGetValue(name, out var cVar))
|
||||
throw new InvalidConfigurationException($"Trying to get unregistered variable '{name}'");
|
||||
|
||||
foreach (var entry in cVar.ValueChanged.Entries)
|
||||
{
|
||||
list.Add((Delegate) entry.Equality!);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the data for a single configuration variable.
|
||||
/// </summary>
|
||||
|
||||
@@ -34,7 +34,7 @@ internal sealed class DumpEventTablesCommand : LocalizedCommands
|
||||
{
|
||||
shell.WriteLine($"{evType}:");
|
||||
|
||||
var idx = comps;
|
||||
var idx = comps.Start;
|
||||
while (idx != -1)
|
||||
{
|
||||
ref var entry = ref table.ComponentLists[idx];
|
||||
|
||||
@@ -56,7 +56,7 @@ internal sealed class TeleportCommand : LocalizedCommands
|
||||
|
||||
if (_map.TryFindGridAt(mapId, position, out var gridUid, out var grid))
|
||||
{
|
||||
var gridPos = xformSystem.GetInvWorldMatrix(gridUid).Transform(position);
|
||||
var gridPos = Vector2.Transform(position, xformSystem.GetInvWorldMatrix(gridUid));
|
||||
|
||||
xformSystem.SetCoordinates(entity, transform, new EntityCoordinates(gridUid, gridPos));
|
||||
}
|
||||
@@ -174,12 +174,7 @@ public sealed class TeleportToCommand : LocalizedCommands
|
||||
|
||||
var hint = args.Length == 1 ? "cmd-tpto-destination-hint" : "cmd-tpto-victim-hint";
|
||||
hint = Loc.GetString(hint);
|
||||
|
||||
var opts = CompletionResult.FromHintOptions(users, hint);
|
||||
if (last != string.Empty && !NetEntity.TryParse(last, out _))
|
||||
return opts;
|
||||
|
||||
return CompletionResult.FromHintOptions(opts.Options.Concat(CompletionHelper.NetEntities(last, _entities)), hint);
|
||||
return CompletionResult.FromHintOptions(users, hint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,27 +189,45 @@ public static class CompletionHelper
|
||||
return Components<MapComponent>(string.Empty, entManager);
|
||||
}
|
||||
|
||||
public static IEnumerable<CompletionOption> NetEntities(string text, IEntityManager? entManager = null)
|
||||
/// <summary>
|
||||
/// Return all existing entities as possible completions. You should generally avoid using this unless you need to.
|
||||
/// </summary>
|
||||
public static IEnumerable<CompletionOption> NetEntities(string text, IEntityManager? entManager = null, int limit = 20)
|
||||
{
|
||||
return Components<MetaDataComponent>(text, entManager);
|
||||
}
|
||||
if (!NetEntity.TryParse(text, out _))
|
||||
yield break;
|
||||
|
||||
public static IEnumerable<CompletionOption> Components<T>(string text, IEntityManager? entManager = null) where T : IComponent
|
||||
{
|
||||
IoCManager.Resolve(ref entManager);
|
||||
var query = entManager.AllEntityQueryEnumerator<MetaDataComponent>();
|
||||
|
||||
var query = entManager.AllEntityQueryEnumerator<T, MetaDataComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _, out var metadata))
|
||||
var i = 0;
|
||||
while (i < limit && query.MoveNext(out var metadata))
|
||||
{
|
||||
if (!entManager.TryGetNetEntity(uid, out var netEntity, metadata: metadata))
|
||||
continue;
|
||||
|
||||
var netString = netEntity.Value.ToString();
|
||||
|
||||
var netString = metadata.NetEntity.ToString();
|
||||
if (!netString.StartsWith(text))
|
||||
continue;
|
||||
|
||||
i++;
|
||||
yield return new CompletionOption(netString, metadata.EntityName);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CompletionOption> Components<T>(string text, IEntityManager? entManager = null, int limit = 20) where T : IComponent
|
||||
{
|
||||
if (!NetEntity.TryParse(text, out _))
|
||||
yield break;
|
||||
|
||||
IoCManager.Resolve(ref entManager);
|
||||
var query = entManager.AllEntityQueryEnumerator<T, MetaDataComponent>();
|
||||
|
||||
var i = 0;
|
||||
while (i < limit && query.MoveNext(out _, out var metadata))
|
||||
{
|
||||
var netString = metadata.NetEntity.ToString();
|
||||
if (!netString.StartsWith(text))
|
||||
continue;
|
||||
|
||||
i++;
|
||||
yield return new CompletionOption(netString, metadata.EntityName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ public abstract partial class SharedContainerSystem
|
||||
|
||||
DebugTools.AssertOwner(container.Owner, containerXform);
|
||||
DebugTools.AssertOwner(toInsert, physics);
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(GetNetEntity(toInsert)));
|
||||
DebugTools.Assert(container.Manager.Containers.ContainsKey(container.ID));
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(GetNetEntity(toInsert)), "entity is expected");
|
||||
DebugTools.Assert(container.Manager.Containers.ContainsKey(container.ID), "manager does not own the container");
|
||||
|
||||
// If someone is attempting to insert an entity into a container that is getting deleted, then we will
|
||||
// automatically delete that entity. I.e., the insertion automatically "succeeds" and both entities get deleted.
|
||||
@@ -49,14 +49,14 @@ public abstract partial class SharedContainerSystem
|
||||
|
||||
if (!TryComp(container.Owner, out MetaDataComponent? ownerMeta))
|
||||
{
|
||||
Log.Error($"Attempted to insert an entity {ToPrettyString(toInsert)} into a non-existent entity.");
|
||||
Log.Error($"Attempted to insert an entity {ToPrettyString(toInsert)} into a non-existent entity. Trace: {Environment.StackTrace}");
|
||||
QueueDel(toInsert);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ownerMeta.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
Log.Error($"Attempted to insert an entity {ToPrettyString(toInsert)} into an entity that is terminating. Entity: {ToPrettyString(container.Owner)}.");
|
||||
Log.Error($"Attempted to insert an entity {ToPrettyString(toInsert)} into an entity that is terminating. Entity: {ToPrettyString(container.Owner)}. Trace: {Environment.StackTrace}");
|
||||
QueueDel(toInsert);
|
||||
return false;
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public abstract partial class SharedContainerSystem
|
||||
|
||||
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
Log.Error($"Attempted to insert a terminating entity {ToPrettyString(uid)} into a container {container.ID} in entity: {ToPrettyString(container.Owner)}.");
|
||||
Log.Error($"Attempted to insert a terminating entity {ToPrettyString(uid)} into a container {container.ID} in entity: {ToPrettyString(container.Owner)}. Trace: {Environment.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -82,14 +82,14 @@ public abstract partial class SharedContainerSystem
|
||||
}
|
||||
|
||||
// Update metadata first, so that parent change events can check IsInContainer.
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0, "invalid metadata flags before insertion");
|
||||
meta.Flags |= MetaDataFlags.InContainer;
|
||||
|
||||
// Remove the entity and any children from broadphases.
|
||||
// This is done before changing can collide to avoid unecceary updates.
|
||||
// TODO maybe combine with RecursivelyUpdatePhysics to avoid fetching components and iterating parents twice?
|
||||
_lookup.RemoveFromEntityTree(toInsert, transform);
|
||||
DebugTools.Assert(transform.Broadphase == null || !transform.Broadphase.Value.IsValid());
|
||||
DebugTools.Assert(transform.Broadphase == null || !transform.Broadphase.Value.IsValid(), "invalid broadphase");
|
||||
|
||||
// Avoid unnecessary broadphase updates while unanchoring, changing physics collision, and re-parenting.
|
||||
var old = transform.Broadphase;
|
||||
@@ -111,7 +111,7 @@ public abstract partial class SharedContainerSystem
|
||||
transform.Broadphase = old;
|
||||
|
||||
// the transform.AttachParent() could previously result in the flag being unset, so check that this hasn't happened.
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) != 0);
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) != 0, "invalid metadata flags after insertion");
|
||||
|
||||
// Implementation specific insert logic
|
||||
container.InternalInsert(toInsert, EntityManager);
|
||||
@@ -125,11 +125,11 @@ public abstract partial class SharedContainerSystem
|
||||
RaiseLocalEvent(toInsert, new EntGotInsertedIntoContainerMessage(toInsert, container), true);
|
||||
|
||||
// The sheer number of asserts tells you about how little I trust container and parenting code.
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) != 0);
|
||||
DebugTools.Assert(!transform.Anchored);
|
||||
DebugTools.Assert(transform.LocalPosition == Vector2.Zero);
|
||||
DebugTools.Assert(MathHelper.CloseTo(transform.LocalRotation.Theta, Angle.Zero));
|
||||
DebugTools.Assert(!PhysicsQuery.TryGetComponent(toInsert, out var phys) || (!phys.Awake && !phys.CanCollide));
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) != 0, "invalid metadata flags after events");
|
||||
DebugTools.Assert(!transform.Anchored, "entity is anchored");
|
||||
DebugTools.AssertEqual(transform.LocalPosition, Vector2.Zero);
|
||||
DebugTools.Assert(MathHelper.CloseTo(transform.LocalRotation.Theta, Angle.Zero), "Angle is not zero");
|
||||
DebugTools.Assert(!PhysicsQuery.TryGetComponent(toInsert, out var phys) || (!phys.Awake && !phys.CanCollide), "Invalid physics");
|
||||
|
||||
Dirty(container.Owner, container.Manager);
|
||||
return true;
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract partial class SharedContainerSystem
|
||||
return false;
|
||||
|
||||
DebugTools.AssertNotNull(container.Manager);
|
||||
DebugTools.Assert(Exists(toRemove));
|
||||
DebugTools.Assert(Exists(toRemove), "toRemove does not exist");
|
||||
|
||||
if (!force && !CanRemove(toRemove, container))
|
||||
return false;
|
||||
@@ -60,11 +60,11 @@ public abstract partial class SharedContainerSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugTools.Assert(meta.EntityLifeStage < EntityLifeStage.Terminating || (force && !reparent));
|
||||
DebugTools.Assert(xform.Broadphase == null || !xform.Broadphase.Value.IsValid());
|
||||
DebugTools.Assert(!xform.Anchored);
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) != 0x0);
|
||||
DebugTools.Assert(!TryComp(toRemove, out PhysicsComponent? phys) || (!phys.Awake && !phys.CanCollide));
|
||||
DebugTools.Assert(meta.EntityLifeStage < EntityLifeStage.Terminating || (force && !reparent), "Entity is terminating");
|
||||
DebugTools.Assert(xform.Broadphase == null || !xform.Broadphase.Value.IsValid(), "broadphase is invalid");
|
||||
DebugTools.Assert(!xform.Anchored || _timing.ApplyingState, "anchor is invalid");
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) != 0x0, "metadata is invalid");
|
||||
DebugTools.Assert(!TryComp(toRemove, out PhysicsComponent? phys) || (!phys.Awake && !phys.CanCollide), "physics is invalid");
|
||||
|
||||
// Unset flag (before parent change events are raised).
|
||||
meta.Flags &= ~MetaDataFlags.InContainer;
|
||||
@@ -104,7 +104,7 @@ public abstract partial class SharedContainerSystem
|
||||
RaiseLocalEvent(container.Owner, new EntRemovedFromContainerMessage(toRemove, container), true);
|
||||
RaiseLocalEvent(toRemove, new EntGotRemovedFromContainerMessage(toRemove, container), false);
|
||||
|
||||
DebugTools.Assert(destination == null || xform.Coordinates.Equals(destination.Value));
|
||||
DebugTools.Assert(destination == null || xform.Coordinates.Equals(destination.Value), "failed to set destination");
|
||||
|
||||
Dirty(container.Owner, container.Manager);
|
||||
return true;
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Shared.ContentPack
|
||||
String("short").ThenReturn(PrimitiveTypeCode.Int16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> UInt16TypeParser =
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt32);
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> Int32TypeParser =
|
||||
String("int").ThenReturn(PrimitiveTypeCode.Int32);
|
||||
|
||||
@@ -84,12 +84,146 @@ Types:
|
||||
- "bool get_HasContents()"
|
||||
Lidgren.Network:
|
||||
NetBuffer:
|
||||
All: True
|
||||
Methods:
|
||||
- "byte[] get_Data()"
|
||||
- "void set_Data(byte[])"
|
||||
- "int get_LengthBytes()"
|
||||
- "void set_LengthBytes(int)"
|
||||
- "int get_LengthBits()"
|
||||
- "void set_LengthBits(int)"
|
||||
- "long get_Position()"
|
||||
- "void set_Position(long)"
|
||||
- "int get_PositionInBytes()"
|
||||
- "byte[] PeekDataBuffer()"
|
||||
- "bool PeekBoolean()"
|
||||
- "byte PeekByte()"
|
||||
- "sbyte PeekSByte()"
|
||||
- "byte PeekByte(int)"
|
||||
- "System.Span`1<byte> PeekBytes(System.Span`1<byte>)"
|
||||
- "byte[] PeekBytes(int)"
|
||||
- "void PeekBytes(byte[], int, int)"
|
||||
- "short PeekInt16()"
|
||||
- "ushort PeekUInt16()"
|
||||
- "int PeekInt32()"
|
||||
- "int PeekInt32(int)"
|
||||
- "uint PeekUInt32()"
|
||||
- "uint PeekUInt32(int)"
|
||||
- "ulong PeekUInt64()"
|
||||
- "long PeekInt64()"
|
||||
- "ulong PeekUInt64(int)"
|
||||
- "long PeekInt64(int)"
|
||||
- "float PeekFloat()"
|
||||
- "System.Half PeekHalf()"
|
||||
- "float PeekSingle()"
|
||||
- "double PeekDouble()"
|
||||
- "string PeekString()"
|
||||
- "int PeekStringSize()"
|
||||
- "bool ReadBoolean()"
|
||||
- "byte ReadByte()"
|
||||
- "bool ReadByte(ref byte)"
|
||||
- "sbyte ReadSByte()"
|
||||
- "byte ReadByte(int)"
|
||||
- "System.Span`1<byte> ReadBytes(System.Span`1<byte>)"
|
||||
- "byte[] ReadBytes(int)"
|
||||
- "bool ReadBytes(int, ref byte[])"
|
||||
- "bool TryReadBytes(System.Span`1<byte>)"
|
||||
- "void ReadBytes(byte[], int, int)"
|
||||
- "void ReadBits(System.Span`1<byte>, int)"
|
||||
- "void ReadBits(byte[], int, int)"
|
||||
- "short ReadInt16()"
|
||||
- "ushort ReadUInt16()"
|
||||
- "int ReadInt32()"
|
||||
- "bool ReadInt32(ref int)"
|
||||
- "int ReadInt32(int)"
|
||||
- "uint ReadUInt32()"
|
||||
- "bool ReadUInt32(ref uint)"
|
||||
- "uint ReadUInt32(int)"
|
||||
- "ulong ReadUInt64()"
|
||||
- "long ReadInt64()"
|
||||
- "ulong ReadUInt64(int)"
|
||||
- "long ReadInt64(int)"
|
||||
- "float ReadFloat()"
|
||||
- "System.Half ReadHalf()"
|
||||
- "float ReadSingle()"
|
||||
- "bool ReadSingle(ref float)"
|
||||
- "double ReadDouble()"
|
||||
- "uint ReadVariableUInt32()"
|
||||
- "bool ReadVariableUInt32(ref uint)"
|
||||
- "int ReadVariableInt32()"
|
||||
- "long ReadVariableInt64()"
|
||||
- "ulong ReadVariableUInt64()"
|
||||
- "float ReadSignedSingle(int)"
|
||||
- "float ReadUnitSingle(int)"
|
||||
- "float ReadRangedSingle(float, float, int)"
|
||||
- "int ReadRangedInteger(int, int)"
|
||||
- "long ReadRangedInteger(long, long)"
|
||||
- "string ReadString()"
|
||||
- "bool ReadString(ref string)"
|
||||
- "double ReadTime(Lidgren.Network.NetConnection, bool)"
|
||||
- "System.Net.IPEndPoint ReadIPEndPoint()"
|
||||
- "void SkipPadBits()"
|
||||
- "void ReadPadBits()"
|
||||
- "void SkipPadBits(int)"
|
||||
- "void EnsureBufferSize(int)"
|
||||
- "void Write(bool)"
|
||||
- "void Write(byte)"
|
||||
- "void WriteAt(int, byte)"
|
||||
- "void Write(sbyte)"
|
||||
- "void Write(byte, int)"
|
||||
- "void Write(byte[])"
|
||||
- "void Write(System.ReadOnlySpan`1<byte>)"
|
||||
- "void Write(byte[], int, int)"
|
||||
- "void Write(ushort)"
|
||||
- "void WriteAt(int, ushort)"
|
||||
- "void Write(ushort, int)"
|
||||
- "void Write(short)"
|
||||
- "void WriteAt(int, short)"
|
||||
- "void Write(int)"
|
||||
- "void WriteAt(int, int)"
|
||||
- "void Write(uint)"
|
||||
- "void WriteAt(int, uint)"
|
||||
- "void Write(uint, int)"
|
||||
- "void Write(int, int)"
|
||||
- "void Write(ulong)"
|
||||
- "void WriteAt(int, ulong)"
|
||||
- "void Write(ulong, int)"
|
||||
- "void Write(long)"
|
||||
- "void Write(long, int)"
|
||||
- "void Write(System.Half)"
|
||||
- "void Write(float)"
|
||||
- "void Write(double)"
|
||||
- "int WriteVariableUInt32(uint)"
|
||||
- "int WriteVariableInt32(int)"
|
||||
- "int WriteVariableInt64(long)"
|
||||
- "int WriteVariableUInt64(ulong)"
|
||||
- "void WriteSignedSingle(float, int)"
|
||||
- "void WriteUnitSingle(float, int)"
|
||||
- "void WriteRangedSingle(float, float, float, int)"
|
||||
- "int WriteRangedInteger(int, int, int)"
|
||||
- "int WriteRangedInteger(long, long, long)"
|
||||
- "void Write(string)"
|
||||
- "void Write(System.Net.IPEndPoint)"
|
||||
- "void WriteTime(bool)"
|
||||
- "void WriteTime(double, bool)"
|
||||
- "void WritePadBits()"
|
||||
- "void WritePadBits(int)"
|
||||
- "void Write(Lidgren.Network.NetBuffer)"
|
||||
- "void Zero(int)"
|
||||
- "void .ctor()"
|
||||
NetDeliveryMethod: { }
|
||||
NetIncomingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "Lidgren.Network.NetIncomingMessageType get_MessageType()"
|
||||
- "Lidgren.Network.NetDeliveryMethod get_DeliveryMethod()"
|
||||
- "int get_SequenceChannel()"
|
||||
- "System.Net.IPEndPoint get_SenderEndPoint()"
|
||||
- "Lidgren.Network.NetConnection get_SenderConnection()"
|
||||
- "double get_ReceiveTime()"
|
||||
- "double ReadTime(bool)"
|
||||
- "string ToString()"
|
||||
NetOutgoingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "string ToString()"
|
||||
Nett:
|
||||
CommentLocation: { } # Enum
|
||||
Toml:
|
||||
@@ -450,6 +584,7 @@ Types:
|
||||
Vector2: { All: True }
|
||||
Vector3: { All: True }
|
||||
Vector4: { All: True }
|
||||
Matrix3x2: { All: True }
|
||||
System.Reflection:
|
||||
Assembly:
|
||||
Methods:
|
||||
@@ -831,6 +966,7 @@ Types:
|
||||
- "object GetValue(long[])"
|
||||
- "System.Collections.IEnumerator GetEnumerator()"
|
||||
- "System.Collections.ObjectModel.ReadOnlyCollection`1<!!0> AsReadOnly<>(!!0[])"
|
||||
- "void Clear(System.Array)"
|
||||
- "void Clear(System.Array, int, int)"
|
||||
- "void ConstrainedCopy(System.Array, int, System.Array, int, int)"
|
||||
- "void Copy(System.Array, int, System.Array, int, int)"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user