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 | |
|---|---|---|---|
|
|
a654a6cf43 | ||
|
|
6211cf2e03 | ||
|
|
a522b4cf86 | ||
|
|
6d33be8c0f | ||
|
|
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 -->
|
||||
|
||||
|
||||
169
RELEASE-NOTES.md
169
RELEASE-NOTES.md
@@ -54,6 +54,175 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 156.0.2
|
||||
|
||||
|
||||
## 156.0.1
|
||||
|
||||
|
||||
## 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;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,6 @@ namespace Robust.Client
|
||||
_reflectionManager.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDefaultPrototypes();
|
||||
_prototypeManager.ResolveResults();
|
||||
_userInterfaceManager.Initialize();
|
||||
_eyeManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedUserInterfaceComponent))]
|
||||
[RegisterComponent]
|
||||
public sealed partial class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -206,8 +206,10 @@ namespace Robust.Client.GameObjects
|
||||
SetEntityContextActive(_inputManager, controlled);
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
protected override void PostInject()
|
||||
{
|
||||
base.PostInject();
|
||||
|
||||
_sawmillInputContext = _logManager.GetSawmill("input.context");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,7 +500,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (netId, comp) in netComps.Value)
|
||||
{
|
||||
DebugTools.AssertNotNull(netId);
|
||||
if (!comp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -59,19 +59,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 +82,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();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
@@ -509,4 +524,64 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!;
|
||||
@@ -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
|
||||
{
|
||||
@@ -373,5 +380,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];
|
||||
@@ -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;
|
||||
@@ -177,7 +177,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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ tags = ""
|
||||
# want to use HTTPS (with a reverse proxy), or other advanced scenarios.
|
||||
# Must be in the form of an ss14:// or ss14s:// URI pointing to the status API.
|
||||
server_url = ""
|
||||
# Comma-separated list of URLs of hub servers to advertise to.
|
||||
hub_urls = "https://central.spacestation14.io/hub/"
|
||||
|
||||
[build]
|
||||
# *Absolutely all of these can be supplied using a "build.json" file*
|
||||
|
||||
@@ -148,6 +148,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
}
|
||||
|
||||
return $@"// <auto-generated />
|
||||
using System;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Analyzers;
|
||||
@@ -157,7 +158,7 @@ namespace {nameSpace};
|
||||
|
||||
public partial class {componentName}
|
||||
{{
|
||||
[Serializable, NetSerializable]
|
||||
[System.Serializable, NetSerializable]
|
||||
public class {stateName} : ComponentState
|
||||
{{{stateFields}
|
||||
}}
|
||||
|
||||
@@ -338,6 +338,16 @@ namespace Robust.Shared.Maths
|
||||
return MathF.Max(MathF.Min(a, b), MathF.Min(MathF.Max(a, b), c));
|
||||
}
|
||||
|
||||
public static TimeSpan Min(TimeSpan a, TimeSpan b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static TimeSpan Max(TimeSpan a, TimeSpan b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
#endregion MinMax
|
||||
|
||||
#region Mod
|
||||
@@ -592,6 +602,11 @@ namespace Robust.Shared.Maths
|
||||
return a + (b - a) * blend;
|
||||
}
|
||||
|
||||
public static TimeSpan Lerp(TimeSpan a, TimeSpan b, double t)
|
||||
{
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
#endregion Lerp
|
||||
|
||||
#region InterpolateCubic
|
||||
|
||||
@@ -10,7 +10,14 @@ namespace Robust.Shared.Maths
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public struct Vector2i : IEquatable<Vector2i>, ISpanFormattable
|
||||
public struct Vector2i :
|
||||
IEquatable<Vector2i>,
|
||||
ISpanFormattable,
|
||||
IAdditionOperators<Vector2i, Vector2i, Vector2i>,
|
||||
ISubtractionOperators<Vector2i, Vector2i, Vector2i>,
|
||||
IMultiplyOperators<Vector2i, Vector2i, Vector2i>,
|
||||
IMultiplyOperators<Vector2i, int, Vector2i>,
|
||||
IComparisonOperators<Vector2i, Vector2i, bool>
|
||||
{
|
||||
public static readonly Vector2i Zero = (0, 0);
|
||||
public static readonly Vector2i One = (1, 1);
|
||||
@@ -213,5 +220,25 @@ namespace Robust.Shared.Maths
|
||||
out charsWritten,
|
||||
$"({X}, {Y})");
|
||||
}
|
||||
|
||||
public static bool operator >(Vector2i left, Vector2i right)
|
||||
{
|
||||
return left.LengthSquared > right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator >=(Vector2i left, Vector2i right)
|
||||
{
|
||||
return left.LengthSquared >= right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator <(Vector2i left, Vector2i right)
|
||||
{
|
||||
return left.LengthSquared < right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator <=(Vector2i left, Vector2i right)
|
||||
{
|
||||
return left.LengthSquared <= right.LengthSquared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,14 @@ namespace Robust.Shared.Maths
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector3 : IEquatable<Vector3>, ISpanFormattable
|
||||
public struct Vector3 :
|
||||
IEquatable<Vector3>,
|
||||
ISpanFormattable,
|
||||
IAdditionOperators<Vector3, Vector3, Vector3>,
|
||||
ISubtractionOperators<Vector3, Vector3, Vector3>,
|
||||
IMultiplyOperators<Vector3, Vector3, Vector3>,
|
||||
IMultiplyOperators<Vector3, float, Vector3>,
|
||||
IComparisonOperators<Vector3, Vector3, bool>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
@@ -1204,5 +1211,25 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static bool operator >(Vector3 left, Vector3 right)
|
||||
{
|
||||
return left.LengthSquared > right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator >=(Vector3 left, Vector3 right)
|
||||
{
|
||||
return left.LengthSquared >= right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator <(Vector3 left, Vector3 right)
|
||||
{
|
||||
return left.LengthSquared < right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator <=(Vector3 left, Vector3 right)
|
||||
{
|
||||
return left.LengthSquared <= right.LengthSquared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Xml.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
@@ -39,7 +40,13 @@ namespace Robust.Shared.Maths
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector4 : IEquatable<Vector4>, ISpanFormattable
|
||||
public struct Vector4 :
|
||||
IEquatable<Vector4>,
|
||||
ISpanFormattable,
|
||||
IAdditionOperators<Vector4, Vector4, Vector4>,
|
||||
ISubtractionOperators<Vector4, Vector4, Vector4>,
|
||||
IMultiplyOperators<Vector4, Vector4, Vector4>,
|
||||
IComparisonOperators<Vector4, Vector4, bool>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
@@ -986,5 +993,30 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
#endregion IEquatable<Vector4> Members
|
||||
|
||||
public static Vector4 operator *(Vector4 left, Vector4 right)
|
||||
{
|
||||
return new(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
|
||||
}
|
||||
|
||||
public static bool operator >(Vector4 left, Vector4 right)
|
||||
{
|
||||
return left.LengthSquared > right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator >=(Vector4 left, Vector4 right)
|
||||
{
|
||||
return left.LengthSquared >= right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator <(Vector4 left, Vector4 right)
|
||||
{
|
||||
return left.LengthSquared < right.LengthSquared;
|
||||
}
|
||||
|
||||
public static bool operator <=(Vector4 left, Vector4 right)
|
||||
{
|
||||
return left.LengthSquared <= right.LengthSquared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ namespace Robust.Shared.Scripting
|
||||
|
||||
[field: Dependency] public ToolshedManager shed { get; } = default!;
|
||||
|
||||
public ToolshedManager Toolshed => shed;
|
||||
public ToolshedEnvironment Environment => shed.DefaultEnvironment;
|
||||
|
||||
protected ScriptGlobalsShared(IDependencyCollection dependencies)
|
||||
{
|
||||
dependencies.InjectDependencies(this);
|
||||
|
||||
3
Robust.Shared/AssemblyInfo.cs
Normal file
3
Robust.Shared/AssemblyInfo.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting", AllInternalsVisible = true)]
|
||||
@@ -1303,10 +1303,10 @@ namespace Robust.Shared
|
||||
CVarDef.Create("hub.tags", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// URL of the master hub server to advertise to.
|
||||
/// Comma-separated list of URLs of hub servers to advertise to.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> HubMasterUrl =
|
||||
CVarDef.Create("hub.master_url", "https://central.spacestation14.io/hub/", CVar.SERVERONLY);
|
||||
public static readonly CVarDef<string> HubUrls =
|
||||
CVarDef.Create("hub.hub_urls", "https://central.spacestation14.io/hub/", CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// URL of this server to advertise.
|
||||
|
||||
@@ -127,6 +127,11 @@ namespace Robust.Shared.Configuration
|
||||
return Parse.Float(input);
|
||||
}
|
||||
|
||||
if (type == typeof(long))
|
||||
{
|
||||
return long.Parse(input);
|
||||
}
|
||||
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Robust.Shared.Configuration
|
||||
using var file = File.OpenRead(configFile);
|
||||
var result = LoadFromTomlStream(file);
|
||||
_configFile = configFile;
|
||||
_sawmill.Info($"Configuration Loaded from '{Path.GetFullPath(configFile)}'");
|
||||
_sawmill.Info($"Configuration loaded from file");
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -612,6 +612,11 @@ namespace Robust.Shared.Configuration
|
||||
return Enum.Parse(type, value);
|
||||
}
|
||||
|
||||
if (type == typeof(long))
|
||||
{
|
||||
return long.Parse(value);
|
||||
}
|
||||
|
||||
// Must be a string.
|
||||
return value;
|
||||
}
|
||||
@@ -633,7 +638,11 @@ namespace Robust.Shared.Configuration
|
||||
return obj.Get<float>();
|
||||
|
||||
case TomlObjectType.Int:
|
||||
return obj.Get<int>();
|
||||
var val = obj.Get<long>();
|
||||
if (val is >= int.MinValue and <= int.MaxValue)
|
||||
return obj.Get<int>();
|
||||
|
||||
return val;
|
||||
|
||||
case TomlObjectType.String:
|
||||
return obj.Get<string>();
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.Console
|
||||
/// Basic interface to handle console commands. Any class implementing this will be
|
||||
/// registered with the console system through reflection.
|
||||
/// </summary>
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors), Obsolete("New commands should utilize RtShellCommand.")]
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors), Obsolete("New commands should utilize ToolshedCommand.")]
|
||||
public interface IConsoleCommand
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Robust.Shared.Containers
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <param name="manager">The container manager that this entity is inside of.</param>
|
||||
/// <returns>If a container manager was found.</returns>
|
||||
public static bool TryGetContainerMan(this EntityUid entity, [NotNullWhen(true)] out IContainerManager? manager, IEntityManager? entMan = null)
|
||||
public static bool TryGetContainerMan(this EntityUid entity, [NotNullWhen(true)] out ContainerManagerComponent? manager, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(entMan.EntityExists(entity));
|
||||
@@ -132,9 +132,10 @@ namespace Robust.Shared.Containers
|
||||
/// <see cref="SharedContainerSystem.TryGetManagerComp"/>
|
||||
/// </summary>
|
||||
[Obsolete("Use SharedContainerSystem.TryGetManagerComp() instead")]
|
||||
private static bool TryGetManagerComp(this EntityUid entity, [NotNullWhen(true)] out IContainerManager? manager, IEntityManager? entMan = null)
|
||||
private static bool TryGetManagerComp(this EntityUid entity, [NotNullWhen(true)] out ContainerManagerComponent? manager, IEntityManager? entMan = null)
|
||||
{
|
||||
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
IoCManager.Resolve(ref entMan);
|
||||
return entMan.System<SharedContainerSystem>()
|
||||
.TryGetManagerComp(entity, out manager);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,9 @@ namespace Robust.Shared.Containers
|
||||
/// <summary>
|
||||
/// Holds data about a set of entity containers on this entity.
|
||||
/// </summary>
|
||||
[ComponentReference(typeof(IContainerManager))]
|
||||
[NetworkedComponent]
|
||||
[RegisterComponent, ComponentProtoName("ContainerContainer")]
|
||||
public sealed partial class ContainerManagerComponent : Component, IContainerManager, ISerializationHooks
|
||||
public sealed partial class ContainerManagerComponent : Component, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages containers on an entity.
|
||||
/// </summary>
|
||||
/// <seealso cref="IContainer" />
|
||||
public partial interface IContainerManager : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes a new container of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID for the new container.</param>
|
||||
/// <typeparam name="T">The type of the new container</typeparam>
|
||||
/// <returns>The new container.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if there already is a container with the specified ID</exception>
|
||||
T MakeContainer<T>(string id)
|
||||
where T : IContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove the entity from some container on this entity.
|
||||
/// </summary>
|
||||
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
|
||||
/// destination is not null</param>
|
||||
/// <param name="force">If true, this will not perform can-remove checks.</param>
|
||||
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
|
||||
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
|
||||
/// container, the grid, or the map.</param>
|
||||
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
|
||||
bool Remove(EntityUid toremove,
|
||||
TransformComponent? xform = null,
|
||||
MetaDataComponent? meta = null,
|
||||
bool reparent = true,
|
||||
bool force = false,
|
||||
EntityCoordinates? destination = null,
|
||||
Angle? localRotation = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the container with the specified ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID to look up.</param>
|
||||
/// <returns>The container.</returns>
|
||||
/// <exception cref="KeyNotFoundException">Thrown if the container does not exist.</exception>
|
||||
IContainer GetContainer(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether we have a container with the specified ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The entity ID to check.</param>
|
||||
/// <returns>True if we already have a container, false otherwise.</returns>
|
||||
bool HasContainer(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to retrieve a container with specified ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID to look up.</param>
|
||||
/// <param name="container">The container if it was found, <c>null</c> if not found.</param>
|
||||
/// <returns>True if the container was found, false otherwise.</returns>
|
||||
bool TryGetContainer(string id, [NotNullWhen(true)] out IContainer? container);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to retrieve a container that contains a specific entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is inside the container.</param>
|
||||
/// <param name="container">The container if it was found, <c>null</c> if not found.</param>
|
||||
/// <returns>True if the container was found, false otherwise.</returns>
|
||||
/// <returns>True if the container was found, false otherwise.</returns>
|
||||
bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out IContainer? container);
|
||||
|
||||
bool ContainsEntity(EntityUid entity);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@ namespace Robust.Shared.Containers
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metas;
|
||||
private EntityQuery<TransformComponent> _xforms;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -25,6 +28,9 @@ namespace Robust.Shared.Containers
|
||||
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChanged);
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentStartup>(OnStartupValidation);
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentGetState>(OnContainerGetState);
|
||||
|
||||
_metas = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
_xforms = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
}
|
||||
|
||||
private void OnContainerGetState(EntityUid uid, ContainerManagerComponent component, ref ComponentGetState args)
|
||||
@@ -220,6 +226,62 @@ namespace Robust.Shared.Containers
|
||||
return IsEntityOrParentInContainer(xform.ParentUid, metas: metas, xforms: xforms);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first instance of a component on the recursive parented containers that hold an entity
|
||||
/// </summary>
|
||||
public bool TryFindComponentOnEntityContainerOrParent<T>(
|
||||
EntityUid uid,
|
||||
EntityQuery<T> entityQuery,
|
||||
[NotNullWhen(true)] ref T? foundComponent,
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null) where T : Component
|
||||
{
|
||||
if (!_metas.Resolve(uid, ref meta))
|
||||
return false;
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer)
|
||||
return false;
|
||||
|
||||
if (!_xforms.Resolve(uid, ref xform))
|
||||
return false;
|
||||
|
||||
if (!xform.ParentUid.Valid)
|
||||
return false;
|
||||
|
||||
if (entityQuery.Resolve(xform.ParentUid, ref foundComponent, false))
|
||||
return true;
|
||||
|
||||
return TryFindComponentOnEntityContainerOrParent(xform.ParentUid, entityQuery, ref foundComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all instances of a component on the recursive parented containers that hold an entity
|
||||
/// </summary>
|
||||
public bool TryFindComponentsOnEntityContainerOrParent<T>(
|
||||
EntityUid uid,
|
||||
EntityQuery<T> entityQuery,
|
||||
List<T> foundComponents,
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null) where T : Component
|
||||
{
|
||||
if (!_metas.Resolve(uid, ref meta))
|
||||
return foundComponents.Any();
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer)
|
||||
return foundComponents.Any();
|
||||
|
||||
if (!_xforms.Resolve(uid, ref xform))
|
||||
return foundComponents.Any();
|
||||
|
||||
if (!xform.ParentUid.Valid)
|
||||
return foundComponents.Any();
|
||||
|
||||
if (EntityManager.TryGetComponent(xform.ParentUid, out T? foundComponent))
|
||||
foundComponents.Add(foundComponent);
|
||||
|
||||
return TryFindComponentsOnEntityContainerOrParent(xform.ParentUid, entityQuery, foundComponents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the two entities are not contained, or are contained in the same container.
|
||||
/// </summary>
|
||||
@@ -440,7 +502,7 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool TryGetManagerComp(EntityUid entity, [NotNullWhen(true)] out IContainerManager? manager)
|
||||
internal bool TryGetManagerComp(EntityUid entity, [NotNullWhen(true)] out ContainerManagerComponent? manager)
|
||||
{
|
||||
DebugTools.Assert(Exists(entity));
|
||||
|
||||
|
||||
@@ -71,12 +71,9 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
else
|
||||
{
|
||||
_sawmill.Debug("Robust directory is {0}", ourPath);
|
||||
loadDirs.Add(Path.GetDirectoryName(ourPath)!);
|
||||
}
|
||||
|
||||
_sawmill.Debug(".NET runtime directory is {0}", dotnetDir);
|
||||
|
||||
if (EngineModuleDirectories != null)
|
||||
{
|
||||
foreach (var moduleDir in EngineModuleDirectories)
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
if (directory.Children.TryGetValue(segment, out var child))
|
||||
{
|
||||
if (!(child is DirectoryNode childDir))
|
||||
if (child is not DirectoryNode childDir)
|
||||
{
|
||||
throw new ArgumentException("A file already exists at that location.");
|
||||
}
|
||||
@@ -55,6 +55,7 @@ namespace Robust.Shared.ContentPack
|
||||
var newDir = new DirectoryNode();
|
||||
|
||||
directory.Children.Add(segment, newDir);
|
||||
directory = newDir;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +206,28 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
public void Rename(ResPath oldPath, ResPath newPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (!oldPath.IsRooted)
|
||||
throw new ArgumentException("Path must be rooted", nameof(oldPath));
|
||||
|
||||
if (!newPath.IsRooted)
|
||||
throw new ArgumentException("Path must be rooted", nameof(newPath));
|
||||
|
||||
if (!TryGetNodeAt(oldPath.Directory, out var parent) || parent is not DirectoryNode sourceDir)
|
||||
throw new ArgumentException("Source directory does not exist.");
|
||||
|
||||
if (!TryGetNodeAt(newPath.Directory, out var target) || target is not DirectoryNode targetDir)
|
||||
throw new ArgumentException("Target directory does not exist.");
|
||||
|
||||
var newFile = newPath.Filename;
|
||||
if (targetDir.Children.ContainsKey(newFile))
|
||||
throw new ArgumentException("Target node already exists");
|
||||
|
||||
var oldFile = oldPath.Filename;
|
||||
if (!sourceDir.Children.TryGetValue(oldFile, out var node))
|
||||
throw new ArgumentException("Node does not exist in original directory.");
|
||||
|
||||
sourceDir.Children.Remove(oldFile);
|
||||
targetDir.Children.Add(newFile, node);
|
||||
}
|
||||
|
||||
public void OpenOsWindow(ResPath path)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.Enums
|
||||
public EntityUid MobUid { get; set; }
|
||||
public string? PlacementOption { get; set; }
|
||||
public int Range { get; set; }
|
||||
public ushort TileType { get; set; }
|
||||
public int TileType { get; set; }
|
||||
public int Uses { get; set; } = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[Reflect(false)]
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class Component : IComponent
|
||||
{
|
||||
[DataField("netsync")]
|
||||
@@ -109,7 +110,7 @@ namespace Robust.Shared.GameObjects
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LifeStage = ComponentLifeStage.Stopping;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompShutdownInstance);
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
@@ -257,11 +258,12 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WARNING: Do not subscribe to this unless you know what you are doing!
|
||||
/// The component has been added to the entity. This is the first function
|
||||
/// to be called after the component has been allocated and (optionally) deserialized.
|
||||
/// </summary>
|
||||
[ComponentEvent]
|
||||
public sealed class ComponentAdd : EntityEventArgs { }
|
||||
public readonly record struct ComponentAdd;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when all of the entity's other components have been added and are available,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -8,27 +9,6 @@ namespace Robust.Shared.GameObjects
|
||||
[PublicAPI]
|
||||
public static class ComponentExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Convenience wrapper to implement "create component if it does not already exist".
|
||||
/// Always gives you back a component, and creates it if it does not exist yet.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to fetch or create the component on.</param>
|
||||
/// <param name="component">The existing component, or the new component if none existed yet.</param>
|
||||
/// <typeparam name="T">The type of the component to fetch or create.</typeparam>
|
||||
/// <returns>True if the component already existed, false if it had to be created.</returns>
|
||||
public static bool EnsureComponent<T>(this EntityUid entity, out T component) where T : Component, new()
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
if (entMan.TryGetComponent<T>(entity, out var comp))
|
||||
{
|
||||
component = comp;
|
||||
return true;
|
||||
}
|
||||
|
||||
component = entMan.AddComponent<T>(entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience wrapper to implement "create component if it does not already exist".
|
||||
/// Always gives you back a component, and creates it if it does not exist yet.
|
||||
@@ -36,6 +16,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="entity">The entity to fetch or create the component on.</param>
|
||||
/// <typeparam name="T">The type of the component to fetch or create.</typeparam>
|
||||
/// <returns>The existing component, or the new component if none existed yet.</returns>
|
||||
[Obsolete]
|
||||
public static T EnsureComponent<T>(this EntityUid entity) where T : Component, new()
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
@@ -47,35 +28,6 @@ namespace Robust.Shared.GameObjects
|
||||
return entMan.AddComponent<T>(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience wrapper to implement "create component if it does not already exist".
|
||||
/// Always gives you back a component, and creates it if it does not exist yet.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to fetch or create the component on.</param>
|
||||
/// <param name="component">The existing component, or the new component if none existed yet.</param>
|
||||
/// <param name="warning">
|
||||
/// The custom warning message to log if the component did not exist already.
|
||||
/// Defaults to a predetermined warning if null.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the component to fetch or create.</typeparam>
|
||||
/// <returns>True if the component already existed, false if it had to be created.</returns>
|
||||
public static bool EnsureComponentWarn<T>(this EntityUid entity, out T component, string? warning = null) where T : Component, new()
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
if (entMan.TryGetComponent<T>(entity, out var comp))
|
||||
{
|
||||
component = comp;
|
||||
return true;
|
||||
}
|
||||
|
||||
warning ??= $"Entity {entity} at {entMan.GetComponent<TransformComponent>(entity).MapPosition} did not have a {typeof(T)}";
|
||||
|
||||
Logger.Warning(warning);
|
||||
|
||||
component = entMan.AddComponent<T>(entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience wrapper to implement "create component if it does not already exist".
|
||||
/// Always gives you back a component, and creates it if it does not exist yet.
|
||||
@@ -87,6 +39,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Defaults to a predetermined warning if null.
|
||||
/// </param>
|
||||
/// <returns>The existing component, or the new component if none existed yet.</returns>
|
||||
[Obsolete]
|
||||
public static T EnsureComponentWarn<T>(this EntityUid entity, string? warning = null) where T : Component, new()
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
@@ -102,6 +55,7 @@ namespace Robust.Shared.GameObjects
|
||||
return entMan.AddComponent<T>(entity);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static IComponent SetAndDirtyIfChanged<TValue>(
|
||||
this IComponent comp,
|
||||
ref TValue backingField,
|
||||
|
||||
@@ -548,6 +548,9 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
while (enumerator.MoveNext(out var component, out var reg))
|
||||
{
|
||||
if (component.Deleted)
|
||||
continue;
|
||||
|
||||
if (reg.ReferenceEvent != dispatchByReference)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
@@ -569,7 +572,11 @@ namespace Robust.Shared.GameObjects
|
||||
if (reg.ReferenceEvent != byRef)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
found.Add(new OrderedEventDispatch((ref Unit ev) => reg.Handler(euid, component, ref ev), reg.Order));
|
||||
found.Add(new OrderedEventDispatch((ref Unit ev) =>
|
||||
{
|
||||
if (!component.Deleted)
|
||||
reg.Handler(euid, component, ref ev);
|
||||
}, reg.Order));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,7 +747,7 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
component = _entityManager.GetComponent(_uid, compType);
|
||||
component = _entityManager.GetComponentInternal(_uid, compType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,9 +55,6 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public event Action<RemovedComponentEventArgs>? ComponentRemoved;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<DeletedComponentEventArgs>? ComponentDeleted;
|
||||
|
||||
public void InitializeComponents()
|
||||
{
|
||||
if (Initialized)
|
||||
@@ -118,7 +115,9 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public void InitializeComponents(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
DebugTools.Assert(metadata == null || metadata.Owner == uid);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
metadata ??= GetComponent<MetaDataComponent>(uid);
|
||||
DebugTools.Assert(metadata.EntityLifeStage == EntityLifeStage.PreInit);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Initializing;
|
||||
@@ -185,7 +184,9 @@ namespace Robust.Shared.GameObjects
|
||||
public Component AddComponent(EntityUid uid, ushort netId)
|
||||
{
|
||||
var newComponent = (Component)_componentFactory.GetComponent(netId);
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
newComponent.Owner = uid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
AddComponent(uid, newComponent);
|
||||
return newComponent;
|
||||
}
|
||||
@@ -193,7 +194,9 @@ namespace Robust.Shared.GameObjects
|
||||
public T AddComponent<T>(EntityUid uid) where T : Component, new()
|
||||
{
|
||||
var newComponent = _componentFactory.GetComponent<T>();
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
newComponent.Owner = uid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
AddComponent(uid, newComponent);
|
||||
return newComponent;
|
||||
}
|
||||
@@ -202,19 +205,21 @@ namespace Robust.Shared.GameObjects
|
||||
where T : Component
|
||||
{
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly EntityUid _owner;
|
||||
public readonly CompIdx CompType;
|
||||
public readonly T Comp;
|
||||
|
||||
public CompInitializeHandle(IEntityManager entityManager, T comp, CompIdx compType)
|
||||
public CompInitializeHandle(IEntityManager entityManager, EntityUid owner, T comp, CompIdx compType)
|
||||
{
|
||||
_entMan = entityManager;
|
||||
_owner = owner;
|
||||
Comp = comp;
|
||||
CompType = compType;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var metadata = _entMan.GetComponent<MetaDataComponent>(Comp.Owner);
|
||||
var metadata = _entMan.GetComponent<MetaDataComponent>(_owner);
|
||||
|
||||
if (!metadata.EntityInitialized && !metadata.EntityInitializing)
|
||||
return;
|
||||
@@ -237,18 +242,16 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var reg = _componentFactory.GetRegistration<T>();
|
||||
var newComponent = (T)_componentFactory.GetComponent(reg);
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
newComponent.Owner = uid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
if (!uid.IsValid() || !EntityExists(uid))
|
||||
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
|
||||
|
||||
if (newComponent == null) throw new ArgumentNullException(nameof(newComponent));
|
||||
|
||||
if (newComponent.Owner != uid) throw new InvalidOperationException("Component is not owned by entity.");
|
||||
|
||||
AddComponentInternal(uid, newComponent, false, true);
|
||||
|
||||
return new CompInitializeHandle<T>(this, newComponent, reg.Idx);
|
||||
return new CompInitializeHandle<T>(this, uid, newComponent, reg.Idx);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -259,7 +262,16 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (component == null) throw new ArgumentNullException(nameof(component));
|
||||
|
||||
if (component.Owner != uid) throw new InvalidOperationException("Component is not owned by entity.");
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (component.Owner == default)
|
||||
{
|
||||
component.Owner = uid;
|
||||
}
|
||||
else if (component.Owner != uid)
|
||||
{
|
||||
throw new InvalidOperationException("Component is not owned by entity.");
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
AddComponentInternal(uid, component, overwrite, false);
|
||||
}
|
||||
@@ -335,6 +347,9 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (metadata.EntityInitialized)
|
||||
component.LifeStartup(this);
|
||||
|
||||
if (metadata.EntityLifeStage >= EntityLifeStage.MapInitialized)
|
||||
EventBus.RaiseComponentEvent(component, MapInitEventInstance);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -606,8 +621,6 @@ namespace Robust.Shared.GameObjects
|
||||
// TODO if terminating the entity, maybe defer this?
|
||||
// _entCompIndex.Remove(uid) gets called later on anyways.
|
||||
_entCompIndex.Remove(entityUid, component);
|
||||
|
||||
ComponentDeleted?.Invoke(new DeletedComponentEventArgs(new ComponentEventArgs(component, entityUid), terminating));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -755,6 +768,18 @@ namespace Robust.Shared.GameObjects
|
||||
return _netComponents[uid][netId];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IComponent GetComponentInternal(EntityUid uid, CompIdx type)
|
||||
{
|
||||
var dict = _entTraitArray[type.Value];
|
||||
if (dict.TryGetValue(uid, out var comp))
|
||||
{
|
||||
return comp;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"Entity {uid} does not have a component of type {type}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetComponent<T>(EntityUid uid, [NotNullWhen(true)] out T? component)
|
||||
@@ -1422,7 +1447,9 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (component != null)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
DebugTools.Assert(uid == component.Owner, "Specified Entity is not the component's Owner!");
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1515,7 +1542,9 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (component != null)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
DebugTools.Assert(uid == component.Owner, "Specified Entity is not the component's Owner!");
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
191
Robust.Shared/GameObjects/EntityManager.Spawn.cs
Normal file
191
Robust.Shared/GameObjects/EntityManager.Spawn.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial class EntityManager
|
||||
{
|
||||
// This method will soon be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> SpawnAttachedTo(protoName, coordinates, overrides);
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, coordinates, overrides);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = SpawnAttachedTo(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = Spawn(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = SpawnAttachedTo(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = Spawn(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
public virtual EntityUid SpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
if (!coordinates.IsValid(this))
|
||||
throw new InvalidOperationException($"Tried to spawn entity {protoName} on invalid coordinates {coordinates}.");
|
||||
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.GetMapId(this));
|
||||
return entity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, MapCoordinates.Nullspace, overrides);
|
||||
|
||||
public virtual EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.MapId);
|
||||
return entity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid SpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, coordinates.ToMap(this, _xforms), overrides);
|
||||
|
||||
public bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
uid = null;
|
||||
if (!_xformQuery.Resolve(target, ref xform))
|
||||
return false;
|
||||
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return false;
|
||||
|
||||
if (!_metaQuery.TryGetComponent(target, out var meta))
|
||||
return false;
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == 0)
|
||||
{
|
||||
uid = SpawnAttachedTo(protoName, xform.Coordinates, overrides);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryGetComponent(xform.ParentUid, out ContainerManagerComponent? containerComp))
|
||||
return false;
|
||||
|
||||
foreach (var container in containerComp.Containers.Values)
|
||||
{
|
||||
if (!container.Contains(target))
|
||||
continue;
|
||||
|
||||
uid = Spawn(protoName, overrides);
|
||||
if (container.Insert(uid.Value, this))
|
||||
return true;
|
||||
|
||||
DeleteEntity(uid.Value);
|
||||
uid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
uid = null;
|
||||
if (containerComp == null && !TryGetComponent(containerUid, out containerComp))
|
||||
return false;
|
||||
|
||||
if (!containerComp.Containers.TryGetValue(containerId, out var container))
|
||||
return false;
|
||||
|
||||
uid = Spawn(protoName, overrides);
|
||||
|
||||
if (container.Insert(uid.Value, this))
|
||||
return true;
|
||||
|
||||
DeleteEntity(uid.Value);
|
||||
uid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public EntityUid SpawnNextToOrDrop(string? protoName, EntityUid target, TransformComponent? xform = null, ComponentRegistry? overrides = null)
|
||||
{
|
||||
xform ??= _xformQuery.GetComponent(target);
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return Spawn(protoName);
|
||||
|
||||
var uid = Spawn(protoName, overrides);
|
||||
_xforms.PlaceNextToOrDrop(uid, target);
|
||||
return uid;
|
||||
}
|
||||
|
||||
public EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
var uid = Spawn(protoName, overrides);
|
||||
|
||||
if ((containerComp == null && !TryGetComponent(containerUid, out containerComp))
|
||||
|| !containerComp.Containers.TryGetValue(containerId, out var container)
|
||||
|| !container.Insert(uid, this))
|
||||
{
|
||||
|
||||
xform ??= _xformQuery.GetComponent(containerUid);
|
||||
if (xform.ParentUid.IsValid())
|
||||
_xforms.PlaceNextToOrDrop(uid, containerUid, targetXform: xform);
|
||||
}
|
||||
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,6 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public event Action<EntityUid>? EntityAdded;
|
||||
public event Action<EntityUid>? EntityInitialized;
|
||||
public event Action<EntityUid>? EntityStarted;
|
||||
public event Action<EntityUid>? EntityDeleted;
|
||||
|
||||
/// <summary>
|
||||
@@ -318,12 +317,7 @@ namespace Robust.Shared.GameObjects
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, default, overrides);
|
||||
|
||||
if (coordinates.IsValid(this))
|
||||
{
|
||||
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
}
|
||||
|
||||
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
@@ -360,89 +354,13 @@ namespace Robust.Shared.GameObjects
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(EntityCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Count];
|
||||
|
||||
for (var i = 0; i < protoNames.Count; i++)
|
||||
{
|
||||
ents[i] = SpawnEntity(protoNames[i], coordinates);
|
||||
}
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
if (!coordinates.IsValid(this))
|
||||
throw new InvalidOperationException($"Tried to spawn entity {protoName} on invalid coordinates {coordinates}.");
|
||||
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.GetMapId(this));
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
InitializeAndStartEntity(entity, coordinates.MapId);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int EntityCount => Entities.Count;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetEntities() => Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Marks this entity as dirty so that it will be updated over the network.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Calling Dirty on a component will call this directly.
|
||||
/// </remarks>
|
||||
/// <inheritdoc />
|
||||
public virtual void DirtyEntity(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
// We want to retrieve MetaDataComponent even if its Deleted flag is set.
|
||||
@@ -459,7 +377,8 @@ namespace Robust.Shared.GameObjects
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
if (metadata.EntityLastModifiedTick == _gameTiming.CurTick) return;
|
||||
if (metadata.EntityLastModifiedTick == _gameTiming.CurTick)
|
||||
return;
|
||||
|
||||
metadata.EntityLastModifiedTick = _gameTiming.CurTick;
|
||||
|
||||
@@ -469,12 +388,14 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("use override with an EntityUid")]
|
||||
public void Dirty(Component component, MetaDataComponent? meta = null)
|
||||
{
|
||||
Dirty(component.Owner, component, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dirty(EntityUid uid, Component component, MetaDataComponent? meta = null)
|
||||
{
|
||||
if (component.LifeStage >= ComponentLifeStage.Removing || !component.NetSyncEnabled)
|
||||
@@ -623,20 +544,6 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
_eventBus.OnEntityDeleted(uid);
|
||||
|
||||
// Another try-catch, so quickly after the other one?!
|
||||
// Yes. Both of these are try-catch blocks for *events*, which take our precious execution flow away from
|
||||
// us and into whatever spooky code subscribed to this. We don't want an exception in user code suddenly
|
||||
// fucking up entity deletion and leaving us with a frankesteintity, now do we?
|
||||
try
|
||||
{
|
||||
EventBus.RaiseEvent(EventSource.Local, new EntityDeletedMessage(uid));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Caught exception while raising {nameof(EntityDeletedMessage)} on '{ToPrettyString(uid, metadata)}'\n{e}");
|
||||
}
|
||||
|
||||
Entities.Remove(uid);
|
||||
}
|
||||
|
||||
@@ -756,7 +663,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
return CreateEntity(prototype, uid, context);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
@@ -815,7 +722,6 @@ namespace Robust.Shared.GameObjects
|
||||
public void StartEntity(EntityUid entity)
|
||||
{
|
||||
StartComponents(entity);
|
||||
EntityStarted?.Invoke(entity);
|
||||
}
|
||||
|
||||
public void RunMapInit(EntityUid entity, MetaDataComponent meta)
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -189,8 +190,11 @@ public partial class EntitySystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks an entity as dirty.
|
||||
/// Marks this entity as dirty so that it will be updated over the network.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Calling Dirty on a component will call this directly.
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void DirtyEntity(EntityUid uid, MetaDataComponent? meta = null)
|
||||
{
|
||||
@@ -201,6 +205,7 @@ public partial class EntitySystem
|
||||
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Obsolete("Use Dirty(EntityUid, Component, MetaDataComponent?")]
|
||||
protected void Dirty(Component component, MetaDataComponent? meta = null)
|
||||
{
|
||||
EntityManager.Dirty(component, meta);
|
||||
@@ -532,6 +537,13 @@ public partial class EntitySystem
|
||||
return EntityManager.AddComponent<T>(uid);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.AddComponent<T>(EntityUid, T, bool)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void AddComp<T>(EntityUid uid, T component, bool overwrite = false) where T : Component
|
||||
{
|
||||
EntityManager.AddComponent(uid, component, overwrite);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.EnsureComponent<T>(EntityUid)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected T EnsureComp<T>(EntityUid uid) where T : Component, new()
|
||||
@@ -641,18 +653,80 @@ public partial class EntitySystem
|
||||
|
||||
#region Entity Spawning
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnEntity(string?, EntityCoordinates, ComponentRegistry?)" />
|
||||
// This method will be obsoleted soon.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype, EntityCoordinates coordinates)
|
||||
{
|
||||
return EntityManager.SpawnEntity(prototype, coordinates);
|
||||
return ((IEntityManager)EntityManager).SpawnEntity(prototype, coordinates);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnEntity(string?, MapCoordinates, ComponentRegistry?)" />
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, MapCoordinates, ComponentRegistry?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype, MapCoordinates coordinates)
|
||||
=> EntityManager.Spawn(prototype, coordinates);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, ComponentRegistry?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype)
|
||||
=> EntityManager.Spawn(prototype);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAttachedTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates)
|
||||
=> EntityManager.SpawnAttachedTo(prototype, coordinates);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAtPosition" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnAtPosition(string? prototype, EntityCoordinates coordinates)
|
||||
=> EntityManager.SpawnAtPosition(prototype, coordinates);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TrySpawnInContainer" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool TrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.SpawnEntity(prototype, coordinates);
|
||||
return EntityManager.TrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TrySpawnNextTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.TrySpawnNextTo(protoName, target, out uid, xform, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnNextToOrDrop" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnNextToOrDrop(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.SpawnNextToOrDrop(protoName, target, xform, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnInContainerOrDrop" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? container = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.SpawnInContainerOrDrop(protoName, containerUid, containerId, xform, container, overrides);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -203,7 +203,10 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
#endregion
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
|
||||
void IPostInjectInit.PostInject() => PostInject();
|
||||
|
||||
protected virtual void PostInject()
|
||||
{
|
||||
Log = LogManager.GetSawmill(SawmillName);
|
||||
|
||||
|
||||
@@ -20,18 +20,21 @@ using Robust.Shared.Exceptions;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public sealed class EntitySystemManager : IEntitySystemManager
|
||||
public sealed class EntitySystemManager : IEntitySystemManager, IPostInjectInit
|
||||
{
|
||||
[IoC.Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[IoC.Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[IoC.Dependency] private readonly ProfManager _profManager = default!;
|
||||
[IoC.Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
|
||||
[IoC.Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
|
||||
private DependencyCollection _systemDependencyCollection = default!;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
internal DependencyCollection SystemDependencyCollection = default!;
|
||||
private readonly List<Type> _systemTypes = new();
|
||||
|
||||
private static readonly Histogram _tickUsageHistogram = Metrics.CreateHistogram("robust_entity_systems_update_usage",
|
||||
@@ -63,12 +66,12 @@ namespace Robust.Shared.GameObjects
|
||||
public T GetEntitySystem<T>()
|
||||
where T : IEntitySystem
|
||||
{
|
||||
return _systemDependencyCollection.Resolve<T>();
|
||||
return SystemDependencyCollection.Resolve<T>();
|
||||
}
|
||||
|
||||
public T? GetEntitySystemOrNull<T>() where T : IEntitySystem
|
||||
{
|
||||
_systemDependencyCollection.TryResolveType<T>(out var system);
|
||||
SystemDependencyCollection.TryResolveType<T>(out var system);
|
||||
return system;
|
||||
}
|
||||
|
||||
@@ -76,7 +79,7 @@ namespace Robust.Shared.GameObjects
|
||||
public void Resolve<T>([NotNull] ref T? instance)
|
||||
where T : IEntitySystem
|
||||
{
|
||||
_systemDependencyCollection.Resolve(ref instance);
|
||||
SystemDependencyCollection.Resolve(ref instance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -84,7 +87,7 @@ namespace Robust.Shared.GameObjects
|
||||
where T1 : IEntitySystem
|
||||
where T2 : IEntitySystem
|
||||
{
|
||||
_systemDependencyCollection.Resolve(ref instance1, ref instance2);
|
||||
SystemDependencyCollection.Resolve(ref instance1, ref instance2);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -93,7 +96,7 @@ namespace Robust.Shared.GameObjects
|
||||
where T2 : IEntitySystem
|
||||
where T3 : IEntitySystem
|
||||
{
|
||||
_systemDependencyCollection.Resolve(ref instance1, ref instance2, ref instance3);
|
||||
SystemDependencyCollection.Resolve(ref instance1, ref instance2, ref instance3);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -103,14 +106,14 @@ namespace Robust.Shared.GameObjects
|
||||
where T3 : IEntitySystem
|
||||
where T4 : IEntitySystem
|
||||
{
|
||||
_systemDependencyCollection.Resolve(ref instance1, ref instance2, ref instance3, ref instance4);
|
||||
SystemDependencyCollection.Resolve(ref instance1, ref instance2, ref instance3, ref instance4);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetEntitySystem<T>([NotNullWhen(true)] out T? entitySystem)
|
||||
where T : IEntitySystem
|
||||
{
|
||||
return _systemDependencyCollection.TryResolveType<T>(out entitySystem);
|
||||
return SystemDependencyCollection.TryResolveType<T>(out entitySystem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -122,7 +125,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
var excludedTypes = new HashSet<Type>();
|
||||
|
||||
_systemDependencyCollection = new(_dependencyCollection);
|
||||
SystemDependencyCollection = new(_dependencyCollection);
|
||||
var subTypes = new Dictionary<Type, Type>();
|
||||
_systemTypes.Clear();
|
||||
IEnumerable<Type> systems;
|
||||
@@ -138,9 +141,9 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var type in systems)
|
||||
{
|
||||
Logger.DebugS("go.sys", "Initializing entity system {0}", type);
|
||||
_sawmill.Debug("Initializing entity system {0}", type);
|
||||
|
||||
_systemDependencyCollection.Register(type);
|
||||
SystemDependencyCollection.Register(type);
|
||||
_systemTypes.Add(type);
|
||||
|
||||
excludedTypes.Add(type);
|
||||
@@ -173,21 +176,21 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var (baseType, type) in subTypes)
|
||||
{
|
||||
_systemDependencyCollection.Register(baseType, type, overwrite: true);
|
||||
SystemDependencyCollection.Register(baseType, type, overwrite: true);
|
||||
_systemTypes.Remove(baseType);
|
||||
}
|
||||
|
||||
_systemDependencyCollection.BuildGraph();
|
||||
SystemDependencyCollection.BuildGraph();
|
||||
|
||||
foreach (var systemType in _systemTypes)
|
||||
{
|
||||
var system = (IEntitySystem)_systemDependencyCollection.ResolveType(systemType);
|
||||
var system = (IEntitySystem)SystemDependencyCollection.ResolveType(systemType);
|
||||
system.Initialize();
|
||||
SystemLoaded?.Invoke(this, new SystemChangedArgs(system));
|
||||
}
|
||||
|
||||
// Create update order for entity systems.
|
||||
var (fUpdate, update) = CalculateUpdateOrder(_systemTypes, subTypes, _systemDependencyCollection);
|
||||
var (fUpdate, update) = CalculateUpdateOrder(_systemTypes, subTypes, SystemDependencyCollection);
|
||||
|
||||
_frameUpdateOrder = fUpdate.ToArray();
|
||||
_updateOrder = update
|
||||
@@ -263,8 +266,8 @@ namespace Robust.Shared.GameObjects
|
||||
// System.Values is modified by RemoveSystem
|
||||
foreach (var systemType in _systemTypes)
|
||||
{
|
||||
if(_systemDependencyCollection == null) continue;
|
||||
var system = (IEntitySystem)_systemDependencyCollection.ResolveType(systemType);
|
||||
if(SystemDependencyCollection == null) continue;
|
||||
var system = (IEntitySystem)SystemDependencyCollection.ResolveType(systemType);
|
||||
SystemUnloaded?.Invoke(this, new SystemChangedArgs(system));
|
||||
system.Shutdown();
|
||||
_entityManager.EventBus.UnsubscribeEvents(system);
|
||||
@@ -280,7 +283,7 @@ namespace Robust.Shared.GameObjects
|
||||
_updateOrder = Array.Empty<UpdateReg>();
|
||||
_frameUpdateOrder = Array.Empty<IEntitySystem>();
|
||||
_initialized = false;
|
||||
_systemDependencyCollection?.Clear();
|
||||
SystemDependencyCollection?.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -357,12 +360,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool TryGetEntitySystem(Type sysType, [NotNullWhen(true)] out object? system)
|
||||
{
|
||||
return _systemDependencyCollection.TryResolveType(sysType, out system);
|
||||
return SystemDependencyCollection.TryResolveType(sysType, out system);
|
||||
}
|
||||
|
||||
public object GetEntitySystem(Type sysType)
|
||||
{
|
||||
return _systemDependencyCollection.ResolveType(sysType);
|
||||
return SystemDependencyCollection.ResolveType(sysType);
|
||||
}
|
||||
|
||||
private static bool NeedsUpdate(Type type)
|
||||
@@ -406,6 +409,11 @@ namespace Robust.Shared.GameObjects
|
||||
return System.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill("go.sys");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SystemChangedArgs : EventArgs
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public sealed class EntityDeletedMessage : EntityEventArgs
|
||||
{
|
||||
public EntityUid Entity { get; }
|
||||
|
||||
public EntityDeletedMessage(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Instances are dynamically instantiated by a <c>ComponentFactory</c>, and will have their IoC Dependencies resolved.
|
||||
/// </remarks>
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public partial interface IComponent : ISerializationGenerated<IComponent>
|
||||
public partial interface IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The current lifetime stage of this component. You can use this to check
|
||||
|
||||
@@ -18,12 +18,6 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
event Action<RemovedComponentEventArgs>? ComponentRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// A component was deleted. This is usually deferred until some time after it was removed.
|
||||
/// Usually you will want to subscribe to <see cref="ComponentRemoved"/>.
|
||||
/// </summary>
|
||||
event Action<DeletedComponentEventArgs>? ComponentDeleted;
|
||||
|
||||
/// <summary>
|
||||
/// Calls Initialize() on all registered components of the entity.
|
||||
/// </summary>
|
||||
@@ -263,6 +257,14 @@ namespace Robust.Shared.GameObjects
|
||||
/// <returns>The component with the specified network id.</returns>
|
||||
IComponent GetComponent(EntityUid uid, ushort netId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type, even if it has been marked for deletion.
|
||||
/// </summary>
|
||||
/// <param name="uid">Entity UID to look on.</param>
|
||||
/// <param name="type">A trait or component type to check for.</param>
|
||||
/// <returns>The component of Type from the Entity.</returns>
|
||||
IComponent GetComponentInternal(EntityUid uid, CompIdx type);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
/// </summary>
|
||||
|
||||
91
Robust.Shared/GameObjects/IEntityManager.Spawn.cs
Normal file
91
Robust.Shared/GameObjects/IEntityManager.Spawn.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial interface IEntityManager
|
||||
{
|
||||
// This method will soon be marked as obsolete.
|
||||
EntityUid[] SpawnEntities(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
=> SpawnEntitiesAttachedTo(coordinates, protoNames);
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames);
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity in nullspace.
|
||||
/// </summary>
|
||||
EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity at a specific world position. The entity will either be parented to the map or a grid.
|
||||
/// </summary>
|
||||
EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity and then parents it to the entity that the given entity coordinates are relative to.
|
||||
/// </summary>
|
||||
EntityUid SpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the given entity coordinates into world coordinates and spawns an entity at that location. The
|
||||
/// entity will either be parented to the map or a grid.
|
||||
/// </summary>
|
||||
EntityUid SpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity inside of a container.
|
||||
/// </summary>
|
||||
bool TrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity inside of a container. If it fails to insert into the container, it will
|
||||
/// instead attempt to spawn the entity next to the target.
|
||||
/// </summary>
|
||||
public EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity adjacent to some other entity. If the other entity is in a container, this will
|
||||
/// attempt to insert the new entity into the same container.
|
||||
/// </summary>
|
||||
bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity adjacent to some other entity. If the other entity is in a container, this will
|
||||
/// attempt to insert the new entity into the same container. If it fails to insert into the container, it will
|
||||
/// instead attempt to spawn the entity next to the target's parent.
|
||||
/// </summary>
|
||||
EntityUid SpawnNextToOrDrop(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
}
|
||||
@@ -53,7 +53,6 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
event Action<EntityUid>? EntityAdded;
|
||||
event Action<EntityUid>? EntityInitialized;
|
||||
event Action<EntityUid>? EntityStarted;
|
||||
event Action<EntityUid>? EntityDeleted;
|
||||
event Action<EntityUid>? EntityDirtied; // only raised after initialization
|
||||
|
||||
@@ -71,32 +70,6 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
void StartEntity(EntityUid entity);
|
||||
|
||||
EntityUid[] SpawnEntities(EntityCoordinates coordinates, params string?[] protoNames);
|
||||
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames);
|
||||
|
||||
EntityUid[] SpawnEntities(EntityCoordinates coordinates, List<string?> protoNames);
|
||||
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an initialized entity and sets its local coordinates to the given entity coordinates. Note that this
|
||||
/// means that if you specify coordinates relative to some entity, the newly spawned entity will be a child of
|
||||
/// that entity.
|
||||
/// </summary>
|
||||
/// <param name="protoName">The prototype to clone. If this is null, the entity won't have a prototype.</param>
|
||||
/// <param name="coordinates"></param>
|
||||
/// <returns>Newly created entity.</returns>
|
||||
EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity at a specific world position.
|
||||
/// </summary>
|
||||
/// <param name="protoName"></param>
|
||||
/// <param name="coordinates"></param>
|
||||
/// <returns></returns>
|
||||
EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// How many entities are currently active.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Shared.GameObjects
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<CollisionWakeComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, ComponentShutdown>(OnRemove);
|
||||
|
||||
SubscribeLocalEvent<CollisionWakeComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, ComponentHandleState>(OnHandleState);
|
||||
@@ -59,7 +59,7 @@ namespace Robust.Shared.GameObjects
|
||||
args.State = new CollisionWakeComponent.CollisionWakeState(component.Enabled);
|
||||
}
|
||||
|
||||
private void OnRemove(EntityUid uid, CollisionWakeComponent component, ComponentRemove args)
|
||||
private void OnRemove(EntityUid uid, CollisionWakeComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (component.Enabled
|
||||
&& !Terminating(uid)
|
||||
|
||||
@@ -8,8 +8,13 @@ public sealed class DebugExceptionSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DebugExceptionOnAddComponent, ComponentAdd>((_, _, _) => throw new NotSupportedException());
|
||||
SubscribeLocalEvent<DebugExceptionOnAddComponent, ComponentAdd>(OnCompAdd);
|
||||
SubscribeLocalEvent<DebugExceptionInitializeComponent, ComponentInit>((_, _, _) => throw new NotSupportedException());
|
||||
SubscribeLocalEvent<DebugExceptionStartupComponent, ComponentStartup>((_, _, _) => throw new NotSupportedException());
|
||||
}
|
||||
|
||||
private void OnCompAdd(EntityUid uid, DebugExceptionOnAddComponent component, ComponentAdd args)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,8 +193,6 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
private void OnBroadphaseAdd(EntityUid uid, BroadphaseComponent component, ComponentAdd args)
|
||||
{
|
||||
component.DynamicTree = new DynamicTreeBroadPhase();
|
||||
component.StaticTree = new DynamicTreeBroadPhase();
|
||||
component.StaticSundriesTree = new DynamicTree<EntityUid>(
|
||||
(in EntityUid value) => GetTreeAABB(value, uid));
|
||||
component.SundriesTree = new DynamicTree<EntityUid>(
|
||||
@@ -221,7 +219,8 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
return _transform.GetInvWorldMatrix(treeXform).TransformBox(GetWorldAABB(entity, xform));
|
||||
}
|
||||
|
||||
internal void CreateProxies(EntityUid uid, TransformComponent xform, Fixture fixture, PhysicsComponent body)
|
||||
internal void CreateProxies(EntityUid uid, string fixtureId, Fixture fixture, TransformComponent xform,
|
||||
PhysicsComponent body)
|
||||
{
|
||||
if (!TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
return;
|
||||
@@ -237,10 +236,10 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
var tree = body.BodyType == BodyType.Static ? broadphase.StaticTree : broadphase.DynamicTree;
|
||||
DebugTools.Assert(fixture.ProxyCount == 0);
|
||||
|
||||
AddOrMoveProxies(uid, fixture, body, tree, broadphaseTransform, mapTransform, physMap.MoveBuffer);
|
||||
AddOrMoveProxies(uid, fixtureId, fixture, body, tree, broadphaseTransform, mapTransform, physMap.MoveBuffer);
|
||||
}
|
||||
|
||||
internal void DestroyProxies(EntityUid uid, Fixture fixture, TransformComponent xform, BroadphaseComponent broadphase, PhysicsMapComponent? physicsMap)
|
||||
internal void DestroyProxies(EntityUid uid, string fixtureId, Fixture fixture, TransformComponent xform, BroadphaseComponent broadphase, PhysicsMapComponent? physicsMap)
|
||||
{
|
||||
DebugTools.AssertNotNull(xform.Broadphase);
|
||||
DebugTools.Assert(xform.Broadphase!.Value.Uid == broadphase.Owner);
|
||||
@@ -250,7 +249,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
if (fixture.ProxyCount == 0)
|
||||
{
|
||||
Log.Warning($"Tried to destroy fixture {fixture.ID} on {ToPrettyString(uid)} that already has no proxies?");
|
||||
Log.Warning($"Tried to destroy fixture {fixtureId} on {ToPrettyString(uid)} that already has no proxies?");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -381,14 +380,15 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
// TODO BROADPHASE PARENTING this just assumes local = world
|
||||
var broadphaseTransform = new Transform(broadphaseXform.InvLocalMatrix.Transform(mapTransform.Position), mapTransform.Quaternion2D.Angle - broadphaseXform.LocalRotation);
|
||||
|
||||
foreach (var fixture in manager.Fixtures.Values)
|
||||
foreach (var (id, fixture) in manager.Fixtures)
|
||||
{
|
||||
AddOrMoveProxies(uid, fixture, body, tree, broadphaseTransform, mapTransform, physicsMap.MoveBuffer);
|
||||
AddOrMoveProxies(uid, id, fixture, body, tree, broadphaseTransform, mapTransform, physicsMap.MoveBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOrMoveProxies(
|
||||
EntityUid uid,
|
||||
string fixtureId,
|
||||
Fixture fixture,
|
||||
PhysicsComponent body,
|
||||
IBroadPhase tree,
|
||||
@@ -417,7 +417,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var bounds = fixture.Shape.ComputeAABB(broadphaseTransform, i);
|
||||
var proxy = new FixtureProxy(uid, body, bounds, fixture, i);
|
||||
var proxy = new FixtureProxy(uid, body, bounds, fixtureId, fixture, i);
|
||||
proxy.ProxyId = tree.AddProxy(ref proxy);
|
||||
proxy.AABB = bounds;
|
||||
proxies[i] = proxy;
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -95,12 +96,16 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
var fixtures = new List<Fixture>(mapChunks.Count);
|
||||
var fixtures = new Dictionary<string, Fixture>(mapChunks.Count);
|
||||
|
||||
foreach (var (chunk, rectangles) in mapChunks)
|
||||
{
|
||||
UpdateFixture(uid, chunk, rectangles, body, manager, xform);
|
||||
fixtures.AddRange(chunk.Fixtures);
|
||||
|
||||
foreach (var (id, fixture) in chunk.Fixtures)
|
||||
{
|
||||
fixtures[id] = fixture;
|
||||
}
|
||||
}
|
||||
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid,new GridFixtureChangeEvent {NewFixtures = fixtures}, true);
|
||||
@@ -126,7 +131,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// Additionally, we need to handle map deserialization where content may have stored its own data
|
||||
// on the grid (e.g. mass) which we want to preserve.
|
||||
var newFixtures = new List<Fixture>();
|
||||
var newFixtures = new ValueList<(string Id, Fixture Fixture)>();
|
||||
|
||||
Span<Vector2> vertices = stackalloc Vector2[4];
|
||||
|
||||
@@ -144,21 +149,20 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
#pragma warning disable CS0618
|
||||
var newFixture = new Fixture(
|
||||
$"grid_chunk-{bounds.Left}-{bounds.Bottom}",
|
||||
poly,
|
||||
MapGridHelpers.CollisionGroup,
|
||||
MapGridHelpers.CollisionGroup,
|
||||
true) { Body = body};
|
||||
#pragma warning restore CS0618
|
||||
|
||||
newFixtures.Add(newFixture);
|
||||
newFixtures.Add(($"grid_chunk-{bounds.Left}-{bounds.Bottom}", newFixture));
|
||||
}
|
||||
|
||||
var toRemove = new RemQueue<Fixture>();
|
||||
var toRemove = new ValueList<(string Id, Fixture Fixture)>();
|
||||
// Check if we even need to issue an eventbus event
|
||||
var updated = false;
|
||||
|
||||
foreach (var oldFixture in chunk.Fixtures)
|
||||
foreach (var (oldId, oldFixture) in chunk.Fixtures)
|
||||
{
|
||||
var existing = false;
|
||||
|
||||
@@ -166,8 +170,10 @@ namespace Robust.Shared.GameObjects
|
||||
// (TODO: Check IDs and cross-reference for updates?)
|
||||
for (var i = newFixtures.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var fixture = newFixtures[i];
|
||||
if (!oldFixture.Equals(fixture)) continue;
|
||||
var fixture = newFixtures[i].Fixture;
|
||||
if (!oldFixture.Equals(fixture))
|
||||
continue;
|
||||
|
||||
existing = true;
|
||||
newFixtures.RemoveSwap(i);
|
||||
break;
|
||||
@@ -176,36 +182,36 @@ namespace Robust.Shared.GameObjects
|
||||
// Doesn't align with any new fixtures so delete
|
||||
if (existing) continue;
|
||||
|
||||
toRemove.Add(oldFixture);
|
||||
toRemove.Add((oldId, oldFixture));
|
||||
}
|
||||
|
||||
foreach (var fixture in toRemove)
|
||||
foreach (var (id, fixture) in toRemove)
|
||||
{
|
||||
// TODO add a DestroyFixture() override that takes in a list.
|
||||
// reduced broadphase lookups
|
||||
chunk.Fixtures.Remove(fixture);
|
||||
_fixtures.DestroyFixture(uid, fixture, false, body: body, manager: manager, xform: xform);
|
||||
chunk.Fixtures.Remove(id);
|
||||
_fixtures.DestroyFixture(uid, id, fixture, false, body: body, manager: manager, xform: xform);
|
||||
}
|
||||
|
||||
if (newFixtures.Count > 0 || toRemove.List?.Count > 0)
|
||||
if (newFixtures.Count > 0 || toRemove.Count > 0)
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Anything remaining is a new fixture (or at least, may have not serialized onto the chunk yet).
|
||||
foreach (var fixture in newFixtures)
|
||||
foreach (var (id, fixture) in newFixtures)
|
||||
{
|
||||
var existingFixture = _fixtures.GetFixtureOrNull(uid, fixture.ID, manager: manager);
|
||||
var existingFixture = _fixtures.GetFixtureOrNull(uid, id, manager: manager);
|
||||
// Check if it's the same (otherwise remove anyway).
|
||||
if (existingFixture?.Shape is PolygonShape poly &&
|
||||
poly.EqualsApprox((PolygonShape) fixture.Shape))
|
||||
{
|
||||
chunk.Fixtures.Add(existingFixture);
|
||||
chunk.Fixtures.Add(id, existingFixture);
|
||||
continue;
|
||||
}
|
||||
|
||||
chunk.Fixtures.Add(fixture);
|
||||
_fixtures.CreateFixture(uid, fixture, false, manager, body, xform);
|
||||
chunk.Fixtures.Add(id, fixture);
|
||||
_fixtures.CreateFixture(uid, id, fixture, false, manager, body, xform);
|
||||
}
|
||||
|
||||
return updated;
|
||||
@@ -218,7 +224,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public sealed class GridFixtureChangeEvent : EntityEventArgs
|
||||
{
|
||||
public List<Fixture> NewFixtures { get; init; } = default!;
|
||||
public Dictionary<string, Fixture> NewFixtures { get; init; } = default!;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -4,7 +4,14 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public abstract class SharedInputSystem : EntitySystem
|
||||
{
|
||||
private readonly CommandBindRegistry _bindRegistry = new();
|
||||
private CommandBindRegistry _bindRegistry = default!;
|
||||
|
||||
protected override void PostInject()
|
||||
{
|
||||
base.PostInject();
|
||||
|
||||
_bindRegistry = new CommandBindRegistry(Log);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the keyFunction -> handler bindings for the simulation.
|
||||
|
||||
@@ -540,9 +540,9 @@ public abstract partial class SharedMapSystem
|
||||
PhysicsComponent? body = null;
|
||||
TransformComponent? xform = null;
|
||||
|
||||
foreach (var fixture in mapChunk.Fixtures)
|
||||
foreach (var (id, fixture) in mapChunk.Fixtures)
|
||||
{
|
||||
_fixtures.DestroyFixture(uid, fixture, false, manager: manager, body: body, xform: xform);
|
||||
_fixtures.DestroyFixture(uid, id, fixture, false, manager: manager, body: body, xform: xform);
|
||||
}
|
||||
|
||||
RemoveChunk(uid, grid, mapChunk.Indices);
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
@@ -619,23 +620,19 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public TransformComponent? GetParent(EntityUid uid)
|
||||
{
|
||||
return GetParent(uid, _xformQuery);
|
||||
}
|
||||
|
||||
public TransformComponent? GetParent(EntityUid uid, EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
return GetParent(xformQuery.GetComponent(uid), xformQuery);
|
||||
return GetParent(_xformQuery.GetComponent(uid));
|
||||
}
|
||||
|
||||
public TransformComponent? GetParent(TransformComponent xform)
|
||||
{
|
||||
return GetParent(xform, _xformQuery);
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return null;
|
||||
return _xformQuery.GetComponent(xform.ParentUid);
|
||||
}
|
||||
|
||||
public TransformComponent? GetParent(TransformComponent xform, EntityQuery<TransformComponent> xformQuery)
|
||||
public EntityUid GetParentUid(EntityUid uid)
|
||||
{
|
||||
if (!xform.ParentUid.IsValid()) return null;
|
||||
return xformQuery.GetComponent(xform.ParentUid);
|
||||
return _xformQuery.GetComponent(uid).ParentUid;
|
||||
}
|
||||
|
||||
public void SetParent(EntityUid uid, EntityUid parent)
|
||||
@@ -1373,4 +1370,39 @@ public abstract partial class SharedTransformSystem
|
||||
component._gridInitialized = true;
|
||||
component._gridUid = uid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to place one entity next to another entity. If the target entity is in a container, this will attempt
|
||||
/// to insert that entity into the same container.
|
||||
/// </summary>
|
||||
public void PlaceNextToOrDrop(EntityUid uid, EntityUid target,
|
||||
TransformComponent? xform = null, TransformComponent? targetXform = null)
|
||||
{
|
||||
if (!_xformQuery.Resolve(target, ref targetXform))
|
||||
return;
|
||||
|
||||
if (!_xformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
var meta = _metaQuery.GetComponent(target);
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == 0)
|
||||
{
|
||||
if (targetXform.ParentUid.IsValid())
|
||||
SetCoordinates(uid, xform, targetXform.Coordinates);
|
||||
else
|
||||
DetachParentToNull(uid, xform);
|
||||
return;
|
||||
}
|
||||
|
||||
var containerComp = Comp<ContainerManagerComponent>(targetXform.ParentUid);
|
||||
foreach (var container in containerComp.Containers.Values)
|
||||
{
|
||||
if (!container.Contains(target))
|
||||
continue;
|
||||
|
||||
if (!container.Insert(uid, EntityManager, xform))
|
||||
PlaceNextToOrDrop(uid, targetXform.ParentUid, xform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Robust.Shared.Input.Binding
|
||||
/// <inheritdoc cref="ICommandBindRegistry"/>
|
||||
public sealed class CommandBindRegistry : ICommandBindRegistry
|
||||
{
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
// all registered bindings
|
||||
private List<TypedCommandBind> _bindings = new();
|
||||
// handlers in the order they should be resolved for the given key function.
|
||||
@@ -17,6 +19,11 @@ namespace Robust.Shared.Input.Binding
|
||||
private Dictionary<BoundKeyFunction, List<InputCmdHandler>> _bindingsForKey = new();
|
||||
private bool _graphDirty = false;
|
||||
|
||||
public CommandBindRegistry(ISawmill sawmill)
|
||||
{
|
||||
_sawmill = sawmill;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register<TOwner>(CommandBinds commandBinds)
|
||||
{
|
||||
@@ -30,11 +37,11 @@ namespace Robust.Shared.Input.Binding
|
||||
{
|
||||
// feel free to delete this if there's an actual need for registering multiple
|
||||
// bindings for a given type in separate calls to Register()
|
||||
Logger.Warning("Command binds already registered for type {0}, but you are trying" +
|
||||
" to register more. This may " +
|
||||
"be a programming error. Did you register these under the wrong type, or " +
|
||||
"did you forget to unregister these bindings when" +
|
||||
" your system / manager is shutdown?", owner.Name);
|
||||
_sawmill.Warning("Command binds already registered for type {0}, but you are trying" +
|
||||
" to register more. This may " +
|
||||
"be a programming error. Did you register these under the wrong type, or " +
|
||||
"did you forget to unregister these bindings when" +
|
||||
" your system / manager is shutdown?", owner.Name);
|
||||
}
|
||||
|
||||
foreach (var binding in commandBinds.Bindings)
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace Robust.Shared.Input
|
||||
public static readonly BoundKeyFunction TextWordDelete = "TextWordDelete";
|
||||
public static readonly BoundKeyFunction TextNewline = "TextNewline";
|
||||
public static readonly BoundKeyFunction TextSubmit = "TextSubmit";
|
||||
public static readonly BoundKeyFunction MultilineTextSubmit = "MultilineTextSubmit";
|
||||
public static readonly BoundKeyFunction TextSelectAll = "TextSelectAll";
|
||||
public static readonly BoundKeyFunction TextCopy = "TextCopy";
|
||||
public static readonly BoundKeyFunction TextCut = "TextCut";
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Linguini.Bundle.Errors;
|
||||
using Linguini.Syntax.Parser.Error;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Localization;
|
||||
|
||||
@@ -18,52 +19,53 @@ internal static class LocHelper
|
||||
|
||||
private static string FormatErrors(string message, ErrorSpan span, ReadOnlyMemory<char> resource, string? newLine)
|
||||
{
|
||||
newLine ??= Environment.NewLine;
|
||||
|
||||
var lines = new ValueList<(int start, int end)>();
|
||||
|
||||
// Isolate the lines in the context
|
||||
// Also figure out the line number for the 1st line (1-indexed).
|
||||
var startLineNumber = -1;
|
||||
var markOffset = 0;
|
||||
var curLineNumber = 0;
|
||||
var lineEnumerator = new LineEnumerator(resource);
|
||||
while (lineEnumerator.MoveNext(out var lineStart, out var lineEnd))
|
||||
{
|
||||
curLineNumber += 1;
|
||||
if (span.StartSpan >= lineEnd)
|
||||
continue;
|
||||
|
||||
if (span.EndSpan <= lineStart)
|
||||
break;
|
||||
|
||||
lines.Add((lineStart, lineEnd));
|
||||
if (startLineNumber == -1)
|
||||
startLineNumber = curLineNumber;
|
||||
|
||||
if (span.StartMark < lineEnd && span.StartMark >= lineStart)
|
||||
markOffset = span.StartMark - lineStart;
|
||||
}
|
||||
|
||||
// Figure out width of line number column.
|
||||
var lastLine = lines.Count + startLineNumber - 1;
|
||||
var lastLineNumberWidth = $"{lastLine}".Length;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var errContext = resource.Slice(span.StartSpan, span.EndSpan - span.StartSpan).ToString();
|
||||
var lines = new List<ReadOnlyMemory<char>>(5);
|
||||
var currLineOffset = 0;
|
||||
var lastStart = 0;
|
||||
for (var i = 0; i < span.StartMark - span.StartSpan; i++)
|
||||
curLineNumber = startLineNumber;
|
||||
foreach (var (lineStart, lineEnd) in lines)
|
||||
{
|
||||
switch (errContext[i])
|
||||
{
|
||||
// Reset current line so that mark aligns with the reported error
|
||||
// We cheat here a bit, since we both `\r\n` and `\n` end with '\n'
|
||||
case '\n':
|
||||
if (i > 0 && errContext[i - 1] == '\r')
|
||||
{
|
||||
lines.Add(resource.Slice(lastStart, currLineOffset - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.Add(resource.Slice(lastStart, currLineOffset));
|
||||
}
|
||||
var linePadded = $"{curLineNumber}".PadLeft(lastLineNumberWidth);
|
||||
var line = resource.Span[lineStart..lineEnd];
|
||||
sb.Append($" {linePadded} |{line.TrimEnd()}");
|
||||
sb.Append(newLine);
|
||||
|
||||
lastStart = currLineOffset + 1;
|
||||
currLineOffset = 0;
|
||||
break;
|
||||
default:
|
||||
currLineOffset++;
|
||||
break;
|
||||
}
|
||||
curLineNumber += 1;
|
||||
}
|
||||
|
||||
lines.Add(resource.Slice(lastStart, resource.Length - lastStart));
|
||||
sb.Append(' ', markOffset + lastLineNumberWidth + 3);
|
||||
sb.Append('^', span.EndMark - span.StartMark);
|
||||
sb.Append($" {message}");
|
||||
|
||||
|
||||
var lastLine = $"{span.Row + lines.Count - 1}".Length;
|
||||
for (var index = 0; index < lines.Count; index++)
|
||||
{
|
||||
var line = lines[index];
|
||||
|
||||
sb.Append(newLine ?? Environment.NewLine).Append(' ').Append($"{span.Row + index}".PadLeft(lastLine))
|
||||
.Append(" |").Append(line);
|
||||
}
|
||||
|
||||
sb.Append(newLine ?? Environment.NewLine)
|
||||
.Append(' ', currLineOffset + lastLine + 3)
|
||||
.Append('^', span.EndMark - span.StartMark)
|
||||
.Append($" {message}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,12 +248,17 @@ namespace Robust.Shared.Localization
|
||||
|
||||
var resources = files.AsParallel().Select(path =>
|
||||
{
|
||||
using var fileStream = resourceManager.ContentFileRead(path);
|
||||
using var reader = new StreamReader(fileStream, EncodingHelpers.UTF8);
|
||||
string contents;
|
||||
|
||||
var parser = new LinguiniParser(reader);
|
||||
using (var fileStream = resourceManager.ContentFileRead(path))
|
||||
using (var reader = new StreamReader(fileStream, EncodingHelpers.UTF8))
|
||||
{
|
||||
contents = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
var parser = new LinguiniParser(contents);
|
||||
var resource = parser.Parse();
|
||||
return (path, resource, parser.GetReadonlyData);
|
||||
return (path, resource, contents);
|
||||
});
|
||||
|
||||
foreach (var (path, resource, data) in resources)
|
||||
@@ -264,11 +269,11 @@ namespace Robust.Shared.Localization
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteWarningForErrs(ResPath path, List<ParseError> errs, ReadOnlyMemory<char> resource)
|
||||
private void WriteWarningForErrs(ResPath path, List<ParseError> errs, string resource)
|
||||
{
|
||||
foreach (var err in errs)
|
||||
{
|
||||
_logSawmill.Warning("{path}:\n{exception}", path, err.FormatCompileErrors(resource));
|
||||
_logSawmill.Error($"{path}:\n{err.FormatCompileErrors(resource.AsMemory())}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +281,7 @@ namespace Robust.Shared.Localization
|
||||
{
|
||||
foreach (var err in errs)
|
||||
{
|
||||
_logSawmill.Warning("Error extracting `{locId}`\n{e1}", locId, err);
|
||||
_logSawmill.Error("Error extracting `{locId}`\n{e1}", locId, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Robust.Shared.Map
|
||||
/// <summary>
|
||||
/// Physics fixtures that make up this grid chunk.
|
||||
/// </summary>
|
||||
public List<Fixture> Fixtures { get; } = new();
|
||||
public Dictionary<string, Fixture> Fixtures { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The last game simulation tick that a tile on this chunk was modified.
|
||||
|
||||
@@ -125,7 +125,7 @@ internal partial class MapManager
|
||||
|
||||
while (enumerator.MoveNext(out var chunk))
|
||||
{
|
||||
foreach (var fixture in chunk.Fixtures)
|
||||
foreach (var fixture in chunk.Fixtures.Values)
|
||||
{
|
||||
for (var j = 0; j < fixture.Shape.ChildCount; j++)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ internal sealed class MapSerializationContext : ISerializationContext, IEntityLo
|
||||
public SerializationManager.SerializerProvider SerializerProvider { get; } = new();
|
||||
|
||||
// Run-specific data
|
||||
public Dictionary<ushort, string>? TileMap;
|
||||
public Dictionary<int, string>? TileMap;
|
||||
public readonly Dictionary<string, IComponent> CurrentReadingEntityComponents = new();
|
||||
public HashSet<string> CurrentlyIgnoredComponents = new();
|
||||
public string? CurrentComponent;
|
||||
@@ -128,7 +128,11 @@ internal sealed class MapSerializationContext : ISerializationContext, IEntityLo
|
||||
return new ValueDataNode("invalid");
|
||||
}
|
||||
|
||||
Logger.ErrorS("map", "Encountered an invalid entityUid '{0}' while serializing a map.", value);
|
||||
dependencies
|
||||
.Resolve<ILogManager>()
|
||||
.GetSawmill("map")
|
||||
.Error("Encountered an invalid entityUid '{0}' while serializing a map.", value);
|
||||
|
||||
return new ValueDataNode("invalid");
|
||||
}
|
||||
|
||||
@@ -147,7 +151,11 @@ internal sealed class MapSerializationContext : ISerializationContext, IEntityLo
|
||||
if (int.TryParse(node.Value, out var val) && _uidEntityMap.TryGetValue(val, out var entity))
|
||||
return entity;
|
||||
|
||||
Logger.ErrorS("map", "Error in map file: found local entity UID '{0}' which does not exist.", val);
|
||||
dependencies
|
||||
.Resolve<ILogManager>()
|
||||
.GetSawmill("map")
|
||||
.Error("Error in map file: found local entity UID '{0}' which does not exist.", val);
|
||||
|
||||
return EntityUid.Invalid;
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public readonly struct Tile : IEquatable<Tile>, ISpanFormattable
|
||||
/// <summary>
|
||||
/// Internal type ID of this tile.
|
||||
/// </summary>
|
||||
public readonly ushort TypeId;
|
||||
public readonly int TypeId;
|
||||
|
||||
/// <summary>
|
||||
/// Rendering flags.
|
||||
@@ -41,37 +41,13 @@ public readonly struct Tile : IEquatable<Tile>, ISpanFormattable
|
||||
/// <param name="typeId">Internal type ID.</param>
|
||||
/// <param name="flags">Flags used by toolbox's rendering.</param>
|
||||
/// <param name="variant">The visual variant this tile is using.</param>
|
||||
public Tile(ushort typeId, TileRenderFlag flags = 0, byte variant = 0)
|
||||
public Tile(int typeId, TileRenderFlag flags = 0, byte variant = 0)
|
||||
{
|
||||
TypeId = typeId;
|
||||
Flags = flags;
|
||||
Variant = variant;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion of <c>Tile</c> to <c>uint</c> . This should only
|
||||
/// be used in special cases like serialization. Do NOT use this in
|
||||
/// content.
|
||||
/// </summary>
|
||||
public static explicit operator uint(Tile tile)
|
||||
{
|
||||
return ((uint)tile.TypeId << 16) | (uint)tile.Flags << 8 | tile.Variant;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion of <c>uint</c> to <c>Tile</c> . This should only
|
||||
/// be used in special cases like serialization. Do NOT use this in
|
||||
/// content.
|
||||
/// </summary>
|
||||
public static explicit operator Tile(uint tile)
|
||||
{
|
||||
return new(
|
||||
(ushort)(tile >> 16),
|
||||
(TileRenderFlag)(tile >> 8),
|
||||
(byte)tile
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for equality by value between two objects.
|
||||
/// </summary>
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Robust.Shared.Network.Messages
|
||||
/// </summary>
|
||||
public bool Replacement { get; set; }
|
||||
public bool IsTile { get; set; }
|
||||
public ushort TileType { get; set; }
|
||||
public int TileType { get; set; }
|
||||
public string EntityTemplateName { get; set; }
|
||||
public EntityCoordinates EntityCoordinates { get; set; }
|
||||
public Direction DirRcv { get; set; }
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Shared.Network.Messages
|
||||
IsTile = buffer.ReadBoolean();
|
||||
Replacement = buffer.ReadBoolean();
|
||||
|
||||
if (IsTile) TileType = buffer.ReadUInt16();
|
||||
if (IsTile) TileType = buffer.ReadInt32();
|
||||
else EntityTemplateName = buffer.ReadString();
|
||||
|
||||
EntityCoordinates = buffer.ReadEntityCoordinates();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user