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 | |
|---|---|---|---|
|
|
cdf44ef3d9 | ||
|
|
49ec5b9ca3 | ||
|
|
8b53b89423 | ||
|
|
3fd731d917 | ||
|
|
cb1d4ae843 | ||
|
|
039b70f502 | ||
|
|
7892cc895f | ||
|
|
77108284b8 | ||
|
|
5e21dbdd7f | ||
|
|
8274623edb | ||
|
|
e923d69083 | ||
|
|
6e8ab5ce78 | ||
|
|
f905ea631b | ||
|
|
be54c41891 | ||
|
|
33184ecfa5 | ||
|
|
11cf0c1703 | ||
|
|
528544b7a2 | ||
|
|
8571d7e7b5 | ||
|
|
0f06423b7a | ||
|
|
eb9e0ffefc | ||
|
|
903619ecef | ||
|
|
879c6ea538 | ||
|
|
5478545aeb | ||
|
|
650929dcbb | ||
|
|
a289659b49 | ||
|
|
85d15c21e1 | ||
|
|
bcd1566440 | ||
|
|
749ac2c364 | ||
|
|
5eed3bc281 | ||
|
|
d78f378493 | ||
|
|
eef44c15cf | ||
|
|
3d1b2418f9 | ||
|
|
6b49a86ee5 | ||
|
|
cd13cd3cd8 | ||
|
|
2b8d8d6636 | ||
|
|
409fe1a125 | ||
|
|
ab5db4641c | ||
|
|
064e8ee365 | ||
|
|
02dcff7eae | ||
|
|
e1e5f8de54 | ||
|
|
d5ba822a79 | ||
|
|
f448c6b8fa | ||
|
|
5e1d80be35 | ||
|
|
01546f32da | ||
|
|
aeeaaaefc5 | ||
|
|
b6c8060af1 | ||
|
|
99685838da | ||
|
|
8917b29255 | ||
|
|
f0c4d7c5eb | ||
|
|
6a00c62d3c | ||
|
|
fc3116fca5 | ||
|
|
98c1397b3a | ||
|
|
2464bb6c2f | ||
|
|
709142acee | ||
|
|
af4e3e5e1c | ||
|
|
d51a18c6ea | ||
|
|
d5c3d4c0c9 | ||
|
|
a4474d8df8 | ||
|
|
d66f7c7c06 | ||
|
|
b6879869d6 | ||
|
|
815b8e0c48 | ||
|
|
ef4e3baa7f | ||
|
|
270ddb5a53 | ||
|
|
6133fe0808 | ||
|
|
909fd326a0 | ||
|
|
876de4065a | ||
|
|
60e159f0d0 | ||
|
|
80f3aae30c | ||
|
|
98b1862433 | ||
|
|
d2311c193f | ||
|
|
f05ed96461 | ||
|
|
dc23dfaf4d | ||
|
|
62315f7c2e | ||
|
|
b2d121e780 |
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
@@ -16,3 +16,8 @@
|
||||
|
||||
# Be they Fluent translations or Freemarker templates, I know them both!
|
||||
*.ftl @RemieRichards
|
||||
|
||||
# commands commands commands commands
|
||||
**/Toolshed/** @moonheart08
|
||||
*Command.cs @moonheart08
|
||||
*Commands.cs @moonheart08
|
||||
|
||||
4
.github/workflows/build-docfx.yml
vendored
4
.github/workflows/build-docfx.yml
vendored
@@ -7,12 +7,12 @@ jobs:
|
||||
docfx:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/build-test.yml
vendored
4
.github/workflows/build-test.yml
vendored
@@ -15,12 +15,12 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Install dependencies
|
||||
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -35,12 +35,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/publish-client.yml
vendored
4
.github/workflows/publish-client.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
||||
$ver = [regex]::Match($env:GITHUB_REF, "refs/tags/v?(.+)").Groups[1].Value
|
||||
echo ("::set-output name=version::{0}" -f $ver)
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/test-content.yml
vendored
4
.github/workflows/test-content.yml
vendored
@@ -13,13 +13,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out content
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
repository: space-wizards/space-station-14
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Disable submodule autoupdate
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: 78aa82cef0...f19cea8010
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
179
RELEASE-NOTES.md
179
RELEASE-NOTES.md
@@ -54,6 +54,185 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 157.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* UI tooltips now use rich text labels.
|
||||
|
||||
|
||||
## 157.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Unrevert container changes from 155.0.0.
|
||||
* Added server-client EntityUid separation. A given EntityUid will no longer refer to the same entity on the server & client.
|
||||
* EntityUid is no longer net-serializable, use NetEntity instead, EntityManager & entity systems have helper methods for converting between the two,
|
||||
|
||||
|
||||
## 156.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Revert container changes from 155.0.0.
|
||||
|
||||
|
||||
## 155.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* MapInitEvent now gets raised for components that get added to entities that have already been map-initialized.
|
||||
|
||||
### New features
|
||||
|
||||
* VirtualWritableDirProvider now supports file renaming/moving.
|
||||
* Added a new command for toggling the replay UI (`replay_toggleui`).
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed formatting of localization file errors.
|
||||
* Directed event subscriptions will no longer error if the corresponding component is queued for deletion.
|
||||
|
||||
|
||||
## 154.2.0
|
||||
|
||||
|
||||
|
||||
### New features
|
||||
|
||||
* Added support for advertising to multiple hubs simultaneously.
|
||||
* Added new functions to ContainerSystem that recursively look for a component on a contained entity's parents.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix Direction.TurnCw/TurnCcw to South returning Invalid.
|
||||
|
||||
|
||||
## 154.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add MathHelper.Max for TimeSpans.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Make joint initialisation only log under IsFirstTimePredicted on client.
|
||||
|
||||
### Other
|
||||
|
||||
* Mark the proxy Dirty(component) as obsolete in line with EntityManager (Dirty(EntityUid, Component) should be used in its place).
|
||||
|
||||
|
||||
## 154.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Change ignored prototypes to skip prototypes even if the prototype type is found.
|
||||
* Moved IPlayerData interface to shared.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a multiline text submit keybind function.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed multiline edits scrollbar margins.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added more event sources.
|
||||
* Made Toolshed types oneOff IoC injections.
|
||||
|
||||
|
||||
## 153.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed SharedUserInterfaceComponent component references.
|
||||
* Removed EntityDeletedMessage.
|
||||
|
||||
### Other
|
||||
|
||||
* Performance improvements for replay recording.
|
||||
* Lidgren has been updated to [v0.2.6](https://github.com/space-wizards/SpaceWizards.Lidgren.Network/blob/v0.2.6/RELEASE-NOTES.md).
|
||||
* Make EntityManager.AddComponent with a component instance set the owner if its default, add system proxy for it.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added some `EventSource` providers for PVS and replay recording: `Robust.Pvs` and `Robust.ReplayRecording`.
|
||||
* Added RecursiveMoveBenchmark.
|
||||
* Removed redundant prototype resolving.
|
||||
* Removed CollisionWake component removal subscription.
|
||||
* Removed redundant DebugTools.AssertNotNull(netId) in ClientGameStateManager
|
||||
|
||||
|
||||
## 152.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `Robust.Server.GameObjects.BoundUserInterface.InteractionRangeSqrd` is now a get-only property. Modify `InteractionRange` instead if you want to change it on active UIs.
|
||||
* Remove IContainerManager.
|
||||
* Remove and obsolete ComponentExt methods.
|
||||
* Remove EntityStarted and ComponentDeleted C# events.
|
||||
* Convert Tile.TypeId to an int. Old maps that were saved with TypeId being an ushort will still be properly deserialized.
|
||||
|
||||
### New features
|
||||
|
||||
* `BoundUserInterfaceCheckRangeEvent` can be used to implement custom logic for BUI range checks.
|
||||
* Add support for long values in CVars.
|
||||
* Allow user code to implement own logic for bound user interface range checks.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix timers counting down slower than real time and drifting.
|
||||
* Add missing System using statement to generated component states.
|
||||
* Fix build with USE_SYSTEM_SQLITE.
|
||||
* Fix prototype manager not being initialized in robust server simulation tests.
|
||||
* Fix not running serialization hooks when copying non-byref data definition fields without a custom type serializer.
|
||||
|
||||
### Other
|
||||
|
||||
* Remove warning for glibc 2.37.
|
||||
* Remove personally-identifiable file paths from client logs.
|
||||
|
||||
### Internal
|
||||
|
||||
* Disable obsoletion and inherited member hidden warnings in serialization source generated code.
|
||||
* Update CI workflows to use setup-dotnet 3.2.0 and checkout 3.6.0.
|
||||
* Fix entity spawn tests having instance per test lifecycle with a non static OneTimeTearDown method.
|
||||
* Add new PVS test to check that there is no issue with entity states referencing other entities that the client is not yet aware of.
|
||||
|
||||
|
||||
## 151.0.0
|
||||
|
||||
|
||||
## 150.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix some partial datadefs.
|
||||
|
||||
|
||||
## 150.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove the Id field from Fixtures as the Id is already stored on FixturesComponent.
|
||||
|
||||
### New features
|
||||
|
||||
* Add AbstractDictionarySerializer for abstract classes.
|
||||
* Add many new spawn functions for entities for common operations.
|
||||
|
||||
|
||||
## 149.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix serialization sharing instances when copying data definitions and not assigning null when the source is null.
|
||||
* Fixed resizing a window to be bigger than its set maxsize crashing the client.
|
||||
|
||||
|
||||
## 149.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
@@ -161,9 +161,263 @@ command-description-EqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are equal.
|
||||
command-description-NotEqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are not equal.
|
||||
|
||||
command-description-append =
|
||||
Appends a value to the input enumerable.
|
||||
command-description-DefaultIfNullCommand =
|
||||
Replaces the input with the type's default value if it is null, albeit only for value types (not objects).
|
||||
command-description-OrValueCommand =
|
||||
If the input is null, uses the provided alternate value.
|
||||
command-description-DebugPrintCommand =
|
||||
Prints the given value transparently, for debug prints in a command run.
|
||||
command-description-i =
|
||||
Integer constant.
|
||||
command-description-f =
|
||||
Float constant.
|
||||
command-description-s =
|
||||
String constant.
|
||||
command-description-b =
|
||||
Bool constant.
|
||||
command-description-join =
|
||||
Joins two sequences together into one sequence.
|
||||
command-description-reduce =
|
||||
Given a block to use as a reducer, turns a sequence into a single value.
|
||||
The left hand side of the block is implied, and the right hand is stored in $value.
|
||||
command-description-rep =
|
||||
Repeats the input value N times to form a sequence.
|
||||
command-description-take =
|
||||
Takes N values from the input sequence
|
||||
command-description-spawn-at =
|
||||
Spawns an entity at the given coordinates.
|
||||
command-description-spawn-on =
|
||||
Spawns an entity on the given entity, at it's coordinates.
|
||||
command-description-spawn-attached =
|
||||
Spawns an entity attached to the given entity, at (0 0) relative to it.
|
||||
command-description-mappos =
|
||||
Returns an entity's coordinates relative to it's current map.
|
||||
command-description-pos =
|
||||
Returns an entity's coordinates.
|
||||
command-description-tp-coords =
|
||||
Teleports the target to the given coordinates.
|
||||
command-description-tp-to =
|
||||
Teleports the target to the given other entity.
|
||||
command-description-tp-into =
|
||||
Teleports the target "into" the given other entity, attaching it at (0 0) relative to it.
|
||||
command-description-comp-get =
|
||||
Gets the given component from the given entity.
|
||||
command-description-comp-add =
|
||||
Adds the given component to the given entity.
|
||||
command-description-comp-ensure =
|
||||
Ensures the given entity has the given component.
|
||||
command-description-comp-has =
|
||||
Check if the given entity has the given component.
|
||||
command-description-AddVecCommand =
|
||||
Adds a scalar (single value) to every element in the input.
|
||||
command-description-SubVecCommand =
|
||||
Subtracts a scalar (single value) from every element in the input.
|
||||
command-description-MulVecCommand =
|
||||
Multiplies a scalar (single value) by every element in the input.
|
||||
command-description-DivVecCommand =
|
||||
Divides every element in the input by a scalar (single value).
|
||||
command-description-rng-to =
|
||||
Returns a number from its input to its argument (i.e. n..m inclusive)
|
||||
command-description-rng-from =
|
||||
Returns a number to its input from its argument (i.e. m..n inclusive)
|
||||
command-description-rng-prob =
|
||||
Returns a boolean based on the input probability/chance (from 0 to 1)
|
||||
command-description-sum =
|
||||
Computes the sum of the input.
|
||||
command-description-bin =
|
||||
"Bins" the input, counting up how many times each unique element occurs.
|
||||
command-description-extremes =
|
||||
Returns the two extreme ends of a list, interwoven.
|
||||
command-description-sortby =
|
||||
Sorts the input least to greatest by the computed key.
|
||||
command-description-sortmapby =
|
||||
Sorts the input least to greatest by the computed key, replacing the value with it's computed key afterward.
|
||||
command-description-sort =
|
||||
Sorts the input least to greatest.
|
||||
command-description-sortdownby =
|
||||
Sorts the input greatest to least by the computed key.
|
||||
command-description-sortmapdownby =
|
||||
Sorts the input greatest to least by the computed key, replacing the value with it's computed key afterward.
|
||||
command-description-sortdown =
|
||||
Sorts the input greatest to least.
|
||||
command-description-iota =
|
||||
Returns a list of numbers 1 to N.
|
||||
command-description-to =
|
||||
Returns a list of numbers N to M.
|
||||
command-description-curtick =
|
||||
The current game tick.
|
||||
command-description-curtime =
|
||||
The current game time (a TimeSpan)
|
||||
command-description-realtime =
|
||||
The current realtime since startup (a TimeSpan)
|
||||
command-description-servertime =
|
||||
The current server game time, or zero if we are the server (a TimeSpan)
|
||||
command-description-replace =
|
||||
Replaces the input entities with the given prototype, preserving position and rotation (but nothing else)
|
||||
command-description-allcomps =
|
||||
Returns all components on the given entity.
|
||||
command-description-entitysystemupdateorder-tick =
|
||||
Lists the tick update order of entity systems.
|
||||
|
||||
command-description-entitysystemupdateorder-frame =
|
||||
Lists the frame update order of entity systems.
|
||||
command-description-more =
|
||||
Prints the contents of $more, i.e. any extras that Toolshed didn't print from the last command.
|
||||
command-description-ModulusCommand =
|
||||
Computes the modulus of two values.
|
||||
This is usually remainder, check C#'s documentation for the type.
|
||||
command-description-ModVecCommand =
|
||||
Performs the modulus operation over the input with the given constant right-hand value.
|
||||
command-description-BitAndNotCommand =
|
||||
Performs bitwise AND-NOT over the input.
|
||||
command-description-BitOrNotCommand =
|
||||
Performs bitwise OR-NOT over the input.
|
||||
command-description-BitXnorCommand =
|
||||
Performs bitwise XNOR over the input.
|
||||
command-description-BitNotCommand =
|
||||
Performs bitwise NOT on the input.
|
||||
command-description-abs =
|
||||
Computes the absolute value of the input (removing the sign)
|
||||
command-description-average =
|
||||
Computes the average (arithmetic mean) of the input.
|
||||
command-description-bibytecount =
|
||||
Returns the size of the input in bytes, given that the input implements IBinaryInteger.
|
||||
This is NOT sizeof.
|
||||
command-description-shortestbitlength =
|
||||
Returns the minimum number of bits needed to represent the input value.
|
||||
command-description-countleadzeros =
|
||||
Counts the number of leading binary zeros in the input value.
|
||||
command-description-counttrailingzeros =
|
||||
Counts the number of trailing binary zeros in the input value.
|
||||
command-description-fpi =
|
||||
pi (3.14159...) as a float.
|
||||
command-description-fe =
|
||||
e (2.71828...) as a float.
|
||||
command-description-ftau =
|
||||
tau (6.28318...) as a float.
|
||||
command-description-fepsilon =
|
||||
The epsilon value for a float, exactly 1.4e-45.
|
||||
command-description-dpi =
|
||||
pi (3.14159...) as a double.
|
||||
command-description-de =
|
||||
e (2.71828...) as a double.
|
||||
command-description-dtau =
|
||||
tau (6.28318...) as a double.
|
||||
command-description-depsilon =
|
||||
The epsilon value for a double, exactly 4.9406564584124654E-324.
|
||||
command-description-hpi =
|
||||
pi (3.14...) as a half.
|
||||
command-description-he =
|
||||
e (2.71...) as a half.
|
||||
command-description-htau =
|
||||
tau (6.28...) as a half.
|
||||
command-description-hepsilon =
|
||||
The epsilon value for a half, exactly 5.9604645E-08.
|
||||
command-description-floor =
|
||||
Returns the floor of the input value (rounding toward zero).
|
||||
command-description-ceil =
|
||||
Returns the ceil of the input value (rounding away from zero).
|
||||
command-description-round =
|
||||
Rounds the input value.
|
||||
command-description-trunc =
|
||||
Truncates the input value.
|
||||
command-description-round2frac =
|
||||
Rounds the input value to the specified number of fractional digits.
|
||||
command-description-exponentbytecount =
|
||||
Returns the number of bytes required to store the exponent.
|
||||
command-description-significandbytecount =
|
||||
Returns the number of bytes required to store the significand.
|
||||
command-description-significandbitcount =
|
||||
Returns the exact bit length of the significand.
|
||||
command-description-exponentshortestbitcount =
|
||||
Returns the minimum number of bits to store the exponent.
|
||||
command-description-stepnext =
|
||||
Steps to the next float value, adding one to the significand with carry.
|
||||
command-description-stepprev =
|
||||
Steps to the previous float value, subtracting one from the significand with carry.
|
||||
command-description-checkedto =
|
||||
Converts from the input numeric type to the target, erroring if not possible.
|
||||
command-description-saturateto =
|
||||
Converts from the input numeric type to the target, saturating if the value is out of range.
|
||||
For example, converting 382 to a byte would saturate to 255 (the maximum value of a byte).
|
||||
command-description-truncto =
|
||||
Converts from the input numeric type to the target, with truncation.
|
||||
In the case of integers, this is a bit cast with sign extension.
|
||||
command-description-iscanonical =
|
||||
Returns whether the input is in canonical form.
|
||||
command-description-iscomplex =
|
||||
Returns whether the input is a complex number (by value, not by type)
|
||||
command-description-iseven =
|
||||
Returns whether the input is even.
|
||||
Not a javascript package.
|
||||
command-description-isodd =
|
||||
Returns whether the input is odd.
|
||||
command-description-isfinite =
|
||||
Returns whether the input is finite.
|
||||
command-description-isimaginary =
|
||||
Returns whether the input is purely imaginary (no real part).
|
||||
command-description-isinfinite =
|
||||
Returns whether the input is infinite.
|
||||
command-description-isinteger =
|
||||
Returns whether the input is an integer (by value, not by type)
|
||||
command-description-isnan =
|
||||
Returns whether the input is Not a Number (NaN).
|
||||
This is a special floating point value, so this is by value, not by type.
|
||||
command-description-isnegative =
|
||||
Returns whether the input is negative.
|
||||
command-description-ispositive =
|
||||
Returns whether the input is positive.
|
||||
command-description-isreal =
|
||||
Returns whether the input is purely real (no imaginary part).
|
||||
command-description-issubnormal =
|
||||
Returns whether the input is in sub-normal form.
|
||||
command-description-iszero =
|
||||
Returns whether the input is zero.
|
||||
command-description-pow =
|
||||
Computes the power of its lefthand to its righthand. x^y.
|
||||
command-description-sqrt =
|
||||
Computes the square root of its input.
|
||||
command-description-cbrt =
|
||||
Computes the cube root of its input.
|
||||
command-description-root =
|
||||
Computes the Nth root of its input.
|
||||
command-description-hypot =
|
||||
Computes the hypotenuse of a triangle with the given sides A and B.
|
||||
command-description-sin =
|
||||
Computes the sine of the input.
|
||||
command-description-sinpi =
|
||||
Computes the sine of the input multiplied by pi.
|
||||
command-description-asin =
|
||||
Computes the arcsine of the input.
|
||||
command-description-asinpi =
|
||||
Computes the arcsine of the input multiplied by pi.
|
||||
command-description-cos =
|
||||
Computes the cosine of the input.
|
||||
command-description-cospi =
|
||||
Computes the cosine of the input multiplied by pi.
|
||||
command-description-acos =
|
||||
Computes the arcosine of the input.
|
||||
command-description-acospi =
|
||||
Computes the arcosine of the input multiplied by pi.
|
||||
command-description-tan =
|
||||
Computes the tangent of the input.
|
||||
command-description-tanpi =
|
||||
Computes the tangent of the input multiplied by pi.
|
||||
command-description-atan =
|
||||
Computes the arctangent of the input.
|
||||
command-description-atanpi =
|
||||
Computes the arctangent of the input multiplied by pi.
|
||||
command-description-iterate =
|
||||
Iterates the given function over the input N times, returning a list of results.
|
||||
Think of this like successively applying the function to a value, tracking all the intermediate values.
|
||||
command-description-pick =
|
||||
Picks a random value from the input.
|
||||
command-description-tee =
|
||||
Tees the input into the given block, ignoring the block's result.
|
||||
This essentially lets you have a branch in your code to do multiple operations on one value.
|
||||
command-description-cmd-info =
|
||||
Returns a CommandSpec for the given command.
|
||||
On it's own, this means it'll print the comamnd's help message.
|
||||
command-description-comp-rm =
|
||||
Removes the given component from the entity.
|
||||
|
||||
171
Robust.Benchmarks/Transform/RecursiveMoveBenchmark.cs
Normal file
171
Robust.Benchmarks/Transform/RecursiveMoveBenchmark.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Transform;
|
||||
|
||||
/// <summary>
|
||||
/// This benchmark tests various transform/move related functions with an entity that has many children.
|
||||
/// </summary>
|
||||
[Virtual, MemoryDiagnoser]
|
||||
public class RecursiveMoveBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private SharedTransformSystem _transform = default!;
|
||||
private ContainerSystem _container = default!;
|
||||
private PvsSystem _pvs = default!;
|
||||
private EntityCoordinates _mapCoords;
|
||||
private EntityCoordinates _gridCoords;
|
||||
private EntityUid _ent;
|
||||
private EntityUid _child;
|
||||
private TransformComponent _childXform = default!;
|
||||
private EntityQuery<TransformComponent> _query;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.InitializeInstance();
|
||||
|
||||
if (!_simulation.Resolve<IConfigurationManager>().GetCVar(CVars.NetPVS))
|
||||
throw new InvalidOperationException("PVS must be enabled");
|
||||
|
||||
_entMan = _simulation.Resolve<IEntityManager>();
|
||||
_transform = _entMan.System<SharedTransformSystem>();
|
||||
_container = _entMan.System<ContainerSystem>();
|
||||
_pvs = _entMan.System<PvsSystem>();
|
||||
_query = _entMan.GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Create map & grid
|
||||
var mapMan = _simulation.Resolve<IMapManager>();
|
||||
var mapSys = _entMan.System<SharedMapSystem>();
|
||||
var mapId = mapMan.CreateMap();
|
||||
var map = mapMan.GetMapEntityId(mapId);
|
||||
var gridComp = mapMan.CreateGrid(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
_mapCoords = new EntityCoordinates(map, 100, 100);
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
|
||||
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
|
||||
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
|
||||
_ent = _entMan.Spawn();
|
||||
|
||||
// Quick check that SetCoordinates actually changes the parent as expected
|
||||
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
// Add 5 direct children in slots to represent clothing.
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var id = $"inventory{i}";
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, id);
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// body parts
|
||||
_container.EnsureContainer<Container>(_ent, "body");
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
// Simple organ
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// body part that has another body part / limb
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
|
||||
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Backpack
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// Misc backpack contents.
|
||||
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Emergency box inside of the backpack
|
||||
var box = backpackStorage.ContainedEntities.First();
|
||||
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Deepest child.
|
||||
_child = boxContainer.ContainedEntities.First();
|
||||
_childXform = _query.GetComponent(_child);
|
||||
|
||||
_pvs.ProcessCollections();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This implicitly measures move events, including PVS and entity lookups. Though given that most of the entities
|
||||
/// are in containers, this will bias the entity lookup aspect.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void MoveEntity()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Like <see cref="MoveEntity"/>, but also processes queued PVS chunk updates.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void MoveAndUpdateChunks()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_pvs.ProcessCollections();
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
_pvs.ProcessCollections();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public Vector2 GetWorldPos()
|
||||
{
|
||||
return _transform.GetWorldPosition(_childXform);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public EntityUid GetRootUid()
|
||||
{
|
||||
var xform = _childXform;
|
||||
while (xform.ParentUid.IsValid())
|
||||
{
|
||||
xform = _query.GetComponent(xform.ParentUid);
|
||||
}
|
||||
return xform.ParentUid;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = EntityUid.Parse(args[0]);
|
||||
var netEntity = NetEntity.Parse(args[0]);
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
var componentName = args[1];
|
||||
|
||||
var component = (Component) _componentFactory.GetComponent(componentName);
|
||||
@@ -49,7 +50,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var entityUid = EntityUid.Parse(args[0]);
|
||||
var netEntity = NetEntity.Parse(args[0]);
|
||||
var entityUid = _entityManager.GetEntity(netEntity);
|
||||
var componentName = args[1];
|
||||
|
||||
var registration = _componentFactory.GetRegistration(componentName);
|
||||
|
||||
@@ -296,6 +296,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class SnapGridGetCell : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public override string Command => "sggcell";
|
||||
@@ -310,7 +311,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
string indices = args[1];
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
if (!NetEntity.TryParse(args[0], out var gridNet))
|
||||
{
|
||||
shell.WriteError($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
@@ -322,7 +323,7 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (_map.TryGetGrid(gridUid, out var grid))
|
||||
if (_map.TryGetGrid(_entManager.GetEntity(gridNet), out var grid))
|
||||
{
|
||||
foreach (var entity in grid.GetAnchoredEntities(new Vector2i(
|
||||
int.Parse(indices.Split(',')[0], CultureInfo.InvariantCulture),
|
||||
@@ -430,6 +431,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class GridTileCount : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public override string Command => "gridtc";
|
||||
@@ -442,7 +444,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
if (!NetEntity.TryParse(args[0], out var gridUidNet) ||
|
||||
!_entManager.TryGetEntity(gridUidNet, out var gridUid))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
|
||||
@@ -167,7 +167,6 @@ namespace Robust.Client
|
||||
_reflectionManager.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDefaultPrototypes();
|
||||
_prototypeManager.ResolveResults();
|
||||
_userInterfaceManager.Initialize();
|
||||
_eyeManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
|
||||
53
Robust.Client/GameObjects/ClientEntityManager.Network.cs
Normal file
53
Robust.Client/GameObjects/ClientEntityManager.Network.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed partial class ClientEntityManager
|
||||
{
|
||||
protected override NetEntity GenerateNetEntity() => new(NextNetworkId++ | NetEntity.ClientEntity);
|
||||
|
||||
/// <summary>
|
||||
/// If the client fails to resolve a NetEntity then during component state handling or the likes we
|
||||
/// flag that comp state as requiring re-running if that NetEntity comes in.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal readonly Dictionary<NetEntity, List<(Type type, EntityUid Owner)>> PendingNetEntityStates = new();
|
||||
|
||||
public override bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
// Can't log false because some content code relies on invalid UIDs.
|
||||
if (!MetaQuery.Resolve(uid, ref metadata, false))
|
||||
return false;
|
||||
|
||||
return metadata.NetEntity.IsClientSide();
|
||||
}
|
||||
|
||||
public override EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity)
|
||||
{
|
||||
if (!nEntity.Valid)
|
||||
{
|
||||
return EntityUid.Invalid;
|
||||
}
|
||||
|
||||
if (NetEntityLookup.TryGetValue(nEntity, out var entity))
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
// Flag the callerEntity to have their state potentially re-run later.
|
||||
var pending = PendingNetEntityStates.GetOrNew(nEntity);
|
||||
pending.Add((typeof(T), callerEntity));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public override EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
|
||||
{
|
||||
var entity = EnsureEntity<T>(netCoordinates.NetEntity, callerEntity);
|
||||
return new EntityCoordinates(entity, netCoordinates.Position);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Prometheus;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Replays;
|
||||
@@ -18,7 +16,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Manager for entities -- controls things like template loading and instantiation
|
||||
/// </summary>
|
||||
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
public sealed partial class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientNetManager _networkManager = default!;
|
||||
@@ -27,8 +25,6 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
|
||||
|
||||
protected override int NextEntityUid { get; set; } = EntityUid.ClientUid + 1;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetupNetworking();
|
||||
@@ -39,13 +35,16 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void FlushEntities()
|
||||
{
|
||||
// Server doesn't network deletions on client shutdown so we need to
|
||||
// manually clear these out or risk stale data getting used.
|
||||
PendingNetEntityStates.Clear();
|
||||
using var _ = _gameTiming.StartStateApplicationArea();
|
||||
base.FlushEntities();
|
||||
}
|
||||
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid uid)
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName)
|
||||
{
|
||||
return base.CreateEntity(prototypeName, uid);
|
||||
return base.CreateEntity(prototypeName);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
|
||||
@@ -68,7 +67,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void QueueDeleteEntity(EntityUid uid)
|
||||
{
|
||||
if (uid.IsClientSide())
|
||||
if (IsClientSide(uid))
|
||||
{
|
||||
base.QueueDeleteEntity(uid);
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
[RegisterComponent]
|
||||
public sealed partial class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Owner.IsClientSide() || !animatedComp.NetSyncEnabled)
|
||||
if (IsClientSide(component.Owner) || !animatedComp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
var reg = _compFact.GetRegistration(animatedComp);
|
||||
|
||||
@@ -81,9 +81,13 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
#region Event Handlers
|
||||
private void PlayAudioEntityHandler(PlayAudioEntityMessage ev)
|
||||
{
|
||||
var stream = EntityManager.EntityExists(ev.EntityUid)
|
||||
? (PlayingStream?) Play(ev.FileName, ev.EntityUid, ev.FallbackCoordinates, ev.AudioParams, false)
|
||||
: (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams, false);
|
||||
var uid = GetEntity(ev.NetEntity);
|
||||
var coords = GetCoordinates(ev.Coordinates);
|
||||
var fallback = GetCoordinates(ev.FallbackCoordinates);
|
||||
|
||||
var stream = EntityManager.EntityExists(uid)
|
||||
? (PlayingStream?) Play(ev.FileName, uid, fallback, ev.AudioParams, false)
|
||||
: (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false);
|
||||
|
||||
if (stream != null)
|
||||
stream.NetIdentifier = ev.Identifier;
|
||||
@@ -98,7 +102,10 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
|
||||
private void PlayAudioPositionalHandler(PlayAudioPositionalMessage ev)
|
||||
{
|
||||
var stream = (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams, false);
|
||||
var coords = GetCoordinates(ev.Coordinates);
|
||||
var fallback = GetCoordinates(ev.FallbackCoordinates);
|
||||
|
||||
var stream = (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false);
|
||||
if (stream != null)
|
||||
stream.NetIdentifier = ev.Identifier;
|
||||
}
|
||||
@@ -383,8 +390,8 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
_replayRecording.RecordReplayMessage(new PlayAudioEntityMessage
|
||||
{
|
||||
FileName = filename,
|
||||
EntityUid = entity,
|
||||
FallbackCoordinates = fallbackCoordinates ?? default,
|
||||
NetEntity = GetNetEntity(entity),
|
||||
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates) ?? default,
|
||||
AudioParams = audioParams ?? AudioParams.Default
|
||||
});
|
||||
}
|
||||
@@ -437,8 +444,8 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
_replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage
|
||||
{
|
||||
FileName = filename,
|
||||
Coordinates = coordinates,
|
||||
FallbackCoordinates = fallbackCoordinates,
|
||||
Coordinates = GetNetCoordinates(coordinates),
|
||||
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates),
|
||||
AudioParams = audioParams ?? AudioParams.Default
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -5,13 +6,11 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
using static Robust.Shared.Containers.ContainerManagerComponent;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -23,14 +22,20 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
[Dependency] private readonly PointLightSystem _lightSys = default!;
|
||||
|
||||
private EntityQuery<PointLightComponent> _pointLightQuery;
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
private readonly HashSet<EntityUid> _updateQueue = new();
|
||||
|
||||
public readonly Dictionary<EntityUid, IContainer> ExpectedEntities = new();
|
||||
public readonly Dictionary<NetEntity, BaseContainer> ExpectedEntities = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_pointLightQuery = GetEntityQuery<PointLightComponent>();
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
EntityManager.EntityInitialized += HandleEntityInitialized;
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentHandleState>(HandleComponentState);
|
||||
|
||||
@@ -43,20 +48,18 @@ namespace Robust.Client.GameObjects
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
protected override void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing)
|
||||
protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing)
|
||||
{
|
||||
DebugTools.Assert(ExpectedEntities.TryGetValue(missing, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(missing));
|
||||
var netEntity = GetNetEntity(missing);
|
||||
DebugTools.Assert(ExpectedEntities.TryGetValue(netEntity, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(netEntity));
|
||||
}
|
||||
|
||||
private void HandleEntityInitialized(EntityUid uid)
|
||||
{
|
||||
if (!RemoveExpectedEntity(uid, out var container))
|
||||
if (!RemoveExpectedEntity(GetNetEntity(uid), out var container))
|
||||
return;
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
|
||||
container.Insert(uid);
|
||||
container.Insert(uid, EntityManager, transform: TransformQuery.GetComponent(uid), meta: MetaQuery.GetComponent(uid));
|
||||
}
|
||||
|
||||
private void HandleComponentState(EntityUid uid, ContainerManagerComponent component, ref ComponentHandleState args)
|
||||
@@ -64,23 +67,24 @@ namespace Robust.Client.GameObjects
|
||||
if (args.Current is not ContainerManagerComponentState cast)
|
||||
return;
|
||||
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xform = xformQuery.GetComponent(uid);
|
||||
var xform = TransformQuery.GetComponent(uid);
|
||||
|
||||
// Delete now-gone containers.
|
||||
var toDelete = new ValueList<string>();
|
||||
foreach (var (id, container) in component.Containers)
|
||||
{
|
||||
if (cast.Containers.ContainsKey(id))
|
||||
{
|
||||
DebugTools.Assert(cast.Containers[id].ContainerType == container.GetType().Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var entity in container.ContainedEntities.ToArray())
|
||||
{
|
||||
container.Remove(entity,
|
||||
EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
metaQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true,
|
||||
reparent: false);
|
||||
|
||||
@@ -98,26 +102,32 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// Add new containers and update existing contents.
|
||||
|
||||
foreach (var (containerType, id, showEnts, occludesLight, entityUids) in cast.Containers.Values)
|
||||
foreach (var (id, data) in cast.Containers)
|
||||
{
|
||||
if (!component.Containers.TryGetValue(id, out var container))
|
||||
{
|
||||
container = ContainerFactory(component, containerType, id);
|
||||
var type = _serializer.FindSerializedType(typeof(BaseContainer), data.ContainerType);
|
||||
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type!, inject:false);
|
||||
container.Init(id, uid, component);
|
||||
component.Containers.Add(id, container);
|
||||
}
|
||||
|
||||
// sync show flag
|
||||
container.ShowContents = showEnts;
|
||||
container.OccludesLight = occludesLight;
|
||||
DebugTools.Assert(container.ID == id);
|
||||
container.ShowContents = data.ShowContents;
|
||||
container.OccludesLight = data.OccludesLight;
|
||||
|
||||
// Remove gone entities.
|
||||
var toRemove = new ValueList<EntityUid>();
|
||||
|
||||
DebugTools.Assert(!container.Contains(EntityUid.Invalid));
|
||||
|
||||
var stateNetEnts = data.ContainedEntities;
|
||||
var stateEnts = GetEntityArray(stateNetEnts); // No need to ensure entities.
|
||||
|
||||
foreach (var entity in container.ContainedEntities)
|
||||
{
|
||||
if (!entityUids.Contains(entity))
|
||||
{
|
||||
if (!stateEnts.Contains(entity))
|
||||
toRemove.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in toRemove)
|
||||
@@ -125,8 +135,8 @@ namespace Robust.Client.GameObjects
|
||||
container.Remove(
|
||||
entity,
|
||||
EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
metaQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true,
|
||||
reparent: false);
|
||||
|
||||
@@ -134,13 +144,11 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Remove entities that were expected, but have been removed from the container.
|
||||
var removedExpected = new ValueList<EntityUid>();
|
||||
foreach (var entityUid in container.ExpectedEntities)
|
||||
var removedExpected = new ValueList<NetEntity>();
|
||||
foreach (var netEntity in container.ExpectedEntities)
|
||||
{
|
||||
if (!entityUids.Contains(entityUid))
|
||||
{
|
||||
removedExpected.Add(entityUid);
|
||||
}
|
||||
if (!stateNetEnts.Contains(netEntity))
|
||||
removedExpected.Add(netEntity);
|
||||
}
|
||||
|
||||
foreach (var entityUid in removedExpected)
|
||||
@@ -149,14 +157,20 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Add new entities.
|
||||
foreach (var entity in entityUids)
|
||||
for (var i = 0; i < stateNetEnts.Length; i++)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(entity, out MetaDataComponent? meta))
|
||||
var entity = stateEnts[i];
|
||||
var netEnt = stateNetEnts[i];
|
||||
if (!entity.IsValid())
|
||||
{
|
||||
AddExpectedEntity(entity, container);
|
||||
DebugTools.Assert(netEnt.IsValid());
|
||||
AddExpectedEntity(netEnt, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
var meta = MetaData(entity);
|
||||
DebugTools.Assert(meta.NetEntity == netEnt);
|
||||
|
||||
// If an entity is currently in the shadow realm, it means we probably left PVS and are now getting
|
||||
// back into range. We do not want to directly insert this entity, as IF the container and entity
|
||||
// transform states did not get sent simultaneously, the entity's transform will be modified by the
|
||||
@@ -166,18 +180,18 @@ namespace Robust.Client.GameObjects
|
||||
// containers/players.
|
||||
if ((meta.Flags & MetaDataFlags.Detached) != 0)
|
||||
{
|
||||
AddExpectedEntity(entity, container);
|
||||
AddExpectedEntity(netEnt, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.Contains(entity))
|
||||
continue;
|
||||
|
||||
RemoveExpectedEntity(entity, out _);
|
||||
RemoveExpectedEntity(netEnt, out _);
|
||||
container.Insert(entity, EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
xform,
|
||||
metaQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true);
|
||||
|
||||
DebugTools.Assert(container.Contains(entity));
|
||||
@@ -198,7 +212,7 @@ namespace Robust.Client.GameObjects
|
||||
if (message.OldParent != null && message.OldParent.Value.IsValid())
|
||||
return;
|
||||
|
||||
if (!RemoveExpectedEntity(message.Entity, out var container))
|
||||
if (!RemoveExpectedEntity(GetNetEntity(message.Entity), out var container))
|
||||
return;
|
||||
|
||||
if (xform.ParentUid != container.Owner)
|
||||
@@ -208,84 +222,69 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
|
||||
container.Insert(message.Entity, EntityManager);
|
||||
}
|
||||
|
||||
private IContainer ContainerFactory(ContainerManagerComponent component, string containerType, string id)
|
||||
public void AddExpectedEntity(NetEntity netEntity, BaseContainer container)
|
||||
{
|
||||
var type = _serializer.FindSerializedType(typeof(IContainer), containerType);
|
||||
if (type is null) throw new ArgumentException($"Container of type {containerType} for id {id} cannot be found.");
|
||||
#if DEBUG
|
||||
var uid = GetEntity(netEntity);
|
||||
|
||||
var newContainer = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
|
||||
newContainer.ID = id;
|
||||
newContainer.Manager = component;
|
||||
return newContainer;
|
||||
}
|
||||
if (TryComp<MetaDataComponent>(uid, out var meta))
|
||||
{
|
||||
DebugTools.Assert((meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
|
||||
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
|
||||
}
|
||||
#endif
|
||||
|
||||
public void AddExpectedEntity(EntityUid uid, IContainer container)
|
||||
{
|
||||
DebugTools.Assert(!TryComp(uid, out MetaDataComponent? meta) ||
|
||||
(meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
|
||||
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
|
||||
|
||||
if (!ExpectedEntities.TryAdd(uid, container))
|
||||
if (!ExpectedEntities.TryAdd(netEntity, container))
|
||||
{
|
||||
// It is possible that we were expecting this entity in one container, but it has now moved to another
|
||||
// container, and this entity's state is just being applied before the old container is getting updated.
|
||||
var oldContainer = ExpectedEntities[uid];
|
||||
ExpectedEntities[uid] = container;
|
||||
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(uid),
|
||||
$"Entity {ToPrettyString(uid)} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
|
||||
oldContainer.ExpectedEntities.Remove(uid);
|
||||
var oldContainer = ExpectedEntities[netEntity];
|
||||
ExpectedEntities[netEntity] = container;
|
||||
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(netEntity),
|
||||
$"Entity {netEntity} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
|
||||
oldContainer.ExpectedEntities.Remove(netEntity);
|
||||
}
|
||||
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(uid),
|
||||
$"Contained entity {ToPrettyString(uid)} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Add(uid);
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(netEntity),
|
||||
$"Contained entity {netEntity} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Add(netEntity);
|
||||
}
|
||||
|
||||
public bool RemoveExpectedEntity(EntityUid uid, [NotNullWhen(true)] out IContainer? container)
|
||||
public bool RemoveExpectedEntity(NetEntity netEntity, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
if (!ExpectedEntities.Remove(uid, out container))
|
||||
if (!ExpectedEntities.Remove(netEntity, out container))
|
||||
return false;
|
||||
|
||||
DebugTools.Assert(container.ExpectedEntities.Contains(uid),
|
||||
$"While removing expected contained entity {ToPrettyString(uid)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Remove(uid);
|
||||
DebugTools.Assert(container.ExpectedEntities.Contains(netEntity),
|
||||
$"While removing expected contained entity {ToPrettyString(netEntity)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Remove(netEntity);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
|
||||
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var toUpdate in _updateQueue)
|
||||
{
|
||||
if (Deleted(toUpdate))
|
||||
continue;
|
||||
|
||||
UpdateEntityRecursively(toUpdate, xformQuery, pointQuery, spriteQuery);
|
||||
UpdateEntityRecursively(toUpdate);
|
||||
}
|
||||
|
||||
_updateQueue.Clear();
|
||||
}
|
||||
|
||||
private void UpdateEntityRecursively(
|
||||
EntityUid entity,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery)
|
||||
private void UpdateEntityRecursively(EntityUid entity)
|
||||
{
|
||||
// Recursively go up parents and containers to see whether both sprites and lights need to be occluded
|
||||
// Could maybe optimise this more by checking nearest parent that has sprite / light and whether it's container
|
||||
// occluded but this probably isn't a big perf issue.
|
||||
var xform = xformQuery.GetComponent(entity);
|
||||
var xform = TransformQuery.GetComponent(entity);
|
||||
var parent = xform.ParentUid;
|
||||
var child = entity;
|
||||
var spriteOccluded = false;
|
||||
@@ -293,7 +292,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
while (parent.IsValid() && (!spriteOccluded || !lightOccluded))
|
||||
{
|
||||
var parentXform = xformQuery.GetComponent(parent);
|
||||
var parentXform = TransformQuery.GetComponent(parent);
|
||||
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
|
||||
{
|
||||
spriteOccluded = spriteOccluded || !container.ShowContents;
|
||||
@@ -308,24 +307,21 @@ namespace Robust.Client.GameObjects
|
||||
// This is the CBT bit.
|
||||
// The issue is we need to go through the children and re-check whether they are or are not contained.
|
||||
// if they are contained then the occlusion values may need updating for all those children
|
||||
UpdateEntity(entity, xform, xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
UpdateEntity(entity, xform, spriteOccluded, lightOccluded);
|
||||
}
|
||||
|
||||
private void UpdateEntity(
|
||||
EntityUid entity,
|
||||
TransformComponent xform,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery,
|
||||
bool spriteOccluded,
|
||||
bool lightOccluded)
|
||||
{
|
||||
if (spriteQuery.TryGetComponent(entity, out var sprite))
|
||||
if (_spriteQuery.TryGetComponent(entity, out var sprite))
|
||||
{
|
||||
sprite.ContainerOccluded = spriteOccluded;
|
||||
}
|
||||
|
||||
if (pointQuery.TryGetComponent(entity, out var light))
|
||||
if (_pointLightQuery.TryGetComponent(entity, out var light))
|
||||
_lightSys.SetContainerOccluded(entity, lightOccluded, light);
|
||||
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
@@ -346,14 +342,14 @@ namespace Robust.Client.GameObjects
|
||||
childLightOccluded = childLightOccluded || container.OccludesLight;
|
||||
}
|
||||
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, childSpriteOccluded, childLightOccluded);
|
||||
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), childSpriteOccluded, childLightOccluded);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), spriteOccluded, lightOccluded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
while (chunkEnumerator.MoveNext(out var chunk))
|
||||
{
|
||||
foreach (var fixture in chunk.Fixtures)
|
||||
foreach (var fixture in chunk.Fixtures.Values)
|
||||
{
|
||||
var poly = (PolygonShape) fixture.Shape;
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="message">Arguments for this event.</param>
|
||||
/// <param name="replay">if true, current cmd state will not be checked or updated - use this for "replaying" an
|
||||
/// old input that was saved or buffered until further processing could be done</param>
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, IFullInputCmdMessage message, bool replay = false)
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
@@ -78,14 +78,27 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
|
||||
// local handlers can block sending over the network.
|
||||
if (handler.HandleCmdMessage(session, message))
|
||||
if (handler.HandleCmdMessage(EntityManager, session, message))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// send it off to the server
|
||||
DispatchInputCommand(message);
|
||||
var clientMsg = (ClientFullInputCmdMessage)message;
|
||||
var fullMsg = new FullInputCmdMessage(
|
||||
clientMsg.Tick,
|
||||
clientMsg.SubTick,
|
||||
(int)clientMsg.InputSequence,
|
||||
clientMsg.InputFunctionId,
|
||||
clientMsg.State,
|
||||
GetNetCoordinates(clientMsg.Coordinates),
|
||||
clientMsg.ScreenCoordinates)
|
||||
{
|
||||
Uid = GetNetEntity(clientMsg.Uid)
|
||||
};
|
||||
|
||||
DispatchInputCommand(clientMsg, fullMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -93,7 +106,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Handle a predicted input command.
|
||||
/// </summary>
|
||||
/// <param name="inputCmd">Input command to handle as predicted.</param>
|
||||
public void PredictInputCommand(FullInputCmdMessage inputCmd)
|
||||
public void PredictInputCommand(IFullInputCmdMessage inputCmd)
|
||||
{
|
||||
DebugTools.AssertNotNull(_playerManager.LocalPlayer);
|
||||
|
||||
@@ -103,15 +116,16 @@ namespace Robust.Client.GameObjects
|
||||
var session = _playerManager.LocalPlayer!.Session;
|
||||
foreach (var handler in BindRegistry.GetHandlers(keyFunc))
|
||||
{
|
||||
if (handler.HandleCmdMessage(session, inputCmd)) break;
|
||||
if (handler.HandleCmdMessage(EntityManager, session, inputCmd))
|
||||
break;
|
||||
}
|
||||
Predicted = false;
|
||||
|
||||
}
|
||||
|
||||
private void DispatchInputCommand(FullInputCmdMessage message)
|
||||
private void DispatchInputCommand(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message)
|
||||
{
|
||||
_stateManager.InputCommandDispatched(message);
|
||||
_stateManager.InputCommandDispatched(clientMsg, message);
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message, message.InputSequence);
|
||||
}
|
||||
|
||||
@@ -152,7 +166,7 @@ namespace Robust.Client.GameObjects
|
||||
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
|
||||
|
||||
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
|
||||
coords, new ScreenCoordinates(0, 0, default), EntityUid.Invalid);
|
||||
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
|
||||
|
||||
HandleInputCommand(localPlayer.Session, keyFunction, message);
|
||||
}
|
||||
@@ -206,8 +220,10 @@ namespace Robust.Client.GameObjects
|
||||
SetEntityContextActive(_inputManager, controlled);
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
protected override void PostInject()
|
||||
{
|
||||
base.PostInject();
|
||||
|
||||
_sawmillInputContext = _logManager.GetSawmill("input.context");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void MessageReceived(BoundUIWrapMessage ev)
|
||||
{
|
||||
var uid = ev.Entity;
|
||||
var uid = GetEntity(ev.Entity);
|
||||
|
||||
if (!TryComp<ClientUserInterfaceComponent>(uid, out var cmp))
|
||||
return;
|
||||
|
||||
@@ -53,7 +54,7 @@ namespace Robust.Client.GameObjects
|
||||
if(_playerManager.LocalPlayer != null)
|
||||
message.Session = _playerManager.LocalPlayer.Session;
|
||||
|
||||
message.Entity = uid;
|
||||
message.Entity = GetNetEntity(uid);
|
||||
message.UiKey = uiKey;
|
||||
|
||||
// Raise as object so the correct type is used.
|
||||
@@ -125,7 +126,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
internal void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
|
||||
{
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, msg, bui.UiKey));
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), msg, bui.UiKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// These methods are used by the Game State Manager.
|
||||
|
||||
EntityUid CreateEntity(string? prototypeName, EntityUid uid = default);
|
||||
EntityUid CreateEntity(string? prototypeName);
|
||||
|
||||
void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
|
||||
|
||||
// Entities that have removed networked components
|
||||
// could pool the ushort sets, but predicted component changes are rare... soo...
|
||||
internal readonly Dictionary<EntityUid, HashSet<ushort>> RemovedComponents = new();
|
||||
@@ -40,11 +40,11 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnTerminate(ref EntityTerminatingEvent ev)
|
||||
{
|
||||
if (!_timing.InPrediction || ev.Entity.IsClientSide())
|
||||
if (!_timing.InPrediction || IsClientSide(ev.Entity))
|
||||
return;
|
||||
|
||||
// Client-side entity deletion is not supported and will cause errors.
|
||||
Logger.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
|
||||
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
|
||||
}
|
||||
|
||||
private void OnCompRemoved(RemovedComponentEventArgs args)
|
||||
@@ -52,8 +52,9 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
if (args.Terminating)
|
||||
return;
|
||||
|
||||
var uid = args.BaseArgs.Owner;
|
||||
var comp = args.BaseArgs.Component;
|
||||
if (!_timing.InPrediction || comp.Owner.IsClientSide() || !comp.NetSyncEnabled)
|
||||
if (!_timing.InPrediction || !comp.NetSyncEnabled || IsClientSide(uid))
|
||||
return;
|
||||
|
||||
// Was this component added during prediction? If yes, then there is no need to re-add it when resetting.
|
||||
@@ -62,7 +63,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
var netId = _compFact.GetRegistration(comp).NetID;
|
||||
if (netId != null)
|
||||
RemovedComponents.GetOrNew(comp.Owner).Add(netId.Value);
|
||||
RemovedComponents.GetOrNew(uid).Add(netId.Value);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -73,7 +74,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnEntityDirty(EntityUid e)
|
||||
{
|
||||
if (_timing.InPrediction && !e.IsClientSide())
|
||||
if (_timing.InPrediction && !IsClientSide(e))
|
||||
DirtyEntities.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,10 @@ namespace Robust.Client.GameStates
|
||||
= new();
|
||||
|
||||
// Game state dictionaries that get used every tick.
|
||||
private readonly Dictionary<EntityUid, (bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState?nextState)> _toApply = new();
|
||||
private readonly Dictionary<EntityUid, EntityState> _toCreate = new();
|
||||
private readonly Dictionary<EntityUid, (bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
|
||||
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
|
||||
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
|
||||
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
|
||||
|
||||
private uint _metaCompNetId;
|
||||
|
||||
@@ -157,7 +159,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
public void InputCommandDispatched(FullInputCmdMessage message)
|
||||
public void InputCommandDispatched(ClientFullInputCmdMessage clientMessage, FullInputCmdMessage message)
|
||||
{
|
||||
if (!IsPredictionEnabled)
|
||||
{
|
||||
@@ -201,7 +203,7 @@ namespace Robust.Client.GameStates
|
||||
public void UpdateFullRep(GameState state, bool cloneDelta = false)
|
||||
=> _processor.UpdateFullRep(state, cloneDelta);
|
||||
|
||||
public Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
=> _processor.GetFullRep();
|
||||
|
||||
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
|
||||
@@ -210,7 +212,7 @@ namespace Robust.Client.GameStates
|
||||
PvsLeave?.Invoke(message);
|
||||
}
|
||||
|
||||
public void QueuePvsDetach(List<EntityUid> entities, GameTick tick)
|
||||
public void QueuePvsDetach(List<NetEntity> entities, GameTick tick)
|
||||
{
|
||||
_processor.AddLeavePvsMessage(entities, tick);
|
||||
if (_replayRecording.IsRecording)
|
||||
@@ -298,7 +300,7 @@ namespace Robust.Client.GameStates
|
||||
_processor.UpdateFullRep(curState);
|
||||
}
|
||||
|
||||
IEnumerable<EntityUid> createdEntities;
|
||||
IEnumerable<NetEntity> createdEntities;
|
||||
using (_prof.Group("ApplyGameState"))
|
||||
{
|
||||
if (_timing.LastProcessedTick < targetProcessedTick && nextState != null)
|
||||
@@ -323,7 +325,7 @@ namespace Robust.Client.GameStates
|
||||
catch (MissingMetadataException e)
|
||||
{
|
||||
// Something has gone wrong. Probably a missing meta-data component. Perhaps a full server state will fix it.
|
||||
RequestFullState(e.Uid);
|
||||
RequestFullState(e.NetEntity);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
@@ -392,10 +394,10 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestFullState(EntityUid? missingEntity = null)
|
||||
public void RequestFullState(NetEntity? missingEntity = null)
|
||||
{
|
||||
_sawmill.Info("Requesting full server state");
|
||||
_network.ClientSendMessage(new MsgStateRequestFull { Tick = _timing.LastRealTick , MissingEntity = missingEntity ?? EntityUid.Invalid });
|
||||
_network.ClientSendMessage(new MsgStateRequestFull { Tick = _timing.LastRealTick , MissingEntity = missingEntity ?? NetEntity.Invalid });
|
||||
_processor.RequestFullState();
|
||||
}
|
||||
|
||||
@@ -472,7 +474,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
var countReset = 0;
|
||||
var system = _entitySystemManager.GetEntitySystem<ClientDirtySystem>();
|
||||
var query = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
RemQueue<Component> toRemove = new();
|
||||
|
||||
// This is terrible, and I hate it.
|
||||
@@ -486,7 +488,8 @@ namespace Robust.Client.GameStates
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($"Entity {entity} was made dirty.");
|
||||
|
||||
if (!_processor.TryGetLastServerStates(entity, out var last))
|
||||
if (!metaQuery.TryGetComponent(entity, out var meta) ||
|
||||
!_processor.TryGetLastServerStates(meta.NetEntity, out var last))
|
||||
{
|
||||
// Entity was probably deleted on the server so do nothing.
|
||||
continue;
|
||||
@@ -500,7 +503,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (netId, comp) in netComps.Value)
|
||||
{
|
||||
DebugTools.AssertNotNull(netId);
|
||||
if (!comp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
@@ -565,7 +567,6 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
var meta = query.GetComponent(entity);
|
||||
DebugTools.Assert(meta.EntityLastModifiedTick > _timing.LastRealTick);
|
||||
meta.EntityLastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
@@ -589,15 +590,16 @@ namespace Robust.Client.GameStates
|
||||
/// initial server state for any newly created entity. It does this by simply using the standard <see
|
||||
/// cref="IEntityManager.GetComponentState(IEventBus, IComponent)"/>.
|
||||
/// </remarks>
|
||||
private void MergeImplicitData(IEnumerable<EntityUid> createdEntities)
|
||||
private void MergeImplicitData(IEnumerable<NetEntity> createdEntities)
|
||||
{
|
||||
var outputData = new Dictionary<EntityUid, Dictionary<ushort, ComponentState>>();
|
||||
var outputData = new Dictionary<NetEntity, Dictionary<ushort, ComponentState>>();
|
||||
var bus = _entityManager.EventBus;
|
||||
|
||||
foreach (var createdEntity in createdEntities)
|
||||
foreach (var netEntity in createdEntities)
|
||||
{
|
||||
var createdEntity = _entityManager.GetEntity(netEntity);
|
||||
var compData = new Dictionary<ushort, ComponentState>();
|
||||
outputData.Add(createdEntity, compData);
|
||||
outputData.Add(netEntity, compData);
|
||||
|
||||
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
|
||||
{
|
||||
@@ -618,7 +620,7 @@ namespace Robust.Client.GameStates
|
||||
_network.ClientSendMessage(new MsgStateAck() { Sequence = sequence });
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> ApplyGameState(GameState curState, GameState? nextState)
|
||||
public IEnumerable<NetEntity> ApplyGameState(GameState curState, GameState? nextState)
|
||||
{
|
||||
using var _ = _timing.StartStateApplicationArea();
|
||||
|
||||
@@ -638,7 +640,7 @@ namespace Robust.Client.GameStates
|
||||
_config.TickProcessMessages();
|
||||
}
|
||||
|
||||
(IEnumerable<EntityUid> Created, List<EntityUid> Detached) output;
|
||||
(IEnumerable<NetEntity> Created, List<NetEntity> Detached) output;
|
||||
using (_prof.Group("Entity"))
|
||||
{
|
||||
output = ApplyEntityStates(curState, nextState);
|
||||
@@ -657,7 +659,7 @@ namespace Robust.Client.GameStates
|
||||
return output.Created;
|
||||
}
|
||||
|
||||
private (IEnumerable<EntityUid> Created, List<EntityUid> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
private (IEnumerable<NetEntity> Created, List<NetEntity> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
{
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
@@ -666,6 +668,7 @@ namespace Robust.Client.GameStates
|
||||
var enteringPvs = 0;
|
||||
_toApply.Clear();
|
||||
_toCreate.Clear();
|
||||
_pendingReapplyNetStates.Clear();
|
||||
var curSpan = curState.EntityStates.Span;
|
||||
|
||||
// Create new entities
|
||||
@@ -676,21 +679,40 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var es in curSpan)
|
||||
{
|
||||
if (metas.HasComponent(es.Uid))
|
||||
if (_entityManager.TryGetEntity(es.NetEntity, out var nUid))
|
||||
{
|
||||
DebugTools.Assert(_entityManager.EntityExists(nUid));
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
var uid = es.Uid;
|
||||
var metaState = (MetaDataComponentState?)es.ComponentChanges.Value?.FirstOrDefault(c => c.NetID == _metaCompNetId).State;
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(uid);
|
||||
throw new MissingMetadataException(es.NetEntity);
|
||||
|
||||
_entities.CreateEntity(metaState.PrototypeId, uid);
|
||||
_toCreate.Add(uid, es);
|
||||
var uid = _entities.CreateEntity(metaState.PrototypeId);
|
||||
_toCreate.Add(es.NetEntity, es);
|
||||
_toApply.Add(uid, (false, GameTick.Zero, es, null));
|
||||
|
||||
var newMeta = metas.GetComponent(uid);
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
// We need to clear this mapping before assigning the real net id.
|
||||
// TODO NetEntity Jank: prevent the client from creating this in the first place.
|
||||
_entityManager.ClearNetEntity(newMeta.NetEntity);
|
||||
|
||||
_entityManager.SetNetEntity(uid, es.NetEntity, newMeta);
|
||||
newMeta.LastStateApplied = curState.ToSequence;
|
||||
|
||||
// Check if there's any component states awaiting this entity.
|
||||
if (_entityManager.PendingNetEntityStates.TryGetValue(es.NetEntity, out var value))
|
||||
{
|
||||
foreach (var (type, owner) in value)
|
||||
{
|
||||
var pending = _pendingReapplyNetStates.GetOrNew(owner);
|
||||
pending.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(count));
|
||||
@@ -698,7 +720,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var es in curSpan)
|
||||
{
|
||||
if (!metas.TryGetComponent(es.Uid, out var meta) || _toCreate.ContainsKey(es.Uid))
|
||||
var uid = _entityManager.GetEntity(es.NetEntity);
|
||||
|
||||
if (!metas.TryGetComponent(uid, out var meta) || _toCreate.ContainsKey(es.NetEntity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -715,7 +739,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
_toApply.Add(es.Uid, (isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
_toApply.Add(uid, (isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
meta.LastStateApplied = curState.ToSequence;
|
||||
}
|
||||
|
||||
@@ -729,22 +753,31 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var es in nextState.EntityStates.Span)
|
||||
{
|
||||
var uid = es.Uid;
|
||||
|
||||
if (!metas.TryGetComponent(uid, out var meta))
|
||||
if (!_entityManager.TryGetEntity(es.NetEntity, out var uid))
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(metas.HasComponent(uid));
|
||||
|
||||
// Does the next state actually have any future information about this entity that could be used for interpolation?
|
||||
if (es.EntityLastModified != nextState.ToSequence)
|
||||
continue;
|
||||
|
||||
if (_toApply.TryGetValue(uid, out var state))
|
||||
_toApply[uid] = (state.EnteringPvs, state.LastApplied, state.curState, es);
|
||||
if (_toApply.TryGetValue(uid.Value, out var state))
|
||||
_toApply[uid.Value] = (state.EnteringPvs, state.LastApplied, state.curState, es);
|
||||
else
|
||||
_toApply[uid] = (false, GameTick.Zero, null, es);
|
||||
_toApply[uid.Value] = (false, GameTick.Zero, null, es);
|
||||
}
|
||||
}
|
||||
|
||||
// Check pending states and see if we need to force any entities to re-run component states.
|
||||
foreach (var uid in _pendingReapplyNetStates.Keys)
|
||||
{
|
||||
if (_toApply.ContainsKey(uid))
|
||||
continue;
|
||||
|
||||
_toApply[uid] = (false, GameTick.Zero, null, null);
|
||||
}
|
||||
|
||||
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
|
||||
|
||||
// Apply entity states.
|
||||
@@ -767,6 +800,7 @@ namespace Robust.Client.GameStates
|
||||
if (!_toApply.TryGetValue(xform.ParentUid, out var parent) || !parent.EnteringPvs)
|
||||
queuedBroadphaseUpdates.Add((entity, xform));
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(_toApply.Count));
|
||||
}
|
||||
|
||||
@@ -830,37 +864,37 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// Construct hashset for set.Contains() checks.
|
||||
var entityStates = state.EntityStates.Span;
|
||||
var stateEnts = new HashSet<EntityUid>(entityStates.Length);
|
||||
var stateEnts = new HashSet<NetEntity>();
|
||||
foreach (var entState in entityStates)
|
||||
{
|
||||
stateEnts.Add(entState.Uid);
|
||||
stateEnts.Add(entState.NetEntity);
|
||||
}
|
||||
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
var currentEnts = _entities.GetEntities();
|
||||
var toDelete = new List<EntityUid>(Math.Max(64, _entities.EntityCount - stateEnts.Count));
|
||||
foreach (var ent in currentEnts)
|
||||
|
||||
// Client side entities won't need the transform, but that should always be a tiny minority of entities
|
||||
var metaQuery = _entityManager.AllEntityQueryEnumerator<MetaDataComponent, TransformComponent>();
|
||||
|
||||
while (metaQuery.MoveNext(out var ent, out var metadata, out var xform))
|
||||
{
|
||||
if (ent.IsClientSide())
|
||||
var netEnt = metadata.NetEntity;
|
||||
if (metadata.NetEntity.IsClientSide())
|
||||
{
|
||||
if (deleteClientEntities)
|
||||
toDelete.Add(ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stateEnts.Contains(ent) && metas.TryGetComponent(ent, out var meta))
|
||||
if (stateEnts.Contains(netEnt))
|
||||
{
|
||||
if (resetAllEntities || meta.LastStateApplied > state.ToSequence)
|
||||
meta.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
|
||||
if (resetAllEntities || metadata.LastStateApplied > state.ToSequence)
|
||||
metadata.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!xforms.TryGetComponent(ent, out var xform))
|
||||
continue;
|
||||
|
||||
// This entity is going to get deleted, but maybe some if its children won't be, so lets detach them to
|
||||
// null. First we will detach the parent in order to reduce the number of broadphase/lookup updates.
|
||||
xformSys.DetachParentToNull(ent, xform);
|
||||
@@ -873,7 +907,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
if (deleteClientChildren
|
||||
&& !deleteClientEntities // don't add duplicates
|
||||
&& child.Value.IsClientSide())
|
||||
&& _entities.IsClientSide(child.Value))
|
||||
{
|
||||
toDelete.Add(child.Value);
|
||||
}
|
||||
@@ -888,7 +922,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
private void ProcessDeletions(
|
||||
ReadOnlySpan<EntityUid> delSpan,
|
||||
ReadOnlySpan<NetEntity> delSpan,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
SharedTransformSystem xformSys)
|
||||
@@ -905,13 +939,19 @@ namespace Robust.Client.GameStates
|
||||
|
||||
using var _ = _prof.Group("Deletion");
|
||||
|
||||
foreach (var id in delSpan)
|
||||
foreach (var netEntity in delSpan)
|
||||
{
|
||||
// Don't worry about this for later.
|
||||
_entityManager.PendingNetEntityStates.Remove(netEntity);
|
||||
|
||||
if (!_entityManager.TryGetEntity(netEntity, out var id))
|
||||
continue;
|
||||
|
||||
if (!xforms.TryGetComponent(id, out var xform))
|
||||
continue; // Already deleted? or never sent to us?
|
||||
|
||||
// First, a single recursive map change
|
||||
xformSys.DetachParentToNull(id, xform);
|
||||
xformSys.DetachParentToNull(id.Value, xform);
|
||||
|
||||
// Then detach all children.
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
@@ -921,12 +961,12 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
// Finally, delete the entity.
|
||||
_entities.DeleteEntity(id);
|
||||
_entities.DeleteEntity(id.Value);
|
||||
}
|
||||
_prof.WriteValue("Count", ProfData.Int32(delSpan.Length));
|
||||
}
|
||||
|
||||
public void DetachImmediate(List<EntityUid> entities)
|
||||
public void DetachImmediate(List<NetEntity> entities)
|
||||
{
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
@@ -936,7 +976,7 @@ namespace Robust.Client.GameStates
|
||||
Detach(GameTick.MaxValue, null, entities, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
}
|
||||
|
||||
private List<EntityUid> ProcessPvsDeparture(
|
||||
private List<NetEntity> ProcessPvsDeparture(
|
||||
GameTick toTick,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
@@ -945,7 +985,7 @@ namespace Robust.Client.GameStates
|
||||
EntityLookupSystem lookupSys)
|
||||
{
|
||||
var toDetach = _processor.GetEntitiesToDetach(toTick, _pvsDetachBudget);
|
||||
var detached = new List<EntityUid>();
|
||||
var detached = new List<NetEntity>();
|
||||
|
||||
if (toDetach.Count == 0)
|
||||
return detached;
|
||||
@@ -967,16 +1007,18 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private void Detach(GameTick maxTick,
|
||||
GameTick? lastStateApplied,
|
||||
List<EntityUid> entities,
|
||||
List<NetEntity> entities,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
SharedTransformSystem xformSys,
|
||||
ContainerSystem containerSys,
|
||||
EntityLookupSystem lookupSys,
|
||||
List<EntityUid>? detached = null)
|
||||
List<NetEntity>? detached = null)
|
||||
{
|
||||
foreach (var ent in entities)
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
var ent = _entityManager.GetEntity(netEntity);
|
||||
|
||||
if (!metas.TryGetComponent(ent, out var meta))
|
||||
continue;
|
||||
|
||||
@@ -1001,7 +1043,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// In some cursed scenarios an entity inside of a container can leave PVS without the container itself leaving PVS.
|
||||
// In those situations, we need to add the entity back to the list of expected entities after detaching.
|
||||
IContainer? container = null;
|
||||
BaseContainer? container = null;
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
|
||||
@@ -1015,35 +1057,38 @@ namespace Robust.Client.GameStates
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
|
||||
|
||||
if (container != null)
|
||||
containerSys.AddExpectedEntity(ent, container);
|
||||
containerSys.AddExpectedEntity(netEntity, container);
|
||||
}
|
||||
|
||||
detached?.Add(ent);
|
||||
detached?.Add(netEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAndStart(Dictionary<EntityUid, EntityState> toCreate)
|
||||
private void InitializeAndStart(Dictionary<NetEntity, EntityState> toCreate)
|
||||
{
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
HashSet<EntityUid> brokenEnts = new HashSet<EntityUid>();
|
||||
var brokenEnts = new List<EntityUid>();
|
||||
#endif
|
||||
using (_prof.Group("Initialize Entity"))
|
||||
{
|
||||
foreach (var entity in toCreate.Keys)
|
||||
foreach (var netEntity in toCreate.Keys)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
#endif
|
||||
_entities.InitializeEntity(entity);
|
||||
_entities.InitializeEntity(entity, metaQuery.GetComponent(entity));
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Server entity threw in Init: ent={_entityManager.ToPrettyString(entity)}");
|
||||
_sawmill.Error($"Server entity threw in Init: ent={_entities.ToPrettyString(entity)}");
|
||||
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
|
||||
brokenEnts.Add(entity);
|
||||
toCreate.Remove(entity);
|
||||
toCreate.Remove(netEntity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1051,8 +1096,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
using (_prof.Group("Start Entity"))
|
||||
{
|
||||
foreach (var entity in toCreate.Keys)
|
||||
foreach (var netEntity in toCreate.Keys)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
@@ -1065,7 +1111,7 @@ namespace Robust.Client.GameStates
|
||||
_sawmill.Error($"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}");
|
||||
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
|
||||
brokenEnts.Add(entity);
|
||||
toCreate.Remove(entity);
|
||||
toCreate.Remove(netEntity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1082,8 +1128,9 @@ namespace Robust.Client.GameStates
|
||||
private void HandleEntityState(EntityUid uid, IEventBus bus, EntityState? curState,
|
||||
EntityState? nextState, GameTick lastApplied, GameTick toTick, bool enteringPvs)
|
||||
{
|
||||
var size = (curState?.ComponentChanges.Span.Length ?? 0) + (nextState?.ComponentChanges.Span.Length ?? 0);
|
||||
var compStateWork = new Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)>(size);
|
||||
_compStateWork.Clear();
|
||||
var meta = _entityManager.GetComponent<MetaDataComponent>(uid);
|
||||
var netEntity = meta.NetEntity;
|
||||
|
||||
// First remove any deleted components
|
||||
if (curState?.NetComponents != null)
|
||||
@@ -1108,7 +1155,7 @@ namespace Robust.Client.GameStates
|
||||
//
|
||||
// as to why we need to reset: because in the process of detaching to null-space, we will have dirtied
|
||||
// the entity. most notably, all entities will have been ejected from their containers.
|
||||
foreach (var (id, state) in _processor.GetLastServerStates(uid))
|
||||
foreach (var (id, state) in _processor.GetLastServerStates(netEntity))
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(uid, id, out var comp))
|
||||
{
|
||||
@@ -1118,7 +1165,7 @@ namespace Robust.Client.GameStates
|
||||
_entityManager.AddComponent(uid, newComp, true);
|
||||
}
|
||||
|
||||
compStateWork[id] = (comp, state, null);
|
||||
_compStateWork[id] = (comp, state, null);
|
||||
}
|
||||
}
|
||||
else if (curState != null)
|
||||
@@ -1135,7 +1182,7 @@ namespace Robust.Client.GameStates
|
||||
else if (compChange.LastModifiedTick <= lastApplied && lastApplied != GameTick.Zero)
|
||||
continue;
|
||||
|
||||
compStateWork[compChange.NetID] = (comp, compChange.State, null);
|
||||
_compStateWork[compChange.NetID] = (comp, compChange.State, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1153,14 +1200,38 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
if (compStateWork.TryGetValue(compState.NetID, out var state))
|
||||
compStateWork[compState.NetID] = (comp, state.curState, compState.State);
|
||||
if (_compStateWork.TryGetValue(compState.NetID, out var state))
|
||||
_compStateWork[compState.NetID] = (comp, state.curState, compState.State);
|
||||
else
|
||||
compStateWork[compState.NetID] = (comp, null, compState.State);
|
||||
_compStateWork[compState.NetID] = (comp, null, compState.State);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (comp, cur, next) in compStateWork.Values)
|
||||
// If we have a NetEntity we reference come in then apply their state.
|
||||
if (_pendingReapplyNetStates.TryGetValue(uid, out var reapplyTypes))
|
||||
{
|
||||
var lastState = _processor.GetLastServerStates(netEntity);
|
||||
|
||||
foreach (var type in reapplyTypes)
|
||||
{
|
||||
var compRef = _compFactory.GetRegistration(type);
|
||||
var netId = compRef.NetID;
|
||||
|
||||
if (netId == null)
|
||||
continue;
|
||||
|
||||
if (_compStateWork.ContainsKey(netId.Value) ||
|
||||
!_entityManager.TryGetComponent(uid, type, out var comp) ||
|
||||
!lastState.TryGetValue(netId.Value, out var lastCompState))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_compStateWork[netId.Value] = (comp, lastCompState, null);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (comp, cur, next) in _compStateWork.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1181,6 +1252,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
#region Debug Commands
|
||||
|
||||
private bool TryParseUid(IConsoleShell shell, string[] args, out EntityUid uid, [NotNullWhen(true)] out MetaDataComponent? meta)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
@@ -1239,7 +1311,7 @@ namespace Robust.Client.GameStates
|
||||
var xform = _entities.GetComponent<TransformComponent>(uid);
|
||||
if (xform.ParentUid.IsValid())
|
||||
{
|
||||
IContainer? container = null;
|
||||
BaseContainer? container = null;
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
_entities.TryGetComponent(xform.ParentUid, out MetaDataComponent? containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0)
|
||||
@@ -1250,7 +1322,7 @@ namespace Robust.Client.GameStates
|
||||
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachParentToNull(uid, xform);
|
||||
|
||||
if (container != null)
|
||||
containerSys.AddExpectedEntity(uid, container);
|
||||
containerSys.AddExpectedEntity(_entities.GetNetEntity(uid), container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1267,18 +1339,21 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// If this is not a client-side entity, it also needs to be removed from the full-server state dictionary to
|
||||
// avoid errors. This has to be done recursively for all children.
|
||||
void _recursiveRemoveState(TransformComponent xform, EntityQuery<TransformComponent> query)
|
||||
void _recursiveRemoveState(NetEntity netEntity, TransformComponent xform, EntityQuery<MetaDataComponent> metaQuery, EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
_processor._lastStateFullRep.Remove(xform.Owner);
|
||||
_processor._lastStateFullRep.Remove(netEntity);
|
||||
foreach (var child in xform.ChildEntities)
|
||||
{
|
||||
if (query.TryGetComponent(child, out var childXform))
|
||||
_recursiveRemoveState(childXform, query);
|
||||
if (xformQuery.TryGetComponent(child, out var childXform) &&
|
||||
metaQuery.TryGetComponent(child, out var childMeta))
|
||||
{
|
||||
_recursiveRemoveState(childMeta.NetEntity, childXform, metaQuery, xformQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!uid.IsClientSide() && _entities.TryGetComponent(uid, out TransformComponent? xform))
|
||||
_recursiveRemoveState(xform, _entities.GetEntityQuery<TransformComponent>());
|
||||
if (!_entities.IsClientSide(uid) && _entities.TryGetComponent(uid, out TransformComponent? xform))
|
||||
_recursiveRemoveState(meta.NetEntity, xform, _entities.GetEntityQuery<MetaDataComponent>(), _entities.GetEntityQuery<TransformComponent>());
|
||||
|
||||
// Set ApplyingState to true to avoid logging errors about predicting the deletion of networked entities.
|
||||
using (_timing.StartStateApplicationArea())
|
||||
@@ -1312,7 +1387,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
meta.Flags &= ~MetaDataFlags.Detached;
|
||||
|
||||
if (!_processor.TryGetLastServerStates(uid, out var lastState))
|
||||
if (!_processor.TryGetLastServerStates(meta.NetEntity, out var lastState))
|
||||
return;
|
||||
|
||||
foreach (var (id, state) in lastState)
|
||||
@@ -1353,9 +1428,9 @@ namespace Robust.Client.GameStates
|
||||
public sealed class GameStateAppliedArgs : EventArgs
|
||||
{
|
||||
public GameState AppliedState { get; }
|
||||
public readonly List<EntityUid> Detached;
|
||||
public readonly List<NetEntity> Detached;
|
||||
|
||||
public GameStateAppliedArgs(GameState appliedState, List<EntityUid> detached)
|
||||
public GameStateAppliedArgs(GameState appliedState, List<NetEntity> detached)
|
||||
{
|
||||
AppliedState = appliedState;
|
||||
Detached = detached;
|
||||
@@ -1364,12 +1439,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public sealed class MissingMetadataException : Exception
|
||||
{
|
||||
public readonly EntityUid Uid;
|
||||
public readonly NetEntity NetEntity;
|
||||
|
||||
public MissingMetadataException(EntityUid uid)
|
||||
: base($"Server state is missing the metadata component for a new entity: {uid}.")
|
||||
public MissingMetadataException(NetEntity netEntity)
|
||||
: base($"Server state is missing the metadata component for a new entity: {netEntity}.")
|
||||
{
|
||||
Uid = uid;
|
||||
NetEntity = netEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private readonly List<GameState> _stateBuffer = new();
|
||||
|
||||
private readonly Dictionary<GameTick, List<EntityUid>> _pvsDetachMessages = new();
|
||||
private readonly Dictionary<GameTick, List<NetEntity>> _pvsDetachMessages = new();
|
||||
|
||||
private ISawmill _logger = default!;
|
||||
private ISawmill _stateLogger = default!;
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
|
||||
/// </summary>
|
||||
internal readonly Dictionary<EntityUid, Dictionary<ushort, ComponentState>> _lastStateFullRep
|
||||
internal readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _lastStateFullRep
|
||||
= new();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -178,10 +178,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var entityState in state.EntityStates.Span)
|
||||
{
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.Uid, out var compData))
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
|
||||
{
|
||||
compData = new Dictionary<ushort, ComponentState>();
|
||||
_lastStateFullRep.Add(entityState.Uid, compData);
|
||||
_lastStateFullRep.Add(entityState.NetEntity, compData);
|
||||
}
|
||||
|
||||
foreach (var change in entityState.ComponentChanges.Span)
|
||||
@@ -263,7 +263,7 @@ namespace Robust.Client.GameStates
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void AddLeavePvsMessage(List<EntityUid> entities, GameTick tick)
|
||||
internal void AddLeavePvsMessage(List<NetEntity> entities, GameTick tick)
|
||||
{
|
||||
// Late message may still need to be processed,
|
||||
DebugTools.Assert(entities.Count > 0);
|
||||
@@ -272,9 +272,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public void ClearDetachQueue() => _pvsDetachMessages.Clear();
|
||||
|
||||
public List<(GameTick Tick, List<EntityUid> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
|
||||
public List<(GameTick Tick, List<NetEntity> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
|
||||
{
|
||||
var result = new List<(GameTick Tick, List<EntityUid> Entities)>();
|
||||
var result = new List<(GameTick Tick, List<NetEntity> Entities)>();
|
||||
foreach (var (tick, entities) in _pvsDetachMessages)
|
||||
{
|
||||
if (tick > toTick)
|
||||
@@ -353,11 +353,11 @@ namespace Robust.Client.GameStates
|
||||
LastFullStateRequested = _timing.LastRealTick;
|
||||
}
|
||||
|
||||
public void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> implicitData)
|
||||
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> implicitData)
|
||||
{
|
||||
foreach (var (uid, implicitEntState) in implicitData)
|
||||
foreach (var (netEntity, implicitEntState) in implicitData)
|
||||
{
|
||||
var fullRep = _lastStateFullRep[uid];
|
||||
var fullRep = _lastStateFullRep[netEntity];
|
||||
|
||||
foreach (var (netId, implicitCompState) in implicitEntState)
|
||||
{
|
||||
@@ -374,7 +374,7 @@ namespace Robust.Client.GameStates
|
||||
// state from the entity prototype.
|
||||
if (implicitCompState is not IComponentDeltaState implicitDelta || !implicitDelta.FullState)
|
||||
{
|
||||
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {uid}");
|
||||
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {netEntity}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -385,17 +385,17 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity)
|
||||
public Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity netEntity)
|
||||
{
|
||||
return _lastStateFullRep[entity];
|
||||
return _lastStateFullRep[netEntity];
|
||||
}
|
||||
|
||||
public Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
{
|
||||
return _lastStateFullRep;
|
||||
}
|
||||
|
||||
public bool TryGetLastServerStates(EntityUid entity,
|
||||
public bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary)
|
||||
{
|
||||
return _lastStateFullRep.TryGetValue(entity, out dictionary);
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Applies a given set of game states.
|
||||
/// </summary>
|
||||
IEnumerable<EntityUid> ApplyGameState(GameState curState, GameState? nextState);
|
||||
IEnumerable<NetEntity> ApplyGameState(GameState curState, GameState? nextState);
|
||||
|
||||
/// <summary>
|
||||
/// Resets any entities that have changed while predicting future ticks.
|
||||
@@ -86,12 +86,12 @@ namespace Robust.Client.GameStates
|
||||
/// An input command has been dispatched.
|
||||
/// </summary>
|
||||
/// <param name="message">Message being dispatched.</param>
|
||||
void InputCommandDispatched(FullInputCmdMessage message);
|
||||
void InputCommandDispatched(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Requests a full state from the server. This should override even implicit entity data.
|
||||
/// </summary>
|
||||
void RequestFullState(EntityUid? missingEntity = null);
|
||||
void RequestFullState(NetEntity? missingEntity = null);
|
||||
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Returns the full collection of cached game states that are used to reset predicted entities.
|
||||
/// </summary>
|
||||
Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep();
|
||||
Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep();
|
||||
|
||||
/// <summary>
|
||||
/// This will perform some setup in order to reset the game to an earlier state. To fully reset the state
|
||||
@@ -144,12 +144,12 @@ namespace Robust.Client.GameStates
|
||||
/// Queue a collection of entities that are to be detached to null-space & marked as PVS-detached.
|
||||
/// This store and modify the list given to it.
|
||||
/// </summary>
|
||||
void QueuePvsDetach(List<EntityUid> entities, GameTick tick);
|
||||
void QueuePvsDetach(List<NetEntity> entities, GameTick tick);
|
||||
|
||||
/// <summary>
|
||||
/// Immediately detach several entities.
|
||||
/// </summary>
|
||||
void DetachImmediate(List<EntityUid> entities);
|
||||
void DetachImmediate(List<NetEntity> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the PVS detach queue.
|
||||
|
||||
@@ -83,13 +83,13 @@ namespace Robust.Client.GameStates
|
||||
/// The data to merge.
|
||||
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
|
||||
/// </param>
|
||||
void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> data);
|
||||
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> data);
|
||||
|
||||
/// <summary>
|
||||
/// Get the last state data from the server for an entity.
|
||||
/// </summary>
|
||||
/// <returns>Dictionary (net ID -> ComponentState)</returns>
|
||||
Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity);
|
||||
Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the number of applicable states in the game state buffer from a given tick.
|
||||
@@ -98,7 +98,7 @@ namespace Robust.Client.GameStates
|
||||
/// <param name="fromTick">The tick to calculate from.</param>
|
||||
int CalculateBufferSize(GameTick fromTick);
|
||||
|
||||
bool TryGetLastServerStates(EntityUid entity,
|
||||
bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private readonly Font _font;
|
||||
private readonly int _lineHeight;
|
||||
private readonly Dictionary<EntityUid, NetEntData> _netEnts = new();
|
||||
private readonly Dictionary<NetEntity, NetEntData> _netEnts = new();
|
||||
|
||||
public NetEntityOverlay()
|
||||
{
|
||||
@@ -77,12 +77,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var entityState in gameState.EntityStates.Span)
|
||||
{
|
||||
if (!_netEnts.TryGetValue(entityState.Uid, out var netEnt))
|
||||
if (!_netEnts.TryGetValue(entityState.NetEntity, out var netEnt))
|
||||
{
|
||||
if (_netEnts.Count >= _maxEnts)
|
||||
continue;
|
||||
|
||||
_netEnts[entityState.Uid] = netEnt = new();
|
||||
_netEnts[entityState.NetEntity] = netEnt = new();
|
||||
}
|
||||
|
||||
if (!netEnt.InPVS && netEnt.LastUpdate < gameState.ToSequence)
|
||||
@@ -119,11 +119,13 @@ namespace Robust.Client.GameStates
|
||||
var screenHandle = args.ScreenHandle;
|
||||
|
||||
int i = 0;
|
||||
foreach (var (uid, netEnt) in _netEnts)
|
||||
foreach (var (nent, netEnt) in _netEnts)
|
||||
{
|
||||
var uid = _entityManager.GetEntity(nent);
|
||||
|
||||
if (!_entityManager.EntityExists(uid))
|
||||
{
|
||||
_netEnts.Remove(uid);
|
||||
_netEnts.Remove(nent);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Robust.Client.GameStates
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
private const int HistorySize = 60 * 5; // number of ticks to keep in history.
|
||||
private const int TargetPayloadBps = 56000 / 8; // Target Payload size in Bytes per second. A mind-numbing fifty-six thousand bits per second, who would ever need more?
|
||||
@@ -73,7 +74,7 @@ namespace Robust.Client.GameStates
|
||||
_history.Add((toSeq, sz, lag, buffer));
|
||||
|
||||
// not watching an ent
|
||||
if(!WatchEntId.IsValid() || WatchEntId.IsClientSide())
|
||||
if(!WatchEntId.IsValid() || _entManager.IsClientSide(WatchEntId))
|
||||
return;
|
||||
|
||||
string? entStateString = null;
|
||||
@@ -86,7 +87,9 @@ namespace Robust.Client.GameStates
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entState in entStates.Span)
|
||||
{
|
||||
if (entState.Uid != WatchEntId)
|
||||
var uid = _entManager.GetEntity(entState.NetEntity);
|
||||
|
||||
if (uid != WatchEntId)
|
||||
continue;
|
||||
|
||||
if (!entState.ComponentChanges.HasContents)
|
||||
@@ -115,7 +118,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var ent in args.Detached)
|
||||
{
|
||||
if (ent != WatchEntId)
|
||||
var uid = _entManager.GetEntity(ent);
|
||||
|
||||
if (uid != WatchEntId)
|
||||
continue;
|
||||
|
||||
conShell.WriteLine($"watchEnt: Left PVS at tick {args.AppliedState.ToSequence}, eid={WatchEntId}" + "\n");
|
||||
@@ -126,7 +131,9 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var entDelete in entDeletes.Span)
|
||||
{
|
||||
if (entDelete == WatchEntId)
|
||||
var uid = _entManager.GetEntity(entDelete);
|
||||
|
||||
if (uid == WatchEntId)
|
||||
entDelString = "\n Deleted";
|
||||
}
|
||||
}
|
||||
@@ -294,30 +301,33 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private sealed class NetWatchEntCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public override string Command => "net_watchent";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntityUid eValue;
|
||||
EntityUid? entity;
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
eValue = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
entity = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
}
|
||||
else if (!EntityUid.TryParse(args[0], out eValue))
|
||||
else if (!NetEntity.TryParse(args[0], out var netEntity) || !_entManager.TryGetEntity(netEntity, out entity))
|
||||
{
|
||||
shell.WriteError("Invalid argument: Needs to be 0 or an entityId.");
|
||||
return;
|
||||
}
|
||||
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if (!overlayMan.TryGetOverlay(out NetGraphOverlay? overlay))
|
||||
if (!_overlayManager.TryGetOverlay(out NetGraphOverlay? overlay))
|
||||
{
|
||||
overlay = new();
|
||||
overlayMan.AddOverlay(overlay);
|
||||
overlay = new NetGraphOverlay();
|
||||
_overlayManager.AddOverlay(overlay);
|
||||
}
|
||||
|
||||
overlay.WatchEntId = eValue;
|
||||
overlay.WatchEntId = entity.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Robust.Client.Input
|
||||
common.AddFunction(EngineKeyFunctions.TextWordDelete);
|
||||
common.AddFunction(EngineKeyFunctions.TextNewline);
|
||||
common.AddFunction(EngineKeyFunctions.TextSubmit);
|
||||
common.AddFunction(EngineKeyFunctions.MultilineTextSubmit);
|
||||
common.AddFunction(EngineKeyFunctions.TextCopy);
|
||||
common.AddFunction(EngineKeyFunctions.TextCut);
|
||||
common.AddFunction(EngineKeyFunctions.TextPaste);
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Client.Map
|
||||
|
||||
public Texture TileTextureAtlas => _tileTextureAtlas ?? Texture.Transparent;
|
||||
|
||||
private readonly Dictionary<ushort, Box2[]> _tileRegions = new();
|
||||
private readonly Dictionary<int, Box2[]> _tileRegions = new();
|
||||
|
||||
public Box2 ErrorTileRegion { get; private set; }
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Robust.Client.Map
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box2[]? TileAtlasRegion(ushort tileType)
|
||||
public Box2[]? TileAtlasRegion(int tileType)
|
||||
{
|
||||
if (_tileRegions.TryGetValue(tileType, out var region))
|
||||
{
|
||||
|
||||
@@ -27,6 +27,6 @@ namespace Robust.Client.Map
|
||||
/// Gets the region inside the texture atlas to use to draw a tile type.
|
||||
/// </summary>
|
||||
/// <returns>If null, do not draw the tile at all.</returns>
|
||||
Box2[]? TileAtlasRegion(ushort tileType);
|
||||
Box2[]? TileAtlasRegion(int tileType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,9 @@ namespace Robust.Client.Physics
|
||||
Log.Info($"Received grid fixture debug data");
|
||||
if (!_enableDebug) return;
|
||||
|
||||
_nodes[ev.Grid] = ev.Nodes;
|
||||
_connections[ev.Grid] = ev.Connections;
|
||||
var grid = GetEntity(ev.Grid);
|
||||
_nodes[grid] = ev.Nodes;
|
||||
_connections[grid] = ev.Connections;
|
||||
}
|
||||
|
||||
private sealed class GridSplitNodeOverlay : Overlay
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Client.Physics
|
||||
{
|
||||
if (args.Current is not JointComponentState jointState) return;
|
||||
|
||||
component.Relay = jointState.Relay;
|
||||
component.Relay = EnsureEntity<JointComponent>(jointState.Relay, uid);
|
||||
|
||||
// Initial state gets applied before the entity (& entity's transform) have been initialized.
|
||||
// So just let joint init code handle that.
|
||||
@@ -29,7 +29,7 @@ namespace Robust.Client.Physics
|
||||
component.Joints.Clear();
|
||||
foreach (var (id, state) in jointState.Joints)
|
||||
{
|
||||
component.Joints[id] = state.GetJoint();
|
||||
component.Joints[id] = state.GetJoint(EntityManager, uid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -62,8 +62,8 @@ namespace Robust.Client.Physics
|
||||
continue;
|
||||
}
|
||||
|
||||
var other = state.UidA == uid ? state.UidB : state.UidA;
|
||||
|
||||
var uidA = GetEntity(state.UidA);
|
||||
var other = uidA == uid ? GetEntity(state.UidB) : uidA;
|
||||
|
||||
// Add new joint (if possible).
|
||||
// Need to wait for BOTH joint components to come in first before we can add it. Yay dependencies!
|
||||
@@ -82,11 +82,11 @@ namespace Robust.Client.Physics
|
||||
// TODO: component state handling ordering.
|
||||
if (Transform(uid).MapID == MapId.Nullspace)
|
||||
{
|
||||
AddedJoints.Add(state.GetJoint());
|
||||
AddedJoints.Add(state.GetJoint(EntityManager, uid));
|
||||
continue;
|
||||
}
|
||||
|
||||
AddJoint(state.GetJoint());
|
||||
AddJoint(state.GetJoint(EntityManager, uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Robust.Client.Placement
|
||||
}
|
||||
}))
|
||||
.Bind(EngineKeyFunctions.EditorPlaceObject, new PointerStateInputCmdHandler(
|
||||
(session, coords, uid) =>
|
||||
(session, netCoords, nent) =>
|
||||
{
|
||||
if (!IsActive)
|
||||
return false;
|
||||
@@ -239,15 +239,15 @@ namespace Robust.Client.Placement
|
||||
|
||||
if (Eraser)
|
||||
{
|
||||
if (HandleDeletion(coords))
|
||||
if (HandleDeletion(netCoords))
|
||||
return true;
|
||||
|
||||
if (uid == EntityUid.Invalid)
|
||||
if (nent == EntityUid.Invalid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HandleDeletion(uid);
|
||||
HandleDeletion(nent);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -428,7 +428,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
var msg = new MsgPlacement();
|
||||
msg.PlaceType = PlacementManagerMessage.RequestEntRemove;
|
||||
msg.EntityUid = entity;
|
||||
msg.EntityUid = EntityManager.GetNetEntity(entity);
|
||||
_networkManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
var msg = new MsgPlacement();
|
||||
msg.PlaceType = PlacementManagerMessage.RequestRectRemove;
|
||||
msg.EntityCoordinates = new EntityCoordinates(StartPoint.EntityId, rect.BottomLeft);
|
||||
msg.NetCoordinates = new NetCoordinates(EntityManager.GetNetEntity(StartPoint.EntityId), rect.BottomLeft);
|
||||
msg.RectSize = rect.Size;
|
||||
_networkManager.ClientSendMessage(msg);
|
||||
}
|
||||
@@ -790,7 +790,7 @@ namespace Robust.Client.Placement
|
||||
message.EntityTemplateName = CurrentPermission.EntityType;
|
||||
|
||||
// world x and y
|
||||
message.EntityCoordinates = coordinates;
|
||||
message.NetCoordinates = EntityManager.GetNetCoordinates(coordinates);
|
||||
|
||||
message.DirRcv = Direction;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Player
|
||||
{
|
||||
public interface IPlayerManager : Shared.Players.ISharedPlayerManager
|
||||
public interface IPlayerManager : ISharedPlayerManager
|
||||
{
|
||||
new IEnumerable<ICommonSession> Sessions { get; }
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -24,6 +25,7 @@ namespace Robust.Client.Player
|
||||
[Dependency] private readonly IClientNetManager _network = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Active sessions of connected clients to the server.
|
||||
@@ -65,6 +67,8 @@ namespace Robust.Client.Player
|
||||
}
|
||||
}
|
||||
private LocalPlayer? _localPlayer;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public event Action<LocalPlayerChangedEventArgs>? LocalPlayerChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -82,6 +86,7 @@ namespace Robust.Client.Player
|
||||
{
|
||||
_client.RunLevelChanged += OnRunLevelChanged;
|
||||
|
||||
_sawmill = _logMan.GetSawmill("player");
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>();
|
||||
_network.RegisterNetMessage<MsgPlayerList>(HandlePlayerList);
|
||||
}
|
||||
@@ -122,7 +127,13 @@ namespace Robust.Client.Player
|
||||
|
||||
if (myState != null)
|
||||
{
|
||||
UpdateAttachedEntity(myState.ControlledEntity);
|
||||
var uid = _entManager.GetEntity(myState.ControlledEntity);
|
||||
if (myState.ControlledEntity is {Valid: true} && !_entManager.EntityExists(uid))
|
||||
{
|
||||
_sawmill.Error($"Received player state for local player with an unknown net entity!");
|
||||
}
|
||||
|
||||
UpdateAttachedEntity(uid);
|
||||
UpdateSessionStatus(myState.Status);
|
||||
}
|
||||
|
||||
@@ -181,11 +192,13 @@ namespace Robust.Client.Player
|
||||
if (_sessions.TryGetValue(state.UserId, out var session))
|
||||
{
|
||||
var local = (PlayerSession) session;
|
||||
var controlled = _entManager.GetEntity(state.ControlledEntity);
|
||||
|
||||
// Exists, update data.
|
||||
if (local.Name == state.Name
|
||||
&& local.Status == state.Status
|
||||
&& local.Ping == state.Ping
|
||||
&& local.AttachedEntity == state.ControlledEntity)
|
||||
&& local.AttachedEntity == controlled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -194,7 +207,7 @@ namespace Robust.Client.Player
|
||||
local.Name = state.Name;
|
||||
local.Status = state.Status;
|
||||
local.Ping = state.Ping;
|
||||
local.AttachedEntity = state.ControlledEntity;
|
||||
local.AttachedEntity = controlled;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -206,7 +219,7 @@ namespace Robust.Client.Player
|
||||
Name = state.Name,
|
||||
Status = state.Status,
|
||||
Ping = state.Ping,
|
||||
AttachedEntity = state.ControlledEntity,
|
||||
AttachedEntity = _entManager.GetEntity(state.ControlledEntity),
|
||||
};
|
||||
_sessions.Add(state.UserId, newSession);
|
||||
if (state.UserId == LocalPlayer!.UserId)
|
||||
|
||||
22
Robust.Client/Replays/Commands/ReplayToggleUiCommand.cs
Normal file
22
Robust.Client/Replays/Commands/ReplayToggleUiCommand.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Robust.Client.Replays.UI;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Replays.Commands;
|
||||
|
||||
public sealed class ReplayToggleUiCommand : BaseReplayCommand
|
||||
{
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
|
||||
public override string Command => "replay_toggleui";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var screen = _userInterfaceManager.ActiveScreen;
|
||||
if (screen == null || !screen.TryGetWidget(out ReplayControlWidget? replayWidget))
|
||||
return;
|
||||
|
||||
replayWidget.Visible = !replayWidget.Visible;
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,8 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
HashSet<ResPath> uploadedFiles = new();
|
||||
var detached = new HashSet<EntityUid>();
|
||||
var detachQueue = new Dictionary<GameTick, List<EntityUid>>();
|
||||
var detached = new HashSet<NetEntity>();
|
||||
var detachQueue = new Dictionary<GameTick, List<NetEntity>>();
|
||||
|
||||
if (initMessages != null)
|
||||
UpdateMessages(initMessages, uploadedFiles, prototypes, cvars, detachQueue, ref timeBase, true);
|
||||
@@ -92,11 +92,11 @@ public sealed partial class ReplayLoadManager
|
||||
ProcessQueue(GameTick.MaxValue, detachQueue, detached);
|
||||
|
||||
var entSpan = state0.EntityStates.Value;
|
||||
Dictionary<EntityUid, EntityState> entStates = new(entSpan.Count);
|
||||
Dictionary<NetEntity, EntityState> entStates = new(entSpan.Count);
|
||||
foreach (var entState in entSpan)
|
||||
{
|
||||
var modifiedState = AddImplicitData(entState);
|
||||
entStates.Add(entState.Uid, modifiedState);
|
||||
entStates.Add(entState.NetEntity, modifiedState);
|
||||
}
|
||||
|
||||
await callback(0, states.Count, LoadingState.ProcessingFiles, true);
|
||||
@@ -112,11 +112,11 @@ public sealed partial class ReplayLoadManager
|
||||
default,
|
||||
entStates.Values.ToArray(),
|
||||
playerStates.Values.ToArray(),
|
||||
Array.Empty<EntityUid>());
|
||||
Array.Empty<NetEntity>());
|
||||
checkPoints.Add(new CheckpointState(state0, timeBase, cvars, 0, detached));
|
||||
|
||||
DebugTools.Assert(state0.EntityDeletions.Value.Count == 0);
|
||||
var empty = Array.Empty<EntityUid>();
|
||||
var empty = Array.Empty<NetEntity>();
|
||||
|
||||
TimeSpan GetTime(GameTick tick)
|
||||
{
|
||||
@@ -176,8 +176,8 @@ public sealed partial class ReplayLoadManager
|
||||
|
||||
private void ProcessQueue(
|
||||
GameTick curTick,
|
||||
Dictionary<GameTick, List<EntityUid>> detachQueue,
|
||||
HashSet<EntityUid> detached)
|
||||
Dictionary<GameTick, List<NetEntity>> detachQueue,
|
||||
HashSet<NetEntity> detached)
|
||||
{
|
||||
foreach (var (tick, ents) in detachQueue)
|
||||
{
|
||||
@@ -192,7 +192,7 @@ public sealed partial class ReplayLoadManager
|
||||
HashSet<ResPath> uploadedFiles,
|
||||
Dictionary<Type, HashSet<string>> prototypes,
|
||||
Dictionary<string, object> cvars,
|
||||
Dictionary<GameTick, List<EntityUid>> detachQueue,
|
||||
Dictionary<GameTick, List<NetEntity>> detachQueue,
|
||||
ref (TimeSpan, GameTick) timeBase,
|
||||
bool ignoreDuplicates = false)
|
||||
{
|
||||
@@ -301,8 +301,8 @@ public sealed partial class ReplayLoadManager
|
||||
_locMan.ReloadLocalizations();
|
||||
}
|
||||
|
||||
private void UpdateDeletions(NetListAsArray<EntityUid> entityDeletions,
|
||||
Dictionary<EntityUid, EntityState> entStates, HashSet<EntityUid> detached)
|
||||
private void UpdateDeletions(NetListAsArray<NetEntity> entityDeletions,
|
||||
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached)
|
||||
{
|
||||
foreach (var ent in entityDeletions.Span)
|
||||
{
|
||||
@@ -311,16 +311,16 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<EntityUid, EntityState> entStates,
|
||||
ref int spawnedTracker, ref int stateTracker, HashSet<EntityUid> detached)
|
||||
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<NetEntity, EntityState> entStates,
|
||||
ref int spawnedTracker, ref int stateTracker, HashSet<NetEntity> detached)
|
||||
{
|
||||
foreach (var entState in span)
|
||||
{
|
||||
detached.Remove(entState.Uid);
|
||||
if (!entStates.TryGetValue(entState.Uid, out var oldEntState))
|
||||
detached.Remove(entState.NetEntity);
|
||||
if (!entStates.TryGetValue(entState.NetEntity, out var oldEntState))
|
||||
{
|
||||
var modifiedState = AddImplicitData(entState);
|
||||
entStates[entState.Uid] = modifiedState;
|
||||
entStates[entState.NetEntity] = modifiedState;
|
||||
spawnedTracker++;
|
||||
|
||||
#if DEBUG
|
||||
@@ -333,11 +333,11 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
stateTracker++;
|
||||
DebugTools.Assert(oldEntState.Uid == entState.Uid);
|
||||
entStates[entState.Uid] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
|
||||
DebugTools.Assert(oldEntState.NetEntity == entState.NetEntity);
|
||||
entStates[entState.NetEntity] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
|
||||
|
||||
#if DEBUG
|
||||
foreach (var state in entStates[entState.Uid].ComponentChanges.Span)
|
||||
foreach (var state in entStates[entState.NetEntity].ComponentChanges.Span)
|
||||
{
|
||||
DebugTools.Assert(state.State is not IComponentDeltaState delta || delta.FullState);
|
||||
}
|
||||
@@ -388,7 +388,7 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
DebugTools.Assert(newState.NetComponents == null || newState.NetComponents.Count == combined.Count);
|
||||
return new EntityState(newState.Uid, combined, newState.EntityLastModified, newState.NetComponents ?? oldNetComps);
|
||||
return new EntityState(newState.NetEntity, combined, newState.EntityLastModified, newState.NetComponents ?? oldNetComps);
|
||||
}
|
||||
|
||||
private void UpdatePlayerStates(ReadOnlySpan<PlayerState> span, Dictionary<NetUserId, PlayerState> playerStates)
|
||||
|
||||
@@ -70,14 +70,14 @@ public sealed partial class ReplayLoadManager
|
||||
{
|
||||
// This shouldn't be possible, yet it has happened?
|
||||
// TODO this should probably also throw an exception.
|
||||
_sawmill.Error($"Encountered blank entity state? Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}. Attempting to continue.");
|
||||
_sawmill.Error($"Encountered blank entity state? Entity: {entState.NetEntity}. Last modified: {entState.EntityLastModified}. Attempting to continue.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!_confMan.GetCVar(CVars.ReplayIgnoreErrors))
|
||||
throw new MissingMetadataException(entState.Uid);
|
||||
throw new MissingMetadataException(entState.NetEntity);
|
||||
|
||||
_sawmill.Error($"Missing metadata component. Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}.");
|
||||
_sawmill.Error($"Missing metadata component. Entity: {entState.NetEntity}. Last modified: {entState.EntityLastModified}.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +68,18 @@ public sealed partial class ReplayLoadManager
|
||||
var metaState = (MetaDataComponentState?)ent.ComponentChanges.Value?
|
||||
.FirstOrDefault(c => c.NetID == _metaId).State;
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(ent.Uid);
|
||||
throw new MissingMetadataException(ent.NetEntity);
|
||||
|
||||
_entMan.CreateEntityUninitialized(metaState.PrototypeId, ent.Uid);
|
||||
entities.Add(ent.Uid);
|
||||
var uid = _entMan.CreateEntityUninitialized(metaState.PrototypeId);
|
||||
entities.Add(uid);
|
||||
var metaComp = _entMan.GetComponent<MetaDataComponent>(uid);
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
// We need to clear this mapping before assigning the real net id.
|
||||
// TODO NetEntity Jank: prevent the client from creating this in the first place.
|
||||
_entMan.ClearNetEntity(metaComp.NetEntity);
|
||||
|
||||
_entMan.SetNetEntity(uid, ent.NetEntity, metaComp);
|
||||
|
||||
if (i++ % 50 == 0)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ public sealed partial class ReplayLoadManager : IReplayLoadManager
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly EntityManager _entMan = default!;
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
[Dependency] private readonly IClientNetManager _netMan = default!;
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
|
||||
@@ -82,19 +82,28 @@ internal sealed partial class ReplayPlaybackManager
|
||||
var metas = _entMan.GetEntityQuery<MetaDataComponent>();
|
||||
foreach (var es in checkpoint.DetachedStates)
|
||||
{
|
||||
if (metas.TryGetComponent(es.Uid, out var meta) && !meta.EntityDeleted)
|
||||
var uid = _entMan.GetEntity(es.NetEntity);
|
||||
if (metas.TryGetComponent(uid, out var meta) && !meta.EntityDeleted)
|
||||
continue;
|
||||
;
|
||||
|
||||
var metaState = (MetaDataComponentState?)es.ComponentChanges.Value?
|
||||
.FirstOrDefault(c => c.NetID == _metaId).State;
|
||||
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(es.Uid);
|
||||
throw new MissingMetadataException(es.NetEntity);
|
||||
|
||||
_entMan.CreateEntityUninitialized(metaState.PrototypeId, es.Uid);
|
||||
meta = metas.GetComponent(es.Uid);
|
||||
_entMan.InitializeEntity(es.Uid, meta);
|
||||
_entMan.StartEntity(es.Uid);
|
||||
_entMan.CreateEntityUninitialized(metaState.PrototypeId, uid);
|
||||
meta = metas.GetComponent(uid);
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
// We need to clear this mapping before assigning the real net id.
|
||||
// TODO NetEntity Jank: prevent the client from creating this in the first place.
|
||||
_entMan.ClearNetEntity(meta.NetEntity);
|
||||
|
||||
_entMan.SetNetEntity(uid, es.NetEntity, meta);
|
||||
|
||||
_entMan.InitializeEntity(uid, meta);
|
||||
_entMan.StartEntity(uid);
|
||||
meta.LastStateApplied = checkpoint.Tick;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ internal sealed partial class ReplayPlaybackManager
|
||||
}
|
||||
|
||||
_timing.CurTick += 1;
|
||||
_entMan.TickUpdate(args.DeltaSeconds, noPredictions: true);
|
||||
_cEntManager.TickUpdate(args.DeltaSeconds, noPredictions: true);
|
||||
|
||||
if (!Playing || AutoPauseCountdown == null)
|
||||
return;
|
||||
@@ -82,7 +82,7 @@ internal sealed partial class ReplayPlaybackManager
|
||||
// Maybe track our own detach queue and use _gameState.DetachImmediate()?
|
||||
// That way we don't have to clone this. Downside would be that all entities will be immediately
|
||||
// detached. I.e., the detach budget cvar will simply be ignored.
|
||||
var clone = new List<EntityUid>(leavePvs.Entities);
|
||||
var clone = new List<NetEntity>(leavePvs.Entities);
|
||||
|
||||
_gameState.QueuePvsDetach(clone, leavePvs.Tick);
|
||||
continue;
|
||||
|
||||
@@ -33,7 +33,8 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] private readonly IGameController _controller = default!;
|
||||
[Dependency] private readonly IClientEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IClientEntityManager _cEntManager = default!;
|
||||
[Dependency] private readonly ClientEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IConfigurationManager _confMan = default!;
|
||||
[Dependency] private readonly NetworkResourceManager _netResMan = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameState = default!;
|
||||
|
||||
@@ -95,12 +95,12 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
{
|
||||
var tick = _timing.LastRealTick;
|
||||
var players = _player.Sessions.Select(GetPlayerState).ToArray();
|
||||
var deletions = Array.Empty<EntityUid>();
|
||||
var deletions = Array.Empty<NetEntity>();
|
||||
|
||||
var fullRep = _state.GetFullRep();
|
||||
var entStates = new EntityState[fullRep.Count];
|
||||
var i = 0;
|
||||
foreach (var (uid, dict) in fullRep)
|
||||
foreach (var (netEntity, dict) in fullRep)
|
||||
{
|
||||
var compData = new ComponentChange[dict.Count];
|
||||
var netComps = new HashSet<ushort>(dict.Keys);
|
||||
@@ -110,7 +110,7 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
compData[j++] = new ComponentChange(id, compState, tick);
|
||||
}
|
||||
|
||||
entStates[i++] = new EntityState(uid, compData, tick, netComps);
|
||||
entStates[i++] = new EntityState(netEntity, compData, tick, netComps);
|
||||
}
|
||||
|
||||
var state = new GameState(
|
||||
@@ -121,16 +121,17 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
players,
|
||||
deletions);
|
||||
|
||||
var detached = new List<EntityUid>();
|
||||
var detached = new List<NetEntity>();
|
||||
var query = _entMan.AllEntityQueryEnumerator<MetaDataComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (uid.IsClientSide())
|
||||
if (_entMan.IsClientSide(uid))
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(fullRep.ContainsKey(uid));
|
||||
var nent = comp.NetEntity;
|
||||
DebugTools.Assert(fullRep.ContainsKey(nent));
|
||||
if ((comp.Flags & MetaDataFlags.Detached) != 0)
|
||||
detached.Add(uid);
|
||||
detached.Add(nent);
|
||||
}
|
||||
|
||||
var detachMsg = detached.Count > 0 ? new ReplayMessage.LeavePvs(detached, tick) : null;
|
||||
@@ -144,7 +145,7 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
UserId = session.UserId,
|
||||
Status = session.Status,
|
||||
Name = session.Name,
|
||||
ControlledEntity = session.AttachedEntity,
|
||||
ControlledEntity = _entMan.GetNetEntity(session.AttachedEntity),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +63,8 @@ public sealed partial class ReplayControlWidget : UIWidget // AKA Tardis - The f
|
||||
base.FrameUpdate(args);
|
||||
if (_playback.Replay is not { } replay)
|
||||
{
|
||||
Visible = false;
|
||||
return;
|
||||
}
|
||||
Visible = true;
|
||||
|
||||
TickSlider.MinValue = 0;
|
||||
TickSlider.MaxValue = (float)replay.ReplayTime[^1].TotalSeconds;
|
||||
|
||||
@@ -715,7 +715,7 @@ namespace Robust.Client.UserInterface
|
||||
maxH = MathHelper.Clamp(maxConstraint, minH, maxH);
|
||||
|
||||
minConstraint = float.IsNaN(setH) ? 0 : setH;
|
||||
minH = MathHelper.Clamp(maxH, minConstraint, minH);
|
||||
minH = MathHelper.Clamp(minConstraint, minH, maxH);
|
||||
|
||||
return new Vector2(
|
||||
Math.Clamp(avail.X, minW, maxW),
|
||||
|
||||
@@ -776,9 +776,11 @@ public sealed class TextEdit : Control
|
||||
{
|
||||
var size = base.ArrangeOverride(finalSize);
|
||||
|
||||
_scrollBar.Page = size.Y * UIScale;
|
||||
var renderBoxSize = _renderBox.Size;
|
||||
|
||||
UpdateLineBreaks((int)(size.X * UIScale));
|
||||
_scrollBar.Page = renderBoxSize.Y * UIScale;
|
||||
|
||||
UpdateLineBreaks((int)(renderBoxSize.X * UIScale));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.UserInterface.CustomControls
|
||||
{
|
||||
public sealed class Tooltip : PanelContainer
|
||||
{
|
||||
private readonly Label _label;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public string? Text
|
||||
{
|
||||
get => _label.Text;
|
||||
set => _label.Text = value;
|
||||
get => _label.GetMessage();
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
_label.SetMessage(string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
_label.SetMessage(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMessage(FormattedMessage message)
|
||||
{
|
||||
_label.SetMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +44,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
|
||||
AddChild(vbox);
|
||||
|
||||
vbox.AddChild(_label = new Label());
|
||||
vbox.AddChild(_label = new RichTextLabel());
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
|
||||
@@ -38,20 +38,22 @@ namespace Robust.Client.UserInterface
|
||||
/// <param name="tooltip">control to position (current size will be used to determine bounds)</param>
|
||||
public static void PositionTooltip(Vector2 screenBounds, Vector2 screenPosition, Control tooltip)
|
||||
{
|
||||
LayoutContainer.SetPosition(tooltip, screenPosition);
|
||||
|
||||
tooltip.Measure(Vector2Helpers.Infinity);
|
||||
var combinedMinSize = tooltip.DesiredSize;
|
||||
var (right, bottom) = tooltip.Position + combinedMinSize;
|
||||
|
||||
LayoutContainer.SetPosition(tooltip, new Vector2(screenPosition.X, screenPosition.Y - combinedMinSize.Y));
|
||||
|
||||
var right = tooltip.Position.X + combinedMinSize.X;
|
||||
var top = tooltip.Position.Y - combinedMinSize.Y;
|
||||
|
||||
if (right > screenBounds.X)
|
||||
{
|
||||
LayoutContainer.SetPosition(tooltip, new(screenPosition.X - combinedMinSize.X, tooltip.Position.Y));
|
||||
}
|
||||
|
||||
if (bottom > screenBounds.Y)
|
||||
if (top < 0f)
|
||||
{
|
||||
LayoutContainer.SetPosition(tooltip, new(tooltip.Position.X, screenPosition.Y - combinedMinSize.Y));
|
||||
LayoutContainer.SetPosition(tooltip, new(tooltip.Position.X, 0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ internal partial class UserInterfaceManager
|
||||
private float? _tooltipDelay;
|
||||
private bool _showingTooltip;
|
||||
private Control? _suppliedTooltip;
|
||||
private const float TooltipDelay = 1;
|
||||
private const float TooltipDelay = 0.25f;
|
||||
|
||||
private WindowRoot? _focusedRoot;
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Robust.Client.Utility
|
||||
|
||||
public static Direction TurnCw(this Direction dir)
|
||||
{
|
||||
return (Direction)(((int)dir - 1) % 8);
|
||||
return (Direction)(((int)dir + 7) % 8);
|
||||
}
|
||||
|
||||
public static Direction TurnCcw(this Direction dir)
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace Robust.Client.ViewVariables
|
||||
{
|
||||
// TODO: more flexibility in allowing custom instances here.
|
||||
ViewVariablesInstance instance;
|
||||
if (obj is EntityUid entity && _entityManager.EntityExists(entity))
|
||||
if (obj is NetEntity netEntity && _entityManager.GetEntity(netEntity).IsValid())
|
||||
{
|
||||
instance = new ViewVariablesInstanceEntity(this, _entityManager, _robustSerializer, Sawmill);
|
||||
}
|
||||
@@ -269,7 +269,7 @@ namespace Robust.Client.ViewVariables
|
||||
var type = Type.GetType(blob.ObjectType);
|
||||
// TODO: more flexibility in allowing custom instances here.
|
||||
ViewVariablesInstance instance;
|
||||
if (type != null && typeof(EntityUid).IsAssignableFrom(type))
|
||||
if (type != null && typeof(NetEntity).IsAssignableFrom(type))
|
||||
{
|
||||
instance = new ViewVariablesInstanceEntity(this, _entityManager, _robustSerializer, Sawmill);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
private const int TabServerComponents = 3;
|
||||
|
||||
private TabContainer _tabs = default!;
|
||||
private EntityUid _entity = default!;
|
||||
private EntityUid _entity;
|
||||
private NetEntity _netEntity = default!;
|
||||
|
||||
private ViewVariablesAddWindow? _addComponentWindow;
|
||||
private bool _addComponentServer;
|
||||
@@ -71,7 +72,8 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
public override void Initialize(DefaultWindow window, object obj)
|
||||
{
|
||||
_entity = (EntityUid) obj;
|
||||
_netEntity = (NetEntity) obj;
|
||||
_entity = _entityManager.GetEntity(_netEntity);
|
||||
|
||||
var scrollContainer = new ScrollContainer();
|
||||
//scrollContainer.SetAnchorPreset(Control.LayoutPreset.Wide, true);
|
||||
@@ -163,7 +165,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
PopulateClientComponents();
|
||||
|
||||
if (!_entity.IsClientSide())
|
||||
if (!_entityManager.IsClientSide(_entity))
|
||||
{
|
||||
_serverVariables = new BoxContainer
|
||||
{
|
||||
@@ -268,12 +270,12 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
ViewVariablesManager.OpenVV(
|
||||
new ViewVariablesComponentSelector(_entity, componentType.FullName));
|
||||
new ViewVariablesComponentSelector(_entityManager.GetNetEntity(_entity), componentType.FullName));
|
||||
};
|
||||
removeButton.OnPressed += _ =>
|
||||
{
|
||||
// We send a command to remove the component.
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"rmcomp {_entity} {componentType.ComponentName}");
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"rmcomp {_netEntity} {componentType.ComponentName}");
|
||||
PopulateServerComponents();
|
||||
};
|
||||
button.AddChild(removeButton);
|
||||
@@ -417,7 +419,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
if (_addComponentServer)
|
||||
{
|
||||
// Attempted to add a component to the server entity... We send a command.
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"addcomp {_entity} {eventArgs.Entry}");
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"addcomp {_netEntity} {eventArgs.Entry}");
|
||||
PopulateServerComponents();
|
||||
_addComponentWindow?.Populate(await GetValidServerComponentsForAdding());
|
||||
return;
|
||||
@@ -504,7 +506,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
try
|
||||
{
|
||||
_entitySession =
|
||||
await ViewVariablesManager.RequestSession(new ViewVariablesEntitySelector(_entity));
|
||||
await ViewVariablesManager.RequestSession(new ViewVariablesEntitySelector(_netEntity));
|
||||
}
|
||||
catch (SessionDenyException e)
|
||||
{
|
||||
|
||||
@@ -63,20 +63,22 @@ namespace Robust.Client.ViewVariables
|
||||
}
|
||||
|
||||
// Entity.
|
||||
if (!EntityUid.TryParse(args[0], out var entity))
|
||||
if (!NetEntity.TryParse(args[0], out var netEntity))
|
||||
{
|
||||
shell.WriteLine("Invalid specifier format.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = _entities.GetEntity(netEntity);
|
||||
|
||||
if (!_entities.EntityExists(entity))
|
||||
{
|
||||
shell.WriteLine("That entity does not exist locally. Attempting to open remote view...");
|
||||
_cvvm.OpenVV(new ViewVariablesEntitySelector(entity));
|
||||
_cvvm.OpenVV(new ViewVariablesEntitySelector(netEntity));
|
||||
return;
|
||||
}
|
||||
|
||||
_cvvm.OpenVV(entity);
|
||||
_cvvm.OpenVV(netEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +84,17 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Exceptions;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
|
||||
#pragma warning disable RA0002 // Robust access analyzer
|
||||
|
||||
{{namespaceString}}
|
||||
|
||||
{{containingTypesStart}}
|
||||
|
||||
[RobustAutoGenerated]
|
||||
{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}>
|
||||
{
|
||||
{{GetConstructor(definition)}}
|
||||
@@ -178,7 +181,7 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
// Implicit constructor
|
||||
#pragma warning disable CS8618
|
||||
public {{definition.Type.Name}}()
|
||||
#pragma warning enable CS8618
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
}
|
||||
""");
|
||||
@@ -355,13 +358,32 @@ if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.
|
||||
var name = field.Symbol.Name;
|
||||
var tempVarName = $"{name}Temp";
|
||||
var nullableValue = isNullableValueType ? ".Value" : string.Empty;
|
||||
var nullNotAllowed = isClass && !isNullable;
|
||||
|
||||
if (field.CustomSerializer is { Serializer: var serializer, Type: var serializerType })
|
||||
{
|
||||
if (isClass || isNullableValueType)
|
||||
if (nullNotAllowed)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} != null)
|
||||
if ({{name}} == null)
|
||||
{
|
||||
throw new NullNotAllowedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{typeName}} {{tempVarName}} = default!;
|
||||
""");
|
||||
|
||||
if (isNullable || isNullableValueType)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
{{tempVarName}} = null!;
|
||||
}
|
||||
else
|
||||
{
|
||||
""");
|
||||
}
|
||||
@@ -385,6 +407,7 @@ if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.
|
||||
CreateCopyCustom(
|
||||
builder,
|
||||
name,
|
||||
tempVarName,
|
||||
nonNullableTypeName,
|
||||
serializerName,
|
||||
nullableValue,
|
||||
@@ -393,10 +416,19 @@ if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.
|
||||
break;
|
||||
}
|
||||
|
||||
if (isClass || isNullableValueType)
|
||||
if (isNullable || isNullableValueType)
|
||||
{
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
structCopier.AppendLine($"{name} = {tempVarName}!,");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"target.{name} = {tempVarName}!;");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -404,25 +436,18 @@ if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.
|
||||
{{typeName}} {{tempVarName}} = default!;
|
||||
""");
|
||||
|
||||
if (isClass)
|
||||
if (nullNotAllowed)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} != null)
|
||||
if ({{name}} == null)
|
||||
{
|
||||
throw new NullNotAllowedException();
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
var instantiator = string.Empty;
|
||||
if (!type.IsAbstract &&
|
||||
HasEmptyPublicConstructor(type) &&
|
||||
(type.IsReferenceType || IsNullableType(type)))
|
||||
{
|
||||
instantiator = $"{tempVarName} = new();";
|
||||
}
|
||||
|
||||
var hasHooks = ImplementsInterface(type, SerializationHooksNamespace) || !type.IsSealed;
|
||||
builder.AppendLine($$"""
|
||||
{{instantiator}}
|
||||
if (!serialization.TryCustomCopy(this.{{name}}, ref {{tempVarName}}, hookCtx, {{hasHooks.ToString().ToLower()}}, context))
|
||||
{
|
||||
""");
|
||||
@@ -434,26 +459,22 @@ if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.
|
||||
else if (IsDataDefinition(type) && !type.IsAbstract &&
|
||||
type is not INamedTypeSymbol { TypeKind: TypeKind.Interface })
|
||||
{
|
||||
var nullability = type.IsValueType ? string.Empty : "?";
|
||||
var orNew = type.IsReferenceType
|
||||
? $" ?? {name}{nullability}.Instantiate()"
|
||||
: string.Empty;
|
||||
var nullable = !type.IsValueType || IsNullableType(type);
|
||||
|
||||
|
||||
builder.AppendLine($"var temp = {name}{orNew};");
|
||||
|
||||
if (nullable)
|
||||
{
|
||||
builder.AppendLine("""
|
||||
if (temp != null)
|
||||
builder.AppendLine($$"""
|
||||
if ({{name}} == null)
|
||||
{
|
||||
{{tempVarName}} = null!;
|
||||
}
|
||||
else
|
||||
{
|
||||
""");
|
||||
}
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{name}}{{nullability}}.Copy(ref temp, serialization, hookCtx, context);
|
||||
{{tempVarName}} = temp;
|
||||
serialization.CopyTo({{name}}, ref {{tempVarName}}, hookCtx, context{{nullableOverride}});
|
||||
""");
|
||||
|
||||
if (nullable)
|
||||
@@ -468,18 +489,13 @@ if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.
|
||||
|
||||
builder.AppendLine("}");
|
||||
|
||||
if (isClass)
|
||||
{
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
|
||||
if (definition.Type.IsValueType)
|
||||
{
|
||||
structCopier.AppendLine($"{name} = {tempVarName},");
|
||||
structCopier.AppendLine($"{name} = {tempVarName}!,");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"target.{name} = {tempVarName};");
|
||||
builder.AppendLine($"target.{name} = {tempVarName}!;");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -510,25 +526,27 @@ if (serialization.TryCustomCopy(this, ref target, hookCtx, {definition.HasHooks.
|
||||
var newTemp = isNullable && isClass ? $"{tempVarName} ??= new();" : string.Empty;
|
||||
var nullableOverride = isClass ? ", true" : string.Empty;
|
||||
var nullableValue = isNullableValueType ? ".Value" : string.Empty;
|
||||
var nonNullableTypeName = typeName.EndsWith("?") ? typeName.Substring(0, typeName.Length - 1) : typeName;
|
||||
|
||||
builder.AppendLine($$"""
|
||||
{{typeName}} {{tempVarName}} = default!;
|
||||
{{nonNullableTypeName}} {{tempVarName}}CopyTo = default!;
|
||||
{{newTemp}}
|
||||
serialization.CopyTo<{{typeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, ref {{tempVarName}}, hookCtx, context{{nullableOverride}});
|
||||
target.{{varName}} = {{tempVarName}};
|
||||
serialization.CopyTo<{{typeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, ref {{tempVarName}}CopyTo, hookCtx, context{{nullableOverride}});
|
||||
{{tempVarName}} = {{tempVarName}}CopyTo;
|
||||
""");
|
||||
}
|
||||
|
||||
private static void CreateCopyCustom(
|
||||
StringBuilder builder,
|
||||
string varName,
|
||||
string tempVarName,
|
||||
string nonNullableTypeName,
|
||||
string serializerName,
|
||||
string nullableValue,
|
||||
string nullableOverride)
|
||||
{
|
||||
builder.AppendLine($$"""
|
||||
target.{{varName}} = serialization.CreateCopy<{{nonNullableTypeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, hookCtx, context{{nullableOverride}});
|
||||
{{tempVarName}} = serialization.CreateCopy<{{nonNullableTypeName}}, {{serializerName}}>(this.{{varName}}{{nullableValue}}, hookCtx, context{{nullableOverride}});
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.4.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -286,23 +286,6 @@ internal static class Types
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool HasEmptyPublicConstructor(ITypeSymbol type)
|
||||
{
|
||||
if (type is not INamedTypeSymbol named)
|
||||
return false;
|
||||
|
||||
foreach (var constructor in named.InstanceConstructors)
|
||||
{
|
||||
if (constructor.DeclaredAccessibility == Accessibility.Public &&
|
||||
constructor.Parameters.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsVirtualClass(ITypeSymbol type)
|
||||
{
|
||||
return type.IsReferenceType && !type.IsSealed && type.TypeKind != TypeKind.Interface;
|
||||
|
||||
@@ -369,7 +369,6 @@ namespace Robust.Server
|
||||
// otherwise the prototypes will be cleared
|
||||
_prototype.Initialize();
|
||||
_prototype.LoadDefaultPrototypes();
|
||||
_prototype.ResolveResults();
|
||||
_refMan.Initialize();
|
||||
|
||||
IoCManager.Resolve<ToolshedManager>().Initialize();
|
||||
@@ -389,6 +388,11 @@ namespace Robust.Server
|
||||
_protoLoadMan.Initialize();
|
||||
_netResMan.Initialize();
|
||||
|
||||
// String serializer has to be locked before PostInit as content can depend on it (e.g., replays that start
|
||||
// automatically recording on startup).
|
||||
AddFinalStringsToSerializer();
|
||||
_stringSerializer.LockStrings();
|
||||
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);
|
||||
|
||||
_statusHost.Start();
|
||||
@@ -398,9 +402,6 @@ namespace Robust.Server
|
||||
|
||||
_watchdogApi.Initialize();
|
||||
|
||||
AddFinalStringsToSerializer();
|
||||
_stringSerializer.LockStrings();
|
||||
|
||||
if (OperatingSystem.IsWindows() && _config.GetCVar(CVars.SysWinTickPeriod) >= 0)
|
||||
{
|
||||
WindowsTickPeriod.TimeBeginPeriod((uint) _config.GetCVar(CVars.SysWinTickPeriod));
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
{
|
||||
shell.WriteLine($"{uid} is not a valid entity uid.");
|
||||
shell.WriteLine($"{args[0]} is not a valid entity.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entityManager.EntityExists(uid))
|
||||
if (!_entityManager.TryGetEntity(uidNet, out var uid) || !_entityManager.EntityExists(uid))
|
||||
{
|
||||
shell.WriteLine($"No entity found with id {uid}.");
|
||||
return;
|
||||
@@ -44,17 +44,17 @@ namespace Robust.Server.Console.Commands
|
||||
|
||||
if (_entityManager.HasComponent(uid, registration.Type))
|
||||
{
|
||||
shell.WriteLine($"Entity {_entityManager.GetComponent<MetaDataComponent>(uid).EntityName} already has a {componentName} component.");
|
||||
shell.WriteLine($"Entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName} already has a {componentName} component.");
|
||||
}
|
||||
|
||||
var component = (Component) _componentFactory.GetComponent(registration.Type);
|
||||
|
||||
#pragma warning disable CS0618
|
||||
component.Owner = uid;
|
||||
component.Owner = uid.Value;
|
||||
#pragma warning restore CS0618
|
||||
_entityManager.AddComponent(uid, component);
|
||||
_entityManager.AddComponent(uid.Value, component);
|
||||
|
||||
shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent<MetaDataComponent>(uid).EntityName}.");
|
||||
shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,19 +19,19 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var entity))
|
||||
if (!NetEntity.TryParse(args[0], out var entityNet))
|
||||
{
|
||||
shell.WriteLine("Invalid entity UID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entityManager.EntityExists(entity))
|
||||
if (!_entityManager.TryGetEntity(entityNet, out var entity) || !_entityManager.EntityExists(entity))
|
||||
{
|
||||
shell.WriteLine("That entity does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
_entityManager.DeleteEntity(entity);
|
||||
_entityManager.DeleteEntity(entity.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,14 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
{
|
||||
shell.WriteError("Not a valid entity ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
var uid = _ent.GetEntity(uidNet);
|
||||
|
||||
// no saving default grid
|
||||
if (!_ent.EntityExists(uid))
|
||||
{
|
||||
|
||||
@@ -22,12 +22,14 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var netEntity))
|
||||
{
|
||||
shell.WriteLine($"{uid} is not a valid entity uid.");
|
||||
shell.WriteLine($"{netEntity} is not a valid entity uid.");
|
||||
return;
|
||||
}
|
||||
|
||||
var uid = _entityManager.GetEntity(netEntity);
|
||||
|
||||
if (!_entityManager.EntityExists(uid))
|
||||
{
|
||||
shell.WriteLine($"No entity found with id {uid}.");
|
||||
|
||||
@@ -24,7 +24,7 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var netEntity))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {args[0]}");
|
||||
return;
|
||||
@@ -47,6 +47,7 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
var physics = _entityManager.System<SharedPhysicsSystem>();
|
||||
var appearance = _entityManager.System<AppearanceSystem>();
|
||||
|
||||
var uid = _entityManager.GetEntity(netEntity);
|
||||
_entityManager.EnsureComponent<ScaleVisualsComponent>(uid);
|
||||
var @event = new ScaleEntityEvent();
|
||||
_entityManager.EventBus.RaiseLocalEvent(uid, ref @event);
|
||||
@@ -59,19 +60,20 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
|
||||
if (_entityManager.TryGetComponent(uid, out FixturesComponent? manager))
|
||||
{
|
||||
foreach (var fixture in manager.Fixtures.Values)
|
||||
foreach (var (id, fixture) in manager.Fixtures)
|
||||
{
|
||||
switch (fixture.Shape)
|
||||
{
|
||||
case EdgeShape edge:
|
||||
physics.SetVertices(uid, fixture, edge,
|
||||
physics.SetVertices(uid, id, fixture,
|
||||
edge,
|
||||
edge.Vertex0 * scale,
|
||||
edge.Vertex1 * scale,
|
||||
edge.Vertex2 * scale,
|
||||
edge.Vertex3 * scale, manager);
|
||||
break;
|
||||
case PhysShapeCircle circle:
|
||||
physics.SetPositionRadius(uid, fixture, circle, circle.Position * scale, circle.Radius * scale, manager);
|
||||
physics.SetPositionRadius(uid, id, fixture, circle, circle.Position * scale, circle.Radius * scale, manager);
|
||||
break;
|
||||
case PolygonShape poly:
|
||||
var verts = poly.Vertices;
|
||||
@@ -81,7 +83,7 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
verts[i] *= scale;
|
||||
}
|
||||
|
||||
physics.SetVertices(uid, fixture, poly, verts, manager);
|
||||
physics.SetVertices(uid, id, fixture, poly, verts, manager);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -29,10 +29,12 @@ public sealed class SpinCommand : LocalizedCommands
|
||||
}
|
||||
|
||||
// get the target
|
||||
EntityUid target;
|
||||
EntityUid? target;
|
||||
|
||||
if (args.Length == 3)
|
||||
{
|
||||
if (!EntityUid.TryParse(args[2], out target))
|
||||
if (!NetEntity.TryParse(args[2], out var targetNet) ||
|
||||
!_entities.TryGetEntity(targetNet, out target))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {args[1]}");
|
||||
return;
|
||||
@@ -65,6 +67,6 @@ public sealed class SpinCommand : LocalizedCommands
|
||||
|
||||
var physicsSystem = _entities.System<SharedPhysicsSystem>();
|
||||
physicsSystem.SetAngularDamping(physics, drag);
|
||||
physicsSystem.SetAngularVelocity(target, speed, body: physics);
|
||||
physicsSystem.SetAngularVelocity(target.Value, speed, body: physics);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +137,10 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", horizontal, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix2", vertical, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace Robust.Server.Console.Commands
|
||||
shape = new PolygonShape();
|
||||
shape.SetAsBox(0.5f, 0.5f);
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true), body: box);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box);
|
||||
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
}
|
||||
@@ -184,10 +184,10 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", horizontal, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(20, 0), new Vector2(20, 20));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix2", vertical, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
@@ -213,7 +213,7 @@ namespace Robust.Server.Console.Commands
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
// TODO: Need to detect shape and work out if we need to use fixedrotation
|
||||
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true, 5f));
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f));
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(40, 0), new Vector2(-40, 0));
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", horizontal, 2, 2, true), body: ground);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
|
||||
// Setup boxes
|
||||
@@ -255,7 +255,7 @@ namespace Robust.Server.Console.Commands
|
||||
var box = _ent.AddComponent<PhysicsComponent>(boxUid);
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
|
||||
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true, 5f), body: box);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box);
|
||||
y += deltaY;
|
||||
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
@@ -275,7 +275,7 @@ namespace Robust.Server.Console.Commands
|
||||
var ground = _ent.AddComponent<PhysicsComponent>(groundUid);
|
||||
// Due to lookup changes fixtureless bodies are invalid, so
|
||||
var cShape = new PhysShapeCircle(1f);
|
||||
fixtures.CreateFixture(groundUid, new Fixture("fix1", cShape, 0, 0, false));
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false));
|
||||
|
||||
var bodyUid = _ent.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
|
||||
var body = _ent.AddComponent<PhysicsComponent>(bodyUid);
|
||||
@@ -288,19 +288,19 @@ namespace Robust.Server.Console.Commands
|
||||
// TODO: Box2D just deref, bleh shape structs someday
|
||||
var shape1 = new PolygonShape();
|
||||
shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix1", shape1, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f));
|
||||
|
||||
var shape2 = new PolygonShape();
|
||||
shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix2", shape2, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f));
|
||||
|
||||
var shape3 = new PolygonShape();
|
||||
shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix3", shape3, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f));
|
||||
|
||||
var shape4 = new PolygonShape();
|
||||
shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, new Fixture("fix4", shape4, 2, 0, true, 20f));
|
||||
fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f));
|
||||
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
physics.WakeBody(bodyUid, body: body);
|
||||
@@ -328,7 +328,7 @@ namespace Robust.Server.Console.Commands
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
var shape = new PolygonShape();
|
||||
shape.SetAsBox(0.125f, 0.125f);
|
||||
fixtures.CreateFixture(boxUid, new Fixture("fix1", shape, 2, 2, true, 0.0625f), body: box);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box);
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,19 +29,19 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
{
|
||||
shell.WriteError($"Unable to parse {args[0]} as a {nameof(EntityUid)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entities.EntityExists(uid))
|
||||
if (!_entities.TryGetEntity(uidNet, out var uid) || !_entities.EntityExists(uid))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {uid}");
|
||||
return;
|
||||
}
|
||||
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().AddViewSubscriber(uid, playerSession);
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().AddViewSubscriber(uid.Value, playerSession);
|
||||
}
|
||||
|
||||
public sealed class RemoveViewSubscriberCommand : LocalizedCommands
|
||||
@@ -66,19 +66,19 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
{
|
||||
shell.WriteError($"Unable to parse {args[0]} as a {nameof(EntityUid)}");
|
||||
shell.WriteError($"Unable to parse {args[0]} as a {nameof(NetEntity)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entities.EntityExists(uid))
|
||||
if (!_entities.TryGetEntity(uidNet, out var uid) || !_entities.EntityExists(uid))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {uid}");
|
||||
return;
|
||||
}
|
||||
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().RemoveViewSubscriber(uid, playerSession);
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().RemoveViewSubscriber(uid.Value, playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,8 @@ namespace Robust.Server.Console
|
||||
ctx.WriteLine(err.Describe());
|
||||
}
|
||||
|
||||
shell.WriteLine(_toolshed.PrettyPrintType(res));
|
||||
shell.WriteLine(FormattedMessage.FromMarkupPermissive(_toolshed.PrettyPrintType(res, out var more, moreUsed: true)));
|
||||
ctx.WriteVar("more", more);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -217,8 +218,8 @@ namespace Robust.Server.Console
|
||||
|
||||
if ((result.Options.Length == 0 && result.Hint is null) || message.Args.Length <= 1)
|
||||
{
|
||||
var parser = new ForwardParser(message.ArgString, _toolshed);
|
||||
CommandRun.TryParse(false, true, parser, null, null, false, out _, out var completions, out _);
|
||||
var parser = new ParserContext(message.ArgString, _toolshed);
|
||||
CommandRun.TryParse(true, parser, null, null, false, out _, out var completions, out _);
|
||||
if (completions == null)
|
||||
{
|
||||
goto done;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Server.Containers
|
||||
{
|
||||
public sealed class ContainerSystem : SharedContainerSystem
|
||||
{
|
||||
protected override void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing)
|
||||
protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing)
|
||||
{
|
||||
Log.Error($"Missing entity for container {ToPrettyString(uid)}. Missing uid: {missing}");
|
||||
//cont.InternalRemove(ent);
|
||||
|
||||
@@ -47,10 +47,13 @@ internal sealed partial class MetricsManager
|
||||
// Task.Run this so it gets run on another thread pool thread.
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
MetricsEvents.Log.RequestStart();
|
||||
|
||||
var resp = ctx.Response;
|
||||
var req = ctx.Request;
|
||||
try
|
||||
{
|
||||
MetricsEvents.Log.ScrapeStart();
|
||||
|
||||
var stream = resp.OutputStream;
|
||||
// prometheus-net is a terrible library and have to do all this insanity,
|
||||
@@ -74,6 +77,8 @@ internal sealed partial class MetricsManager
|
||||
}), cancel);
|
||||
|
||||
await stream.DisposeAsync();
|
||||
|
||||
MetricsEvents.Log.ScrapeStop();
|
||||
}
|
||||
catch (ScrapeFailedException e)
|
||||
{
|
||||
@@ -97,6 +102,8 @@ internal sealed partial class MetricsManager
|
||||
finally
|
||||
{
|
||||
resp.Close();
|
||||
|
||||
MetricsEvents.Log.RequestStop();
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -9,6 +10,7 @@ using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using EventSource = System.Diagnostics.Tracing.EventSource;
|
||||
|
||||
#nullable enable
|
||||
|
||||
@@ -168,6 +170,24 @@ internal sealed partial class MetricsManager : IMetricsManager, IDisposable
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
[EventSource(Name = "Robust.MetricsManager")]
|
||||
private sealed class MetricsEvents : EventSource
|
||||
{
|
||||
public static MetricsEvents Log { get; } = new();
|
||||
|
||||
[Event(1)]
|
||||
public void ScrapeStart() => WriteEvent(1);
|
||||
|
||||
[Event(2)]
|
||||
public void ScrapeStop() => WriteEvent(2);
|
||||
|
||||
[Event(3)]
|
||||
public void RequestStart() => WriteEvent(3);
|
||||
|
||||
[Event(4)]
|
||||
public void RequestStop() => WriteEvent(4);
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IMetricsManager
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Server.GameObjects
|
||||
/// </summary>
|
||||
/// <seealso cref="BoundUserInterface"/>
|
||||
[PublicAPI]
|
||||
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
[RegisterComponent]
|
||||
public sealed partial class ServerUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
@@ -33,7 +33,9 @@ namespace Robust.Server.GameObjects
|
||||
[PublicAPI]
|
||||
public sealed class BoundUserInterface
|
||||
{
|
||||
public float InteractionRangeSqrd;
|
||||
public float InteractionRange;
|
||||
|
||||
public float InteractionRangeSqrd => InteractionRange * InteractionRange;
|
||||
|
||||
public Enum UiKey { get; }
|
||||
public EntityUid Owner { get; }
|
||||
@@ -58,8 +60,7 @@ namespace Robust.Server.GameObjects
|
||||
UiKey = data.UiKey;
|
||||
Owner = owner;
|
||||
|
||||
// One Abs(), because negative values imply no limit
|
||||
InteractionRangeSqrd = data.InteractionRange * MathF.Abs(data.InteractionRange);
|
||||
InteractionRange = data.InteractionRange;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,9 +86,9 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
var msg = new PlayAudioEntityMessage
|
||||
{
|
||||
FileName = filename,
|
||||
Coordinates = transform.Coordinates,
|
||||
FallbackCoordinates = fallbackCoordinates,
|
||||
EntityUid = uid,
|
||||
Coordinates = GetNetCoordinates(transform.Coordinates),
|
||||
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates),
|
||||
NetEntity = GetNetEntity(uid),
|
||||
AudioParams = audioParams ?? AudioParams.Default,
|
||||
Identifier = id,
|
||||
};
|
||||
@@ -108,8 +108,8 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
var msg = new PlayAudioPositionalMessage
|
||||
{
|
||||
FileName = filename,
|
||||
Coordinates = coordinates,
|
||||
FallbackCoordinates = fallbackCoordinates,
|
||||
Coordinates = GetNetCoordinates(coordinates),
|
||||
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates),
|
||||
AudioParams = audioParams ?? AudioParams.Default,
|
||||
Identifier = id
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Robust.Server.GameObjects
|
||||
//Client Sanitization: unbound command, just ignore
|
||||
foreach (var handler in BindRegistry.GetHandlers(function))
|
||||
{
|
||||
if (handler.HandleCmdMessage(session, msg)) return;
|
||||
if (handler.HandleCmdMessage(EntityManager, session, msg)) return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
private ISawmill _logWriter = default!;
|
||||
|
||||
private static readonly MapLoadOptions DefaultLoadOptions = new();
|
||||
private const int MapFormatVersion = 5;
|
||||
private const int MapFormatVersion = 6;
|
||||
private const int BackwardsVersion = 2;
|
||||
|
||||
private MapSerializationContext _context = default!;
|
||||
@@ -384,11 +384,11 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
// Load tile mapping so that we can map the stored tile IDs into the ones actually used at runtime.
|
||||
var tileMap = data.RootMappingNode.Get<MappingDataNode>("tilemap");
|
||||
_context.TileMap = new Dictionary<ushort, string>(tileMap.Count);
|
||||
_context.TileMap = new Dictionary<int, string>(tileMap.Count);
|
||||
|
||||
foreach (var (key, value) in tileMap.Children)
|
||||
{
|
||||
var tileId = (ushort) ((ValueDataNode)key).AsInt();
|
||||
var tileId = ((ValueDataNode)key).AsInt();
|
||||
var tileDefName = ((ValueDataNode)value).Value;
|
||||
_context.TileMap.Add(tileId, tileDefName);
|
||||
}
|
||||
@@ -960,7 +960,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
{
|
||||
// Although we could use tiledefmanager it might write tiledata we don't need so we'll compress it
|
||||
var gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
var tileDefs = new HashSet<ushort>();
|
||||
var tileDefs = new HashSet<int>();
|
||||
|
||||
foreach (var ent in entities)
|
||||
{
|
||||
@@ -977,7 +977,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
var tileMap = new MappingDataNode();
|
||||
rootNode.Add("tilemap", tileMap);
|
||||
var ordered = new List<ushort>(tileDefs);
|
||||
var ordered = new List<int>(tileDefs);
|
||||
ordered.Sort();
|
||||
|
||||
foreach (var tyleId in ordered)
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Robust.Server.GameObjects
|
||||
/// </summary>
|
||||
private void OnMessageReceived(BoundUIWrapMessage msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var uid = msg.Entity;
|
||||
var uid = GetEntity(msg.Entity);
|
||||
if (!TryComp(uid, out ServerUserInterfaceComponent? uiComp) || args.SenderSession is not IPlayerSession session)
|
||||
return;
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Robust.Server.GameObjects
|
||||
// get the wrapped message and populate it with the sender & UI key information.
|
||||
var message = msg.Message;
|
||||
message.Session = args.SenderSession;
|
||||
message.Entity = uid;
|
||||
message.Entity = GetNetEntity(uid);
|
||||
message.UiKey = msg.UiKey;
|
||||
|
||||
// Raise as object so the correct type is used.
|
||||
@@ -128,12 +128,13 @@ namespace Robust.Server.GameObjects
|
||||
/// <inheritdoc />
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
foreach (var (activeUis, xform) in EntityQuery<ActiveUserInterfaceComponent, TransformComponent>(true))
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var query = AllEntityQuery<ActiveUserInterfaceComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var activeUis, out var xform))
|
||||
{
|
||||
foreach (var ui in activeUis.Interfaces)
|
||||
{
|
||||
CheckRange(activeUis, ui, xform, query);
|
||||
CheckRange(uid, activeUis, ui, xform, xformQuery);
|
||||
|
||||
if (!ui.StateDirty)
|
||||
continue;
|
||||
@@ -160,9 +161,9 @@ namespace Robust.Server.GameObjects
|
||||
/// <summary>
|
||||
/// Verify that the subscribed clients are still in range of the interface.
|
||||
/// </summary>
|
||||
private void CheckRange(ActiveUserInterfaceComponent activeUis, BoundUserInterface ui, TransformComponent transform, EntityQuery<TransformComponent> query)
|
||||
private void CheckRange(EntityUid uid, ActiveUserInterfaceComponent activeUis, BoundUserInterface ui, TransformComponent transform, EntityQuery<TransformComponent> query)
|
||||
{
|
||||
if (ui.InteractionRangeSqrd <= 0)
|
||||
if (ui.InteractionRange <= 0)
|
||||
return;
|
||||
|
||||
// We have to cache the set of sessions because Unsubscribe modifies the original.
|
||||
@@ -181,6 +182,20 @@ namespace Robust.Server.GameObjects
|
||||
if (_ignoreUIRangeQuery.HasComponent(session.AttachedEntity))
|
||||
continue;
|
||||
|
||||
// Handle pluggable BoundUserInterfaceCheckRangeEvent
|
||||
var checkRangeEvent = new BoundUserInterfaceCheckRangeEvent(uid, ui, session);
|
||||
RaiseLocalEvent(uid, ref checkRangeEvent, broadcast: true);
|
||||
if (checkRangeEvent.Result == BoundUserInterfaceRangeResult.Pass)
|
||||
continue;
|
||||
|
||||
if (checkRangeEvent.Result == BoundUserInterfaceRangeResult.Fail)
|
||||
{
|
||||
CloseUi(ui, session, activeUis);
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(checkRangeEvent.Result == BoundUserInterfaceRangeResult.Default);
|
||||
|
||||
if (uiMap != xform.MapID)
|
||||
{
|
||||
CloseUi(ui, session, activeUis);
|
||||
@@ -306,9 +321,9 @@ namespace Robust.Server.GameObjects
|
||||
/// The player session to send this new state to.
|
||||
/// Set to null for sending it to every subscribed player session.
|
||||
/// </param>
|
||||
public static void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
|
||||
public void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
|
||||
{
|
||||
var msg = new BoundUIWrapMessage(bui.Owner, new UpdateBoundStateMessage(state), bui.UiKey);
|
||||
var msg = new BoundUIWrapMessage(GetNetEntity(bui.Owner), new UpdateBoundStateMessage(state), bui.UiKey);
|
||||
if (session == null)
|
||||
{
|
||||
bui.LastStateMsg = msg;
|
||||
@@ -370,7 +385,7 @@ namespace Robust.Server.GameObjects
|
||||
_openInterfaces.GetOrNew(session).Add(bui);
|
||||
RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session));
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
|
||||
// Fun fact, clients needs to have BUIs open before they can receive the state.....
|
||||
if (bui.LastStateMsg != null)
|
||||
@@ -399,7 +414,7 @@ namespace Robust.Server.GameObjects
|
||||
if (!bui._subscribedSessions.Remove(session))
|
||||
return false;
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
CloseShared(bui, session, activeUis);
|
||||
return true;
|
||||
}
|
||||
@@ -477,7 +492,7 @@ namespace Robust.Server.GameObjects
|
||||
/// </summary>
|
||||
public void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage message)
|
||||
{
|
||||
var msg = new BoundUIWrapMessage(bui.Owner, message, bui.UiKey);
|
||||
var msg = new BoundUIWrapMessage(GetNetEntity(bui.Owner), message, bui.UiKey);
|
||||
foreach (var session in bui.SubscribedSessions)
|
||||
{
|
||||
RaiseNetworkEvent(msg, session.ConnectedClient);
|
||||
@@ -503,10 +518,70 @@ namespace Robust.Server.GameObjects
|
||||
if (!bui.SubscribedSessions.Contains(session))
|
||||
return false;
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, message, bui.UiKey), session.ConnectedClient);
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), message, bui.UiKey), session.ConnectedClient);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised by <see cref="UserInterfaceSystem"/> to check whether an interface is still accessible by its user.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
[PublicAPI]
|
||||
public struct BoundUserInterfaceCheckRangeEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity owning the UI being checked for.
|
||||
/// </summary>
|
||||
public readonly EntityUid Target;
|
||||
|
||||
/// <summary>
|
||||
/// The UI itself.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public readonly BoundUserInterface UserInterface;
|
||||
|
||||
/// <summary>
|
||||
/// The player for which the UI is being checked.
|
||||
/// </summary>
|
||||
public readonly IPlayerSession Player;
|
||||
|
||||
/// <summary>
|
||||
/// The result of the range check.
|
||||
/// </summary>
|
||||
public BoundUserInterfaceRangeResult Result;
|
||||
|
||||
public BoundUserInterfaceCheckRangeEvent(
|
||||
EntityUid target,
|
||||
BoundUserInterface userInterface,
|
||||
IPlayerSession player)
|
||||
{
|
||||
Target = target;
|
||||
UserInterface = userInterface;
|
||||
Player = player;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Possible results for a <see cref="BoundUserInterfaceCheckRangeEvent"/>.
|
||||
/// </summary>
|
||||
public enum BoundUserInterfaceRangeResult : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Run built-in range check.
|
||||
/// </summary>
|
||||
Default,
|
||||
|
||||
/// <summary>
|
||||
/// Range check passed, UI is accessible.
|
||||
/// </summary>
|
||||
Pass,
|
||||
|
||||
/// <summary>
|
||||
/// Range check failed, UI is inaccessible.
|
||||
/// </summary>
|
||||
Fail
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Robust.Server.GameObjects
|
||||
// These methods are used by the map loader to do multi-stage entity construction during map load.
|
||||
// I would recommend you refer to the MapLoader for usage.
|
||||
|
||||
EntityUid AllocEntity(EntityPrototype? prototype, EntityUid uid = default);
|
||||
EntityUid AllocEntity(EntityPrototype? prototype);
|
||||
|
||||
void FinishEntityLoad(EntityUid entity, IEntityLoadContext? context = null);
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private ISawmill _netEntSawmill = default!;
|
||||
|
||||
protected override int NextEntityUid { get; set; } = (int) EntityUid.FirstUid;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_netEntSawmill = LogManager.GetSawmill("net.ent");
|
||||
@@ -54,9 +52,9 @@ namespace Robust.Server.GameObjects
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype, EntityUid uid)
|
||||
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype)
|
||||
{
|
||||
return AllocEntity(prototype, out _, uid);
|
||||
return AllocEntity(prototype, out _);
|
||||
}
|
||||
|
||||
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, IEntityLoadContext? context)
|
||||
@@ -79,15 +77,15 @@ namespace Robust.Server.GameObjects
|
||||
StartEntity(entity);
|
||||
}
|
||||
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, uid, context);
|
||||
return base.CreateEntity(prototypeName, context);
|
||||
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
var entity = base.CreateEntity(prototype, uid, context);
|
||||
var entity = base.CreateEntity(prototype, context);
|
||||
|
||||
// At this point in time, all data configure on the entity *should* be purely from the prototype.
|
||||
// As such, we can reset the modified ticks to Zero,
|
||||
|
||||
@@ -24,6 +24,6 @@ namespace Robust.Server.GameStates
|
||||
|
||||
Action<ICommonSession, GameTick>? ClientAck { get; set; }
|
||||
|
||||
Action<ICommonSession, GameTick, EntityUid?>? ClientRequestFull { get; set; }
|
||||
Action<ICommonSession, GameTick, NetEntity?>? ClientRequestFull { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddGlobalOverride(EntityUid uid, bool removeExistingOverride = true, bool recursive = false)
|
||||
{
|
||||
_pvs.EntityPVSCollection.AddGlobalOverride(uid, removeExistingOverride, recursive);
|
||||
_pvs.EntityPVSCollection.AddGlobalOverride(GetNetEntity(uid), removeExistingOverride, recursive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +28,7 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
|
||||
{
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, session, removeExistingOverride);
|
||||
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), session, removeExistingOverride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,6 +39,6 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
if (!Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, xform.Coordinates, true);
|
||||
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), xform.Coordinates, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ internal sealed partial class PvsSystem
|
||||
return;
|
||||
|
||||
var ackedTick = sessionData.LastReceivedAck;
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? ackedData;
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? ackedData;
|
||||
|
||||
if (sessionData.Overflow != null && sessionData.Overflow.Value.Tick <= ackedTick)
|
||||
{
|
||||
|
||||
@@ -24,14 +24,13 @@ namespace Robust.Server.GameStates;
|
||||
|
||||
internal sealed partial class PvsSystem : EntitySystem
|
||||
{
|
||||
[Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IMapManagerInternal _mapManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerNetConfigurationManager _netConfigManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerGameStateManager _serverGameStateManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IParallelManager _parallelManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerGameStateManager _serverGameStateManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerNetConfigurationManager _netConfigManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public const float ChunkSize = 8;
|
||||
|
||||
@@ -63,31 +62,28 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
internal readonly Dictionary<ICommonSession, SessionPVSData> PlayerData = new();
|
||||
|
||||
private PVSCollection<EntityUid> _entityPvsCollection = default!;
|
||||
public PVSCollection<EntityUid> EntityPVSCollection => _entityPvsCollection;
|
||||
private PVSCollection<NetEntity> _entityPvsCollection = default!;
|
||||
public PVSCollection<NetEntity> EntityPVSCollection => _entityPvsCollection;
|
||||
|
||||
private readonly List<IPVSCollection> _pvsCollections = new();
|
||||
|
||||
private readonly ObjectPool<Dictionary<EntityUid, PvsEntityVisibility>> _visSetPool
|
||||
= new DefaultObjectPool<Dictionary<EntityUid, PvsEntityVisibility>>(
|
||||
new DictPolicy<EntityUid, PvsEntityVisibility>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<Dictionary<NetEntity, PvsEntityVisibility>> _visSetPool
|
||||
= new DefaultObjectPool<Dictionary<NetEntity, PvsEntityVisibility>>(
|
||||
new DictPolicy<NetEntity, PvsEntityVisibility>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<Stack<EntityUid>> _stackPool
|
||||
= new DefaultObjectPool<Stack<EntityUid>>(
|
||||
new StackPolicy<EntityUid>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<Stack<NetEntity>> _stackPool
|
||||
= new DefaultObjectPool<Stack<NetEntity>>(
|
||||
new StackPolicy<NetEntity>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<HashSet<EntityUid>> _uidSetPool
|
||||
= new DefaultObjectPool<HashSet<EntityUid>>(new SetPolicy<EntityUid>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<Dictionary<EntityUid, MetaDataComponent>> _chunkCachePool =
|
||||
new DefaultObjectPool<Dictionary<EntityUid, MetaDataComponent>>(
|
||||
new DictPolicy<EntityUid, MetaDataComponent>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<Dictionary<NetEntity, MetaDataComponent>> _chunkCachePool =
|
||||
new DefaultObjectPool<Dictionary<NetEntity, MetaDataComponent>>(
|
||||
new DictPolicy<NetEntity, MetaDataComponent>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<HashSet<int>> _playerChunkPool =
|
||||
new DefaultObjectPool<HashSet<int>>(new SetPolicy<int>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<RobustTree<EntityUid>> _treePool =
|
||||
new DefaultObjectPool<RobustTree<EntityUid>>(new TreePolicy<EntityUid>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<RobustTree<NetEntity>> _treePool =
|
||||
new DefaultObjectPool<RobustTree<NetEntity>>(new TreePolicy<NetEntity>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<Dictionary<MapChunkLocation, int>> _mapChunkPool =
|
||||
new DefaultObjectPool<Dictionary<MapChunkLocation, int>>(
|
||||
@@ -102,19 +98,25 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
private readonly List<(uint, IChunkIndexLocation)> _chunkList = new(64);
|
||||
internal readonly HashSet<ICommonSession> PendingAcks = new();
|
||||
|
||||
private readonly Dictionary<(uint visMask, IChunkIndexLocation location), (Dictionary<NetEntity, MetaDataComponent> metadata,
|
||||
RobustTree<NetEntity> tree)?> _previousTrees = new();
|
||||
|
||||
private readonly HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
|
||||
|
||||
private EntityQuery<EyeComponent> _eyeQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_eyeQuery = GetEntityQuery<EyeComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
_entityPvsCollection = RegisterPVSCollection<EntityUid>();
|
||||
_entityPvsCollection = RegisterPVSCollection<NetEntity>();
|
||||
|
||||
SubscribeLocalEvent<MapChangedEvent>(ev =>
|
||||
{
|
||||
@@ -159,7 +161,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
|
||||
// TODO rate limit this?
|
||||
private void OnClientRequestFull(ICommonSession session, GameTick tick, EntityUid? missingEntity)
|
||||
private void OnClientRequestFull(ICommonSession session, GameTick tick, NetEntity? missingEntity)
|
||||
{
|
||||
if (!PlayerData.TryGetValue(session, out var sessionData))
|
||||
return;
|
||||
@@ -173,7 +175,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if (missingEntity != null)
|
||||
{
|
||||
sb.Append($" Apparently they received an entity without metadata: {ToPrettyString(missingEntity.Value)}.");
|
||||
var entity = GetEntity(missingEntity)!;
|
||||
sb.Append($" Apparently they received an entity without metadata: {ToPrettyString(entity.Value)}.");
|
||||
|
||||
if (sessionData.LastSeenAt.TryGetValue(missingEntity.Value, out var lastSeenTick))
|
||||
sb.Append($" Entity last sent: {lastSeenTick.Value}");
|
||||
@@ -240,17 +243,17 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
#region PVSCollection Event Updates
|
||||
|
||||
private void OnEntityDeleted(EntityUid e)
|
||||
private void OnEntityDeleted(EntityUid e, MetaDataComponent metadata)
|
||||
{
|
||||
_entityPvsCollection.RemoveIndex(EntityManager.CurrentTick, e);
|
||||
_entityPvsCollection.RemoveIndex(EntityManager.CurrentTick, metadata.NetEntity);
|
||||
|
||||
var previousTick = _gameTiming.CurTick - 1;
|
||||
|
||||
foreach (var sessionData in PlayerData.Values)
|
||||
{
|
||||
sessionData.LastSeenAt.Remove(e);
|
||||
sessionData.LastSeenAt.Remove(metadata.NetEntity);
|
||||
if (sessionData.SentEntities.TryGetValue(previousTick, out var ents))
|
||||
ents.Remove(e);
|
||||
ents.Remove(metadata.NetEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +281,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
DebugTools.Assert(!_mapManager.IsMap(ev.Sender));
|
||||
|
||||
var coordinates = _transform.GetMoverCoordinates(ev.Sender, ev.Component);
|
||||
UpdateEntityRecursive(ev.Sender, ev.Component, coordinates, false, ev.ParentChanged);
|
||||
UpdateEntityRecursive(ev.Sender, _metaQuery.GetComponent(ev.Sender), ev.Component, coordinates, false, ev.ParentChanged);
|
||||
}
|
||||
|
||||
private void OnTransformStartup(EntityUid uid, TransformComponent component, ref TransformStartupEvent args)
|
||||
@@ -295,10 +298,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
DebugTools.Assert(!_mapManager.IsMap(uid));
|
||||
|
||||
var coordinates = _transform.GetMoverCoordinates(uid, component);
|
||||
UpdateEntityRecursive(uid, component, coordinates, false, false);
|
||||
UpdateEntityRecursive(uid, _metaQuery.GetComponent(uid), component, coordinates, false, false);
|
||||
}
|
||||
|
||||
private void UpdateEntityRecursive(EntityUid uid, TransformComponent xform, EntityCoordinates coordinates, bool mover, bool forceDirty)
|
||||
private void UpdateEntityRecursive(EntityUid uid, MetaDataComponent metadata, TransformComponent xform, EntityCoordinates coordinates, bool mover, bool forceDirty)
|
||||
{
|
||||
if (mover && !xform.LocalPosition.Equals(Vector2.Zero))
|
||||
{
|
||||
@@ -310,9 +313,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
var indices = PVSCollection<EntityUid>.GetChunkIndices(coordinates.Position);
|
||||
if (xform.GridUid != null)
|
||||
_entityPvsCollection.UpdateIndex(uid, xform.GridUid.Value, indices, forceDirty: forceDirty);
|
||||
_entityPvsCollection.UpdateIndex(metadata.NetEntity, xform.GridUid.Value, indices, forceDirty: forceDirty);
|
||||
else
|
||||
_entityPvsCollection.UpdateIndex(uid, xform.MapID, indices, forceDirty: forceDirty);
|
||||
_entityPvsCollection.UpdateIndex(metadata.NetEntity, xform.MapID, indices, forceDirty: forceDirty);
|
||||
|
||||
var children = xform.ChildEnumerator;
|
||||
|
||||
@@ -322,7 +325,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
// directly.
|
||||
while (children.MoveNext(out var child))
|
||||
{
|
||||
UpdateEntityRecursive(child.Value, _xformQuery.GetComponent(child.Value), coordinates, true, forceDirty);
|
||||
UpdateEntityRecursive(child.Value, _metaQuery.GetComponent(child.Value), _xformQuery.GetComponent(child.Value), coordinates, true, forceDirty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +389,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
pvsCollection.AddGrid(gridId);
|
||||
}
|
||||
|
||||
_entityPvsCollection.AddGlobalOverride(gridId, true, false);
|
||||
_entityPvsCollection.AddGlobalOverride(_metaQuery.GetComponent(gridId).NetEntity, true, false);
|
||||
}
|
||||
|
||||
private void OnMapDestroyed(MapChangedEvent e)
|
||||
@@ -406,7 +409,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if(e.Map == MapId.Nullspace) return;
|
||||
var uid = _mapManager.GetMapEntityId(e.Map);
|
||||
_entityPvsCollection.AddGlobalOverride(uid, true, false);
|
||||
_entityPvsCollection.AddGlobalOverride(_metaQuery.GetComponent(uid).NetEntity, true, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -533,14 +536,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
return (_chunkList, playerChunks, viewerEntities);
|
||||
}
|
||||
|
||||
private Dictionary<(uint visMask, IChunkIndexLocation location), (Dictionary<EntityUid, MetaDataComponent> metadata,
|
||||
RobustTree<EntityUid> tree)?> _previousTrees = new();
|
||||
|
||||
private HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
|
||||
|
||||
public void RegisterNewPreviousChunkTrees(
|
||||
List<(uint, IChunkIndexLocation)> chunks,
|
||||
(Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] trees,
|
||||
(Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] trees,
|
||||
bool[] reuse)
|
||||
{
|
||||
// For any chunks able to re-used we'll chuck them in a dictionary for faster lookup.
|
||||
@@ -584,9 +582,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
public bool TryCalculateChunk(
|
||||
IChunkIndexLocation chunkLocation,
|
||||
uint visMask,
|
||||
EntityQuery<TransformComponent> transform,
|
||||
EntityQuery<MetaDataComponent> metadata,
|
||||
out (Dictionary<EntityUid, MetaDataComponent> mData, RobustTree<EntityUid> tree)? result)
|
||||
out (Dictionary<NetEntity, MetaDataComponent> mData, RobustTree<NetEntity> tree)? result)
|
||||
{
|
||||
if (!_entityPvsCollection.IsDirty(chunkLocation) && _previousTrees.TryGetValue((visMask, chunkLocation), out var previousTree))
|
||||
{
|
||||
@@ -613,11 +609,12 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
var chunkSet = _chunkCachePool.Get();
|
||||
var tree = _treePool.Get();
|
||||
foreach (var uid in chunk)
|
||||
foreach (var netEntity in chunk)
|
||||
{
|
||||
AddToChunkSetRecursively(in uid, visMask, tree, chunkSet, transform, metadata);
|
||||
var uid = GetEntity(netEntity);
|
||||
AddToChunkSetRecursively(in uid, in netEntity, visMask, tree, chunkSet);
|
||||
#if DEBUG
|
||||
var xform = transform.GetComponent(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
if (chunkLocation is MapChunkLocation)
|
||||
DebugTools.Assert(xform.GridUid == null || xform.GridUid == uid);
|
||||
else if (chunkLocation is GridChunkLocation)
|
||||
@@ -649,51 +646,55 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddToChunkSetRecursively(in EntityUid uid, uint visMask, RobustTree<EntityUid> tree, Dictionary<EntityUid, MetaDataComponent> set, EntityQuery<TransformComponent> transform,
|
||||
EntityQuery<MetaDataComponent> metadata)
|
||||
private bool AddToChunkSetRecursively(in EntityUid uid, in NetEntity netEntity, uint visMask, RobustTree<NetEntity> tree, Dictionary<NetEntity, MetaDataComponent> set)
|
||||
{
|
||||
if (set.ContainsKey(uid))
|
||||
if (set.ContainsKey(netEntity))
|
||||
return true;
|
||||
|
||||
var mComp = metadata.GetComponent(uid);
|
||||
var mComp = _metaQuery.GetComponent(uid);
|
||||
|
||||
// TODO: Don't need to know about parents so no longer need to use bool for this method.
|
||||
// If the eye is missing ANY layer this entity or any of its parents belongs to, it is considered invisible.
|
||||
if ((visMask & mComp.VisibilityMask) != mComp.VisibilityMask)
|
||||
return false;
|
||||
|
||||
var xform = transform.GetComponent(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
|
||||
// is this a map or grid?
|
||||
var isRoot = !xform.ParentUid.IsValid() || uid == xform.GridUid;
|
||||
if (isRoot)
|
||||
{
|
||||
DebugTools.Assert(_mapManager.IsGrid(uid) || _mapManager.IsMap(uid));
|
||||
tree.Set(uid);
|
||||
set.Add(uid, mComp);
|
||||
tree.Set(netEntity);
|
||||
set.Add(netEntity, mComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugTools.Assert(!_mapManager.IsGrid(uid) && !_mapManager.IsMap(uid));
|
||||
|
||||
var parent = xform.ParentUid;
|
||||
if (!set.ContainsKey(parent) && //was the parent not yet added to toSend?
|
||||
!AddToChunkSetRecursively(in parent, visMask, tree, set, transform, metadata)) //did we just fail to add the parent?
|
||||
var parentNetEntity = _metaQuery.GetComponent(parent).NetEntity;
|
||||
|
||||
// TODO performance
|
||||
// AddToChunkSetRecursively will result in a redundant set.ContainsKey() check.
|
||||
// This can probably be avoided somehow
|
||||
if (!set.ContainsKey(parentNetEntity) && //was the parent not yet added to toSend?
|
||||
!AddToChunkSetRecursively(in parent, in parentNetEntity, visMask, tree, set)) //did we just fail to add the parent?
|
||||
{
|
||||
return false; //we failed? suppose we dont get added either
|
||||
}
|
||||
|
||||
//i want it to crash here if it gets added double bc that shouldnt happen and will add alot of unneeded cycles
|
||||
tree.Set(uid, parent);
|
||||
set.Add(uid, mComp);
|
||||
tree.Set(netEntity, parentNetEntity);
|
||||
set.Add(netEntity, mComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal (List<EntityState>? updates, List<EntityUid>? deletions, List<EntityUid>? leftPvs, GameTick fromTick)
|
||||
internal (List<EntityState>? updates, List<NetEntity>? deletions, List<NetEntity>? leftPvs, GameTick fromTick)
|
||||
CalculateEntityStates(IPlayerSession session,
|
||||
GameTick fromTick,
|
||||
GameTick toTick,
|
||||
EntityQuery<MetaDataComponent> mQuery,
|
||||
EntityQuery<TransformComponent> tQuery,
|
||||
(Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] chunks,
|
||||
(Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] chunks,
|
||||
HashSet<int> visibleChunks,
|
||||
EntityUid[] viewers)
|
||||
{
|
||||
@@ -712,7 +713,6 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
throw new Exception("Encountered non-empty object inside of _visSetPool. Was the same object returned to the pool more than once?");
|
||||
|
||||
var deletions = _entityPvsCollection.GetDeletedIndices(fromTick);
|
||||
|
||||
var entStateCount = 0;
|
||||
|
||||
var stack = _stackPool.Get();
|
||||
@@ -726,14 +726,15 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
// Each root nodes should simply be a map or a grid entity.
|
||||
DebugTools.Assert(cache.Value.tree.RootNodes.Count == 1,
|
||||
$"Root node count is {cache.Value.tree.RootNodes.Count} instead of 1. Session: {session}");
|
||||
var ent = cache.Value.tree.RootNodes.FirstOrDefault();
|
||||
var nent = cache.Value.tree.RootNodes.FirstOrDefault();
|
||||
var ent = GetEntity(nent);
|
||||
DebugTools.Assert(Exists(ent), $"Root node does not exist. Node {ent}. Session: {session}");
|
||||
DebugTools.Assert(HasComp<MapComponent>(ent) || HasComp<MapGridComponent>(ent));
|
||||
#endif
|
||||
|
||||
foreach (var rootNode in cache.Value.tree.RootNodes)
|
||||
{
|
||||
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, cache.Value.metadata, stack, in fromTick,
|
||||
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, stack, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
}
|
||||
@@ -742,8 +743,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var globalEnumerator = _entityPvsCollection.GlobalOverridesEnumerator;
|
||||
while (globalEnumerator.MoveNext())
|
||||
{
|
||||
var uid = globalEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
var netEntity = globalEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
globalEnumerator.Dispose();
|
||||
@@ -752,8 +754,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var globalRecursiveEnumerator = _entityPvsCollection.GlobalRecursiveOverridesEnumerator;
|
||||
while (globalRecursiveEnumerator.MoveNext())
|
||||
{
|
||||
var uid = globalRecursiveEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
var netEntity = globalRecursiveEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
globalRecursiveEnumerator.Dispose();
|
||||
@@ -761,15 +764,16 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var localEnumerator = _entityPvsCollection.GetElementsForSession(session);
|
||||
while (localEnumerator.MoveNext())
|
||||
{
|
||||
var uid = localEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
var netEntity = localEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
localEnumerator.Dispose();
|
||||
|
||||
foreach (var viewerEntity in viewers)
|
||||
{
|
||||
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
@@ -777,27 +781,32 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
RaiseLocalEvent(ref expandEvent);
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
|
||||
var entityStates = new List<EntityState>(entStateCount);
|
||||
|
||||
foreach (var (uid, visiblity) in visibleEnts)
|
||||
foreach (var (netEntity, visiblity) in visibleEnts)
|
||||
{
|
||||
var uid = GetEntity(netEntity);
|
||||
|
||||
#if DEBUG
|
||||
// if an entity is visible, its parents should always be visible.
|
||||
DebugTools.Assert((tQuery.GetComponent(uid).ParentUid is not { Valid: true } parent) || visibleEnts.ContainsKey(parent),
|
||||
DebugTools.Assert((_xformQuery.GetComponent(uid).ParentUid is not { Valid: true } parent) ||
|
||||
visibleEnts.ContainsKey(_metaQuery.GetComponent(parent).NetEntity),
|
||||
$"Attempted to send an entity without sending it's parents. Entity: {ToPrettyString(uid)}.");
|
||||
#endif
|
||||
|
||||
if (sessionData.RequestedFull)
|
||||
{
|
||||
entityStates.Add(GetFullEntityState(session, uid, mQuery.GetComponent(uid)));
|
||||
entityStates.Add(GetFullEntityState(session, uid, _metaQuery.GetComponent(uid)));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -805,8 +814,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
var entered = visiblity == PvsEntityVisibility.Entered;
|
||||
var entFromTick = entered ? lastSeen.GetValueOrDefault(uid) : fromTick;
|
||||
var state = GetEntityState(session, uid, entFromTick, mQuery.GetComponent(uid));
|
||||
var entFromTick = entered ? lastSeen.GetValueOrDefault(netEntity) : fromTick;
|
||||
var state = GetEntityState(session, uid, entFromTick, _metaQuery.GetComponent(uid));
|
||||
|
||||
if (entered || !state.Empty)
|
||||
entityStates.Add(state);
|
||||
@@ -850,31 +859,30 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
/// Figure out what entities are no longer visible to the client. These entities are sent reliably to the client
|
||||
/// in a separate net message.
|
||||
/// </summary>
|
||||
private List<EntityUid>? ProcessLeavePVS(
|
||||
Dictionary<EntityUid, PvsEntityVisibility> visibleEnts,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent)
|
||||
private List<NetEntity>? ProcessLeavePVS(
|
||||
Dictionary<NetEntity, PvsEntityVisibility> visibleEnts,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent)
|
||||
{
|
||||
if (lastSent == null)
|
||||
return null;
|
||||
|
||||
var leftView = new List<EntityUid>();
|
||||
foreach (var uid in lastSent.Keys)
|
||||
var leftView = new List<NetEntity>();
|
||||
foreach (var netEntity in lastSent.Keys)
|
||||
{
|
||||
if (!visibleEnts.ContainsKey(uid))
|
||||
leftView.Add(uid);
|
||||
if (!visibleEnts.ContainsKey(netEntity))
|
||||
leftView.Add(netEntity);
|
||||
}
|
||||
|
||||
return leftView.Count > 0 ? leftView : null;
|
||||
}
|
||||
|
||||
private void RecursivelyAddTreeNode(in EntityUid nodeIndex,
|
||||
RobustTree<EntityUid> tree,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
Dictionary<EntityUid, MetaDataComponent> metaDataCache,
|
||||
Stack<EntityUid> stack,
|
||||
private void RecursivelyAddTreeNode(in NetEntity nodeIndex,
|
||||
RobustTree<NetEntity> tree,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
Stack<NetEntity> stack,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -899,7 +907,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (!shouldAdd)
|
||||
continue;
|
||||
|
||||
AddToSendSet(in currentNodeIndex, metaDataCache[currentNodeIndex], toSend, fromTick, in entered, ref entStateCount);
|
||||
var entity = GetEntity(currentNodeIndex);
|
||||
AddToSendSet(in currentNodeIndex, _metaQuery.GetComponent(entity), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
var node = tree[currentNodeIndex];
|
||||
@@ -914,12 +923,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
|
||||
public bool RecursivelyAddOverride(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
in EntityQuery<MetaDataComponent> metaQuery,
|
||||
in EntityQuery<TransformComponent> transQuery,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -933,25 +940,31 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (!uid.IsValid())
|
||||
return false;
|
||||
|
||||
var xform = transQuery.GetComponent(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
var parent = xform.ParentUid;
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in metaQuery, in transQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget))
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget,
|
||||
in enteredEntityBudget))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var metadata = _metaQuery.GetComponent(uid);
|
||||
var netEntity = GetNetEntity(uid, metadata);
|
||||
|
||||
//did we already get added?
|
||||
// Note that we check this AFTER adding parents. This is because while this entity may already have been added
|
||||
// to the toSend set, it doesn't guarantee that its parents have been. E.g., if a player ghost just teleported
|
||||
// to follow a far away entity, the player's own entity is still being sent, but we need to ensure that we also
|
||||
// send the new parents, which may otherwise be delayed because of the PVS budget..
|
||||
if (!toSend.ContainsKey(uid))
|
||||
if (!toSend.ContainsKey(netEntity))
|
||||
{
|
||||
// TODO PERFORMANCE.
|
||||
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
|
||||
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
|
||||
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
|
||||
var (entered, _) = ProcessEntry(in uid, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
AddToSendSet(in uid, metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
var (entered, _) = ProcessEntry(in netEntity, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
AddToSendSet(in netEntity, _metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
if (addChildren)
|
||||
@@ -964,10 +977,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
|
||||
private void RecursivelyAddChildren(TransformComponent xform,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -980,12 +993,15 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (!_xformQuery.TryGetComponent(child, out var childXform))
|
||||
continue;
|
||||
|
||||
if (!toSend.ContainsKey(child))
|
||||
var metadata = _metaQuery.GetComponent(child);
|
||||
var childNetEntity = GetNetEntity(child, metadata);
|
||||
|
||||
if (!toSend.ContainsKey(childNetEntity))
|
||||
{
|
||||
var (entered, _) = ProcessEntry(in child, lastAcked, lastSent, lastSeen, ref newEntityCount,
|
||||
var (entered, _) = ProcessEntry(in childNetEntity, lastAcked, lastSent, lastSeen, ref newEntityCount,
|
||||
ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
|
||||
AddToSendSet(in child, _metaQuery.GetComponent(child), toSend, fromTick, in entered, ref entStateCount);
|
||||
AddToSendSet(in childNetEntity, metadata, toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
@@ -993,19 +1009,20 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(
|
||||
in NetEntity netEntity,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
in int newEntityBudget,
|
||||
in int enteredEntityBudget)
|
||||
{
|
||||
var enteredSinceLastSent = lastSent == null || !lastSent.ContainsKey(uid);
|
||||
var enteredSinceLastSent = lastSent == null || !lastSent.ContainsKey(netEntity);
|
||||
|
||||
var entered = enteredSinceLastSent || // OR, entered since last ack:
|
||||
lastAcked == null || !lastAcked.ContainsKey(uid);
|
||||
lastAcked == null || !lastAcked.ContainsKey(netEntity);
|
||||
|
||||
// If the entity is entering, but we already sent this entering entity in the last message, we won't add it to
|
||||
// the budget. Chances are the packet will arrive in a nice and orderly fashion, and the client will stick to
|
||||
@@ -1020,25 +1037,25 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
return (entered, false);
|
||||
|
||||
enteredEntityCount++;
|
||||
if (!lastSeen.ContainsKey(uid))
|
||||
if (!lastSeen.ContainsKey(netEntity))
|
||||
newEntityCount++;
|
||||
}
|
||||
|
||||
return (entered, true);
|
||||
}
|
||||
|
||||
private void AddToSendSet(in EntityUid uid, MetaDataComponent metaDataComponent, Dictionary<EntityUid, PvsEntityVisibility> toSend, GameTick fromTick, in bool entered, ref int entStateCount)
|
||||
private void AddToSendSet(in NetEntity netEntity, MetaDataComponent metaDataComponent, Dictionary<NetEntity, PvsEntityVisibility> toSend, GameTick fromTick, in bool entered, ref int entStateCount)
|
||||
{
|
||||
if (metaDataComponent.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
var rep = new EntityStringRepresentation(uid, metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
|
||||
var rep = new EntityStringRepresentation(GetEntity(netEntity), metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
|
||||
Log.Error($"Attempted to add a deleted entity to PVS send set: '{rep}'. Trace:\n{Environment.StackTrace}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entered)
|
||||
{
|
||||
toSend.Add(uid, PvsEntityVisibility.Entered);
|
||||
toSend.Add(netEntity, PvsEntityVisibility.Entered);
|
||||
entStateCount++;
|
||||
return;
|
||||
}
|
||||
@@ -1046,22 +1063,22 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (metaDataComponent.EntityLastModifiedTick <= fromTick)
|
||||
{
|
||||
//entity has been sent before and hasnt been updated since
|
||||
toSend.Add(uid, PvsEntityVisibility.StayedUnchanged);
|
||||
toSend.Add(netEntity, PvsEntityVisibility.StayedUnchanged);
|
||||
return;
|
||||
}
|
||||
|
||||
//add us
|
||||
toSend.Add(uid, PvsEntityVisibility.StayedChanged);
|
||||
toSend.Add(netEntity, PvsEntityVisibility.StayedChanged);
|
||||
entStateCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entity states that have been modified after and including the provided tick.
|
||||
/// </summary>
|
||||
public (List<EntityState>?, List<EntityUid>?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
|
||||
public (List<EntityState>?, List<NetEntity>?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
|
||||
{
|
||||
List<EntityState>? stateEntities;
|
||||
var toSend = _uidSetPool.Get();
|
||||
var toSend = new HashSet<EntityUid>();
|
||||
DebugTools.Assert(toSend.Count == 0);
|
||||
bool enumerateAll = false;
|
||||
|
||||
@@ -1096,7 +1113,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if (state.Empty)
|
||||
{
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
|
||||
Tick: {fromTick}--{_gameTiming.CurTick}
|
||||
Entity: {ToPrettyString(uid)}
|
||||
Last modified: {md.EntityLastModifiedTick}
|
||||
@@ -1163,7 +1180,6 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
}
|
||||
|
||||
_uidSetPool.Return(toSend);
|
||||
var deletions = _entityPvsCollection.GetDeletedIndices(fromTick);
|
||||
|
||||
if (stateEntities.Count == 0)
|
||||
@@ -1221,7 +1237,8 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
|
||||
DebugTools.Assert(meta.EntityLastModifiedTick >= meta.LastComponentRemoved);
|
||||
var entState = new EntityState(entityUid, changed, meta.EntityLastModifiedTick, netComps);
|
||||
DebugTools.Assert(GetEntity(meta.NetEntity) == entityUid);
|
||||
var entState = new EntityState(meta.NetEntity, changed, meta.EntityLastModifiedTick, netComps);
|
||||
|
||||
return entState;
|
||||
}
|
||||
@@ -1253,7 +1270,7 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
netComps.Add(netId);
|
||||
}
|
||||
|
||||
var entState = new EntityState(entityUid, changed, meta.EntityLastModifiedTick, netComps);
|
||||
var entState = new EntityState(meta.NetEntity, changed, meta.EntityLastModifiedTick, netComps);
|
||||
|
||||
return entState;
|
||||
}
|
||||
@@ -1263,14 +1280,13 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
if (session.Status != SessionStatus.InGame)
|
||||
return Array.Empty<EntityUid>();
|
||||
|
||||
var viewers = _uidSetPool.Get();
|
||||
var viewers = new HashSet<EntityUid>();
|
||||
|
||||
if (session.AttachedEntity != null)
|
||||
{
|
||||
// Fast path
|
||||
if (session is IPlayerSession { ViewSubscriptionCount: 0 })
|
||||
{
|
||||
_uidSetPool.Return(viewers);
|
||||
return new[] { session.AttachedEntity.Value };
|
||||
}
|
||||
|
||||
@@ -1288,7 +1304,6 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
|
||||
var viewerArray = viewers.ToArray();
|
||||
|
||||
_uidSetPool.Return(viewers);
|
||||
return viewerArray;
|
||||
}
|
||||
|
||||
@@ -1336,23 +1351,23 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
/// <summary>
|
||||
/// All <see cref="EntityUid"/>s that this session saw during the last <see cref="DirtyBufferSize"/> ticks.
|
||||
/// </summary>
|
||||
public readonly OverflowDictionary<GameTick, Dictionary<EntityUid, PvsEntityVisibility>> SentEntities = new(DirtyBufferSize);
|
||||
public readonly OverflowDictionary<GameTick, Dictionary<NetEntity, PvsEntityVisibility>> SentEntities = new(DirtyBufferSize);
|
||||
|
||||
/// <summary>
|
||||
/// The most recently acked entities
|
||||
/// </summary>
|
||||
public (GameTick Tick, Dictionary<EntityUid, PvsEntityVisibility> Data)? LastAcked;
|
||||
public (GameTick Tick, Dictionary<NetEntity, PvsEntityVisibility> Data)? LastAcked;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the last tick at which a given entity was acked by a player. Used to avoid re-sending the whole entity
|
||||
/// state when an item re-enters PVS.
|
||||
/// </summary>
|
||||
public readonly Dictionary<EntityUid, GameTick> LastSeenAt = new();
|
||||
public readonly Dictionary<NetEntity, GameTick> LastSeenAt = new();
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SentEntities"/> overflow in case a player's last ack is more than <see cref="DirtyBufferSize"/> ticks behind the current tick.
|
||||
/// </summary>
|
||||
public (GameTick Tick, Dictionary<EntityUid, PvsEntityVisibility> SentEnts)? Overflow;
|
||||
public (GameTick Tick, Dictionary<NetEntity, PvsEntityVisibility> SentEnts)? Overflow;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the client has explicitly requested a full state. Unlike the first state, we will send them
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
@@ -23,8 +24,8 @@ using SharpZstd.Interop;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Prometheus;
|
||||
using Robust.Server.Replays;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Robust.Server.GameStates
|
||||
{
|
||||
@@ -37,7 +38,7 @@ namespace Robust.Server.GameStates
|
||||
|
||||
private PvsSystem _pvs = default!;
|
||||
|
||||
[Dependency] private readonly IServerEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IServerNetManager _networkManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -62,7 +63,7 @@ namespace Robust.Server.GameStates
|
||||
public ushort TransformNetId { get; set; }
|
||||
|
||||
public Action<ICommonSession, GameTick>? ClientAck { get; set; }
|
||||
public Action<ICommonSession, GameTick, EntityUid?>? ClientRequestFull { get; set; }
|
||||
public Action<ICommonSession, GameTick, NetEntity?>? ClientRequestFull { get; set; }
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
@@ -133,7 +134,7 @@ namespace Robust.Server.GameStates
|
||||
if (!_playerManager.TryGetSessionById(msg.MsgChannel.UserId, out var session))
|
||||
return;
|
||||
|
||||
EntityUid? ent = msg.MissingEntity.IsValid() ? msg.MissingEntity : null;
|
||||
NetEntity? ent = msg.MissingEntity.IsValid() ? msg.MissingEntity : null;
|
||||
ClientRequestFull?.Invoke(session, msg.Tick, ent);
|
||||
}
|
||||
|
||||
@@ -219,10 +220,16 @@ namespace Robust.Server.GameStates
|
||||
{
|
||||
try
|
||||
{
|
||||
var guid = i >= 0 ? players[i].UserId.UserId : default;
|
||||
|
||||
PvsEventSource.Log.WorkStart(_gameTiming.CurTick.Value, i, guid);
|
||||
|
||||
if (i >= 0)
|
||||
SendStateUpdate(i, resource, inputSystem, players[i], pvsData, mQuery, tQuery, ref oldestAckValue);
|
||||
else
|
||||
_replay.Update();
|
||||
|
||||
PvsEventSource.Log.WorkStop(_gameTiming.CurTick.Value, i, guid);
|
||||
}
|
||||
catch (Exception e) // Catch EVERY exception
|
||||
{
|
||||
@@ -238,7 +245,7 @@ namespace Robust.Server.GameStates
|
||||
{
|
||||
public HashSet<int>[] PlayerChunks;
|
||||
public EntityUid[][] ViewerEntities;
|
||||
public (Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] ChunkCache;
|
||||
public (Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] ChunkCache;
|
||||
}
|
||||
|
||||
private PvsData? GetPVSData(IPlayerSession[] players)
|
||||
@@ -248,13 +255,11 @@ namespace Robust.Server.GameStates
|
||||
var chunksCount = chunks.Count;
|
||||
var chunkBatches = (int)MathF.Ceiling((float)chunksCount / ChunkBatchSize);
|
||||
var chunkCache =
|
||||
new (Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[chunksCount];
|
||||
new (Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[chunksCount];
|
||||
|
||||
// Update the reused trees sequentially to avoid having to lock the dictionary per chunk.
|
||||
var reuse = ArrayPool<bool>.Shared.Rent(chunksCount);
|
||||
|
||||
var transformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var metadataQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
Parallel.For(0, chunkBatches,
|
||||
new ParallelOptions { MaxDegreeOfParallelism = _parallelMgr.ParallelProcessCount },
|
||||
i =>
|
||||
@@ -265,8 +270,7 @@ namespace Robust.Server.GameStates
|
||||
for (var j = start; j < end; ++j)
|
||||
{
|
||||
var (visMask, chunkIndexLocation) = chunks[j];
|
||||
reuse[j] = _pvs.TryCalculateChunk(chunkIndexLocation, visMask, transformQuery, metadataQuery,
|
||||
out var chunk);
|
||||
reuse[j] = _pvs.TryCalculateChunk(chunkIndexLocation, visMask, out var chunk);
|
||||
chunkCache[j] = chunk;
|
||||
|
||||
#if DEBUG
|
||||
@@ -276,7 +280,8 @@ namespace Robust.Server.GameStates
|
||||
// Each root nodes should simply be a map or a grid entity.
|
||||
DebugTools.Assert(chunk.Value.tree.RootNodes.Count == 1,
|
||||
$"Root node count is {chunk.Value.tree.RootNodes.Count} instead of 1.");
|
||||
var ent = chunk.Value.tree.RootNodes.FirstOrDefault();
|
||||
var nent = chunk.Value.tree.RootNodes.FirstOrDefault();
|
||||
var ent = _entityManager.GetEntity(nent);
|
||||
DebugTools.Assert(_entityManager.EntityExists(ent), $"Root node does not exist. Node {ent}.");
|
||||
DebugTools.Assert(_entityManager.HasComponent<MapComponent>(ent)
|
||||
|| _entityManager.HasComponent<MapGridComponent>(ent));
|
||||
@@ -306,9 +311,9 @@ namespace Robust.Server.GameStates
|
||||
var channel = session.ConnectedClient;
|
||||
var sessionData = _pvs.PlayerData[session];
|
||||
var lastAck = sessionData.LastReceivedAck;
|
||||
List<EntityUid>? leftPvs = null;
|
||||
List<NetEntity>? leftPvs = null;
|
||||
List<EntityState>? entStates;
|
||||
List<EntityUid>? deletions;
|
||||
List<NetEntity>? deletions;
|
||||
GameTick fromTick;
|
||||
|
||||
DebugTools.Assert(_pvs.CullingEnabled == (pvsData != null));
|
||||
@@ -318,8 +323,6 @@ namespace Robust.Server.GameStates
|
||||
session,
|
||||
lastAck,
|
||||
_gameTiming.CurTick,
|
||||
mQuery,
|
||||
tQuery,
|
||||
pvsData.Value.ChunkCache,
|
||||
pvsData.Value.PlayerChunks[i],
|
||||
pvsData.Value.ViewerEntities[i]);
|
||||
@@ -373,5 +376,35 @@ namespace Robust.Server.GameStates
|
||||
_networkManager.ServerSendMessage(pvsMessage, channel);
|
||||
}
|
||||
}
|
||||
|
||||
[EventSource(Name = "Robust.Pvs")]
|
||||
public sealed class PvsEventSource : System.Diagnostics.Tracing.EventSource
|
||||
{
|
||||
public static PvsEventSource Log { get; } = new();
|
||||
|
||||
[Event(1)]
|
||||
public void WorkStart(uint tick, int playerIdx, Guid playerGuid) => WriteEvent(1, tick, playerIdx, playerGuid);
|
||||
|
||||
[Event(2)]
|
||||
public void WorkStop(uint tick, int playerIdx, Guid playerGuid) => WriteEvent(2, tick, playerIdx, playerGuid);
|
||||
|
||||
[NonEvent]
|
||||
private unsafe void WriteEvent(int eventId, uint arg1, int arg2, Guid arg3)
|
||||
{
|
||||
if (IsEnabled())
|
||||
{
|
||||
var descrs = stackalloc EventData[3];
|
||||
|
||||
descrs[0].DataPointer = (IntPtr)(&arg1);
|
||||
descrs[0].Size = 4;
|
||||
descrs[1].DataPointer = (IntPtr)(&arg2);
|
||||
descrs[1].Size = 4;
|
||||
descrs[2].DataPointer = (IntPtr)(&arg3);
|
||||
descrs[2].Size = 16;
|
||||
|
||||
WriteEventCore(eventId, 3, descrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -48,7 +47,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
|
||||
var chunk = instantiationDelegate != null ? instantiationDelegate() : new MapChunk(ind.X, ind.Y, size);
|
||||
|
||||
IReadOnlyDictionary<ushort, string>? tileMap = null;
|
||||
IReadOnlyDictionary<int, string>? tileMap = null;
|
||||
|
||||
if (context is MapSerializationContext serContext)
|
||||
{
|
||||
@@ -65,11 +64,14 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
|
||||
var tileDefinitionManager = dependencies.Resolve<ITileDefinitionManager>();
|
||||
|
||||
node.TryGetValue(new ValueDataNode("version"), out var versionNode);
|
||||
var version = ((ValueDataNode?) versionNode)?.AsInt() ?? 1;
|
||||
|
||||
for (ushort y = 0; y < chunk.ChunkSize; y++)
|
||||
{
|
||||
for (ushort x = 0; x < chunk.ChunkSize; x++)
|
||||
{
|
||||
var id = reader.ReadUInt16();
|
||||
var id = version < 6 ? reader.ReadUInt16() : reader.ReadInt32();
|
||||
var flags = (TileRenderFlag)reader.ReadByte();
|
||||
var variant = reader.ReadByte();
|
||||
|
||||
@@ -98,6 +100,8 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
var gridNode = new ValueDataNode();
|
||||
root.Add("tiles", gridNode);
|
||||
|
||||
root.Add("version", new ValueDataNode("6"));
|
||||
|
||||
gridNode.Value = SerializeTiles(value);
|
||||
|
||||
return root;
|
||||
@@ -106,7 +110,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
private static string SerializeTiles(MapChunk chunk)
|
||||
{
|
||||
// number of bytes written per tile, because sizeof(Tile) is useless.
|
||||
const int structSize = 4;
|
||||
const int structSize = 6;
|
||||
|
||||
var nTiles = chunk.ChunkSize * chunk.ChunkSize * structSize;
|
||||
var barr = new byte[nTiles];
|
||||
@@ -130,7 +130,7 @@ namespace Robust.Server.Physics
|
||||
|
||||
var msg = new ChunkSplitDebugMessage
|
||||
{
|
||||
Grid = uid,
|
||||
Grid = GetNetEntity(uid),
|
||||
};
|
||||
|
||||
foreach (var (index, group) in _nodes[uid])
|
||||
|
||||
@@ -21,9 +21,9 @@ public sealed class JointSystem : SharedJointSystem
|
||||
|
||||
foreach (var (id, joint) in component.Joints)
|
||||
{
|
||||
states.Add(id, joint.GetState());
|
||||
states.Add(id, joint.GetState(EntityManager));
|
||||
}
|
||||
|
||||
args.State = new JointComponentState(component.Relay, states);
|
||||
args.State = new JointComponentState(GetNetEntity(component.Relay), states);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Robust.Server.Placement
|
||||
var alignRcv = msg.Align;
|
||||
var isTile = msg.IsTile;
|
||||
|
||||
ushort tileType = 0;
|
||||
int tileType = 0;
|
||||
var entityTemplateName = "";
|
||||
|
||||
if (isTile) tileType = msg.TileType;
|
||||
@@ -98,8 +98,10 @@ namespace Robust.Server.Placement
|
||||
return;
|
||||
|
||||
//TODO: Distance check, so you can't place things off of screen.
|
||||
// I don't think that's this manager's biggest problem
|
||||
|
||||
var coordinates = msg.EntityCoordinates;
|
||||
var netCoordinates = msg.NetCoordinates;
|
||||
var coordinates = _entityManager.GetCoordinates(netCoordinates);
|
||||
|
||||
if (!coordinates.IsValid(_entityManager))
|
||||
{
|
||||
@@ -177,7 +179,7 @@ namespace Robust.Server.Placement
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaceNewTile(ushort tileType, EntityCoordinates coordinates, NetUserId placingUserId)
|
||||
private void PlaceNewTile(int tileType, EntityCoordinates coordinates, NetUserId placingUserId)
|
||||
{
|
||||
if (!coordinates.IsValid(_entityManager)) return;
|
||||
|
||||
@@ -211,17 +213,19 @@ namespace Robust.Server.Placement
|
||||
private void HandleEntRemoveReq(MsgPlacement msg)
|
||||
{
|
||||
//TODO: Some form of admin check
|
||||
if (!_entityManager.EntityExists(msg.EntityUid))
|
||||
var entity = _entityManager.GetEntity(msg.EntityUid);
|
||||
|
||||
if (!_entityManager.EntityExists(entity))
|
||||
return;
|
||||
|
||||
var placementEraseEvent = new PlacementEntityEvent(msg.EntityUid, _entityManager.GetComponent<TransformComponent>(msg.EntityUid).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
|
||||
var placementEraseEvent = new PlacementEntityEvent(entity, _entityManager.GetComponent<TransformComponent>(entity).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
|
||||
_entityManager.DeleteEntity(msg.EntityUid);
|
||||
_entityManager.DeleteEntity(entity);
|
||||
}
|
||||
|
||||
private void HandleRectRemoveReq(MsgPlacement msg)
|
||||
{
|
||||
EntityCoordinates start = msg.EntityCoordinates;
|
||||
EntityCoordinates start = _entityManager.GetCoordinates(msg.NetCoordinates);
|
||||
Vector2 rectSize = msg.RectSize;
|
||||
foreach (EntityUid entity in EntitySystem.Get<EntityLookupSystem>().GetEntitiesIntersecting(start.GetMapId(_entityManager),
|
||||
new Box2(start.Position, start.Position + rectSize)))
|
||||
|
||||
@@ -6,6 +6,8 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Server.Player
|
||||
@@ -13,7 +15,7 @@ namespace Robust.Server.Player
|
||||
/// <summary>
|
||||
/// Manages each players session when connected to the server.
|
||||
/// </summary>
|
||||
public interface IPlayerManager : Shared.Players.ISharedPlayerManager
|
||||
public interface IPlayerManager : ISharedPlayerManager
|
||||
{
|
||||
BoundKeyMap KeyMap { get; }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Robust.Server.Player
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Server.Player
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -209,7 +210,7 @@ namespace Robust.Server.Player
|
||||
{
|
||||
PlayerState.Status = Status;
|
||||
PlayerState.Name = Name;
|
||||
PlayerState.ControlledEntity = AttachedEntity;
|
||||
PlayerState.ControlledEntity = IoCManager.Resolve<IEntityManager>().GetNetEntity(AttachedEntity);
|
||||
|
||||
_playerManager.Dirty();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
|
||||
<PackageReference Include="SpaceWizards.HttpListener" Version="0.1.0" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.4" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.4" Condition="'$(UseSystemSqlite)' == 'True'" /> <!-- Cannot be private since Content.Server/Database/ServerDbManager.cs depends on SQLitePCL.raw -->
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.4" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="prometheus-net" Version="4.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" PrivateAssets="compile" />
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -9,6 +5,12 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Robust.Server.ServerHub;
|
||||
|
||||
@@ -21,12 +23,12 @@ internal sealed class HubManager
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private string? _advertiseUrl;
|
||||
private string _masterUrl = "";
|
||||
private IReadOnlyList<string> _hubUrls = Array.Empty<string>();
|
||||
private TimeSpan _nextPing;
|
||||
private TimeSpan _interval;
|
||||
|
||||
private bool _active;
|
||||
private bool _firstAdvertisement = true;
|
||||
private readonly HashSet<string> _hubUrlsAdvertisedTo = new HashSet<string>();
|
||||
private HttpClient? _httpClient;
|
||||
|
||||
public async void Start()
|
||||
@@ -38,7 +40,10 @@ internal sealed class HubManager
|
||||
return;
|
||||
|
||||
_cfg.OnValueChanged(CVars.HubAdvertiseInterval, UpdateInterval, true);
|
||||
_cfg.OnValueChanged(CVars.HubMasterUrl, s => _masterUrl = s, true);
|
||||
_cfg.OnValueChanged(CVars.HubUrls, s => _hubUrls = s.Split(",", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.ToList()
|
||||
, true);
|
||||
|
||||
var url = _cfg.GetCVar(CVars.HubServerUrl);
|
||||
if (string.IsNullOrEmpty(url))
|
||||
@@ -100,32 +105,37 @@ internal sealed class HubManager
|
||||
DebugTools.AssertNotNull(_advertiseUrl);
|
||||
DebugTools.AssertNotNull(_httpClient);
|
||||
|
||||
var apiUrl = $"{_masterUrl}api/servers/advertise";
|
||||
|
||||
try
|
||||
foreach (var hubUrl in _hubUrls)
|
||||
{
|
||||
using var response = await _httpClient!.PostAsJsonAsync(apiUrl, new AdvertiseRequest(_advertiseUrl!));
|
||||
var apiUrl = $"{hubUrl}api/servers/advertise";
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
try
|
||||
{
|
||||
var errorText = await response.Content.ReadAsStringAsync();
|
||||
_sawmill.Log(
|
||||
LogLevel.Error,
|
||||
"Error status while advertising server: [{StatusCode}] {Response}",
|
||||
response.StatusCode,
|
||||
errorText);
|
||||
return;
|
||||
}
|
||||
using var response = await _httpClient!.PostAsJsonAsync(apiUrl, new AdvertiseRequest(_advertiseUrl!));
|
||||
|
||||
if (_firstAdvertisement)
|
||||
{
|
||||
_sawmill.Info("Successfully advertised to hub with address {ServerHubAddress}", _advertiseUrl);
|
||||
_firstAdvertisement = false;
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorText = await response.Content.ReadAsStringAsync();
|
||||
_sawmill.Error("Error status while advertising server: [{StatusCode}] {ErrorText}, to {HubUrl}",
|
||||
response.StatusCode,
|
||||
errorText,
|
||||
hubUrl);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_hubUrlsAdvertisedTo.Contains(hubUrl))
|
||||
{
|
||||
_sawmill.Info("Successfully advertised to {HubUrl} with address {AdvertiseUrl}",
|
||||
hubUrl,
|
||||
_advertiseUrl);
|
||||
_hubUrlsAdvertisedTo.Add(hubUrl);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Log(LogLevel.Error, e, "Exception while trying to advertise server to {HubUrl}",
|
||||
hubUrl);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Log(LogLevel.Error, e, $"Exception while trying to advertise server to hub");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared;
|
||||
@@ -18,6 +7,18 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using HttpListener = SpaceWizards.HttpListener.HttpListener;
|
||||
using HttpListenerContext = SpaceWizards.HttpListener.HttpListenerContext;
|
||||
|
||||
@@ -46,11 +47,11 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
private string? _serverNameCache;
|
||||
private string? _serverDescCache;
|
||||
private string[]? _serverTagsCache;
|
||||
private IReadOnlyList<string> _serverTagsCache = Array.Empty<string>();
|
||||
|
||||
public async Task ProcessRequestAsync(HttpListenerContext context)
|
||||
{
|
||||
var apiContext = (IStatusHandlerContext) new ContextImpl(context);
|
||||
var apiContext = (IStatusHandlerContext)new ContextImpl(context);
|
||||
|
||||
_httpSawmill.Info(
|
||||
$"{apiContext.RequestMethod} {apiContext.Url.PathAndQuery} from {apiContext.RemoteEndPoint}");
|
||||
@@ -110,17 +111,10 @@ namespace Robust.Server.ServerStatus
|
||||
// Writes/reads of references are atomic in C# so no further synchronization necessary.
|
||||
_cfg.OnValueChanged(CVars.GameHostName, n => _serverNameCache = n, true);
|
||||
_cfg.OnValueChanged(CVars.GameDesc, n => _serverDescCache = n, true);
|
||||
_cfg.OnValueChanged(CVars.HubTags, t =>
|
||||
{
|
||||
var tags = t.Split(",", StringSplitOptions.RemoveEmptyEntries);
|
||||
for (var i = 0; i < tags.Length; i++)
|
||||
{
|
||||
tags[i] = tags[i].Trim();
|
||||
}
|
||||
_serverTagsCache = tags;
|
||||
},
|
||||
true
|
||||
);
|
||||
_cfg.OnValueChanged(CVars.HubTags, t => _serverTagsCache = t.Split(",", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.ToList(),
|
||||
true);
|
||||
|
||||
if (!_cfg.GetCVar(CVars.StatusEnabled))
|
||||
{
|
||||
@@ -287,7 +281,7 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
public void Respond(string text, HttpStatusCode code = HttpStatusCode.OK, string contentType = MediaTypeNames.Text.Plain)
|
||||
{
|
||||
Respond(text, (int) code, contentType);
|
||||
Respond(text, (int)code, contentType);
|
||||
}
|
||||
|
||||
public void Respond(string text, int code = 200, string contentType = MediaTypeNames.Text.Plain)
|
||||
@@ -307,7 +301,7 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
public void Respond(byte[] data, HttpStatusCode code = HttpStatusCode.OK, string contentType = MediaTypeNames.Text.Plain)
|
||||
{
|
||||
Respond(data, (int) code, contentType);
|
||||
Respond(data, (int)code, contentType);
|
||||
}
|
||||
|
||||
public void Respond(byte[] data, int code = 200, string contentType = MediaTypeNames.Text.Plain)
|
||||
@@ -330,7 +324,7 @@ namespace Robust.Server.ServerStatus
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.StatusCode = (int) HttpStatusCode.NoContent;
|
||||
_context.Response.StatusCode = (int)HttpStatusCode.NoContent;
|
||||
_context.Response.Close();
|
||||
|
||||
return Task.CompletedTask;
|
||||
@@ -338,7 +332,7 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
public Task RespondAsync(string text, HttpStatusCode code = HttpStatusCode.OK, string contentType = "text/plain")
|
||||
{
|
||||
return RespondAsync(text, (int) code, contentType);
|
||||
return RespondAsync(text, (int)code, contentType);
|
||||
}
|
||||
|
||||
public async Task RespondAsync(string text, int code = 200, string contentType = "text/plain")
|
||||
@@ -358,7 +352,7 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
public Task RespondAsync(byte[] data, HttpStatusCode code = HttpStatusCode.OK, string contentType = "text/plain")
|
||||
{
|
||||
return RespondAsync(data, (int) code, contentType);
|
||||
return RespondAsync(data, (int)code, contentType);
|
||||
}
|
||||
|
||||
public async Task RespondAsync(byte[] data, int code = 200, string contentType = "text/plain")
|
||||
@@ -415,7 +409,7 @@ namespace Robust.Server.ServerStatus
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.StatusCode = (int) code;
|
||||
_context.Response.StatusCode = (int)code;
|
||||
|
||||
return Task.FromResult(_context.Response.OutputStream);
|
||||
}
|
||||
|
||||
@@ -125,8 +125,10 @@ namespace Robust.Server.ViewVariables
|
||||
case ViewVariablesComponentSelector componentSelector:
|
||||
{
|
||||
var compType = _reflectionManager.GetType(componentSelector.ComponentType);
|
||||
var entity = _entityManager.GetEntity(componentSelector.Entity);
|
||||
|
||||
if (compType == null ||
|
||||
!_entityManager.TryGetComponent(componentSelector.Entity, compType, out var component))
|
||||
!_entityManager.TryGetComponent(entity, compType, out var component))
|
||||
{
|
||||
Deny(ViewVariablesResponseCode.NoObject);
|
||||
return;
|
||||
@@ -137,7 +139,9 @@ namespace Robust.Server.ViewVariables
|
||||
}
|
||||
case ViewVariablesEntitySelector entitySelector:
|
||||
{
|
||||
if (!_entityManager.EntityExists(entitySelector.Entity))
|
||||
var entity = _entityManager.GetEntity(entitySelector.Entity);
|
||||
|
||||
if (!_entityManager.EntityExists(entity))
|
||||
{
|
||||
Deny(ViewVariablesResponseCode.NoObject);
|
||||
return;
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace Robust.Server.ViewVariables.Traits
|
||||
|
||||
public ViewVariablesTraitEntity(IViewVariablesSession session) : base(session)
|
||||
{
|
||||
_entity = (EntityUid) Session.Object;
|
||||
var netEntity = (NetEntity) Session.Object;
|
||||
_entity = IoCManager.Resolve<IEntityManager>().GetEntity(netEntity);
|
||||
}
|
||||
|
||||
public override ViewVariablesBlob? DataRequest(ViewVariablesRequest viewVariablesRequest)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -27,10 +28,11 @@ namespace Robust.Server.ViewVariables.Traits
|
||||
if (messageRequestMeta is ViewVariablesRequestMembers)
|
||||
{
|
||||
var members = new List<(MemberData mData, MemberInfo mInfo)>();
|
||||
var obj = Session.Object;
|
||||
var objType = Session.ObjectType;
|
||||
|
||||
foreach (var property in Session.ObjectType.GetAllProperties())
|
||||
foreach (var property in objType.GetAllProperties())
|
||||
{
|
||||
|
||||
if (!ViewVariablesUtility.TryGetViewVariablesAccess(property, out var access))
|
||||
{
|
||||
continue;
|
||||
@@ -53,7 +55,7 @@ namespace Robust.Server.ViewVariables.Traits
|
||||
_members.Add(property);
|
||||
}
|
||||
|
||||
foreach (var field in Session.ObjectType.GetAllFields())
|
||||
foreach (var field in objType.GetAllFields())
|
||||
{
|
||||
if (!ViewVariablesUtility.TryGetViewVariablesAccess(field, out var access))
|
||||
{
|
||||
@@ -66,7 +68,7 @@ namespace Robust.Server.ViewVariables.Traits
|
||||
Name = field.Name,
|
||||
Type = field.FieldType.AssemblyQualifiedName,
|
||||
TypePretty = PrettyPrint.PrintUserFacingTypeShort(field.FieldType, 2),
|
||||
Value = field.GetValue(Session.Object),
|
||||
Value = field.GetValue(obj),
|
||||
PropertyIndex = _members.Count
|
||||
}, field));
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user