mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
339 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6cadfedd5 | ||
|
|
9f57b705d7 | ||
|
|
68be9712ad | ||
|
|
aaa446254c | ||
|
|
5e2d2ab317 | ||
|
|
20ae63fbbd | ||
|
|
a92c0cbef4 | ||
|
|
95649a2dd0 | ||
|
|
861807f8b4 | ||
|
|
bd73f1c05a | ||
|
|
7dce51e2cf | ||
|
|
d9b0f3a227 | ||
|
|
05766a2eaa | ||
|
|
a761fbc09e | ||
|
|
f69440b3f2 | ||
|
|
b459d2ce21 | ||
|
|
202182e3d4 | ||
|
|
96cb52e5d2 | ||
|
|
82e0c0baeb | ||
|
|
54d6552164 | ||
|
|
c21b6c993c | ||
|
|
2fe4a8b859 | ||
|
|
8325966dbb | ||
|
|
2459a9d688 | ||
|
|
2cd2d1edd6 | ||
|
|
b982350851 | ||
|
|
4a50bc2154 | ||
|
|
4c85e205b9 | ||
|
|
0b447d9d82 | ||
|
|
ceb205ad52 | ||
|
|
a48ff3dbf1 | ||
|
|
2b85fa88c1 | ||
|
|
19564a421b | ||
|
|
b3f0e467ee | ||
|
|
216292c849 | ||
|
|
68753d15e0 | ||
|
|
2a357051ae | ||
|
|
58e0b62145 | ||
|
|
14cc273997 | ||
|
|
93f4428635 | ||
|
|
164bf68aca | ||
|
|
773b87672b | ||
|
|
eecf834039 | ||
|
|
325fe46aa3 | ||
|
|
2f6c29ab43 | ||
|
|
aab1a2dba9 | ||
|
|
f36fbd9c83 | ||
|
|
126c863f45 | ||
|
|
618a8491bf | ||
|
|
2743b64a2b | ||
|
|
28cc91934c | ||
|
|
eadfcd4c09 | ||
|
|
7871b0010e | ||
|
|
3da04ed17e | ||
|
|
170d192791 | ||
|
|
dcd9939554 | ||
|
|
98ef58eca6 | ||
|
|
ab1e99a0df | ||
|
|
499c236798 | ||
|
|
8dc2345ceb | ||
|
|
9b04270178 | ||
|
|
d75dbc901f | ||
|
|
19a3e82848 | ||
|
|
911abf2693 | ||
|
|
f5874ea402 | ||
|
|
b486ef885c | ||
|
|
9d55d77e48 | ||
|
|
5af3cb969c | ||
|
|
429bc806dc | ||
|
|
81484699a8 | ||
|
|
7cad8d5ba3 | ||
|
|
3aa04a3c86 | ||
|
|
9750b113c8 | ||
|
|
5a6c4220fc | ||
|
|
b2d389f184 | ||
|
|
ad0cb05dd6 | ||
|
|
ad134d9e4e | ||
|
|
be33bc2219 | ||
|
|
aa2fd2107d | ||
|
|
554e0777b1 | ||
|
|
21b7c5f93e | ||
|
|
9e5c1e9c95 | ||
|
|
6825f09fb9 | ||
|
|
58e3a4eb4a | ||
|
|
9a342f0d11 | ||
|
|
f754ddb96d | ||
|
|
7feede0d95 | ||
|
|
ea152366e3 | ||
|
|
ab47d4e009 | ||
|
|
81b2a3825e | ||
|
|
56d850f389 | ||
|
|
b737ecf9b3 | ||
|
|
ed5223b592 | ||
|
|
f87012e681 | ||
|
|
54529fdbe3 | ||
|
|
1745a12e5a | ||
|
|
d201d787b7 | ||
|
|
904ddea274 | ||
|
|
6b6ec844e8 | ||
|
|
f24d18f470 | ||
|
|
77654a1628 | ||
|
|
f3af813b57 | ||
|
|
0623baedcf | ||
|
|
2ade6c04c5 | ||
|
|
a9df9097c1 | ||
|
|
755dac719f | ||
|
|
7095a58685 | ||
|
|
16e68a4351 | ||
|
|
0152f9d1d8 | ||
|
|
16d916796a | ||
|
|
e865157432 | ||
|
|
24d5ce4bd4 | ||
|
|
662195e4ff | ||
|
|
d00fd6f736 | ||
|
|
2d58c1071d | ||
|
|
a9db89d023 | ||
|
|
684cabf3e6 | ||
|
|
a4f51f0cd9 | ||
|
|
a8ddd837c8 | ||
|
|
82aace7997 | ||
|
|
01ce244b7b | ||
|
|
58aa6e5c75 | ||
|
|
4818c3aab4 | ||
|
|
3b6adeb5ff | ||
|
|
889b8351be | ||
|
|
ac37b0a131 | ||
|
|
f6f1fc425a | ||
|
|
7476628840 | ||
|
|
668cdbe76b | ||
|
|
a0a6e9b111 | ||
|
|
06d28f04e6 | ||
|
|
57897161d0 | ||
|
|
c4c528478e | ||
|
|
a6c295b89c | ||
|
|
165913a4de | ||
|
|
675dfdaabd | ||
|
|
fab172d6f6 | ||
|
|
e75c1659f6 | ||
|
|
0c440a8fc9 | ||
|
|
0c2c8f352a | ||
|
|
0a4a2b7a36 | ||
|
|
b5b59c1d2f | ||
|
|
f4f0967fdc | ||
|
|
3d69766112 | ||
|
|
d1eb3438d5 | ||
|
|
8f6b189d29 | ||
|
|
ef8b278b47 | ||
|
|
c53ce2c907 | ||
|
|
f063aa3ea1 | ||
|
|
30f63254ef | ||
|
|
30a5b6152c | ||
|
|
910a7f8bff | ||
|
|
526a88293e | ||
|
|
22cd840b83 | ||
|
|
415c518bc7 | ||
|
|
2417dbb0e0 | ||
|
|
005673a957 | ||
|
|
942db3120c | ||
|
|
c0a5fab19e | ||
|
|
d16c62b132 | ||
|
|
7da22557fe | ||
|
|
92f47c0f20 | ||
|
|
9576d0739f | ||
|
|
10f25faabf | ||
|
|
74831a177e | ||
|
|
88d3168913 | ||
|
|
902519093c | ||
|
|
366266a8ae | ||
|
|
a22cce7783 | ||
|
|
e5e738b8cd | ||
|
|
b8f6e83473 | ||
|
|
c5fb186c57 | ||
|
|
131d7f5422 | ||
|
|
217996f1ed | ||
|
|
fc718d68a5 | ||
|
|
d7d9578803 | ||
|
|
9bbeb54569 | ||
|
|
1ea7071ffb | ||
|
|
196028b619 | ||
|
|
c102da052f | ||
|
|
5d46cdcfa4 | ||
|
|
cd646d3b07 | ||
|
|
922165fa19 | ||
|
|
4879252e99 | ||
|
|
0e21f5727a | ||
|
|
3ce8a00389 | ||
|
|
9a283fe541 | ||
|
|
f3e3e64db3 | ||
|
|
4a4a135089 | ||
|
|
e323a67806 | ||
|
|
3a328ffdd5 | ||
|
|
bc5107e297 | ||
|
|
2abf33c9be | ||
|
|
71c46828c2 | ||
|
|
814d6fe2d0 | ||
|
|
77b98b8308 | ||
|
|
34b0a7fc6d | ||
|
|
1b6123c79f | ||
|
|
1476f9d462 | ||
|
|
d62efe7301 | ||
|
|
6af0c88f27 | ||
|
|
5f05b0aa2a | ||
|
|
e6c335b6cd | ||
|
|
5cd8e8276e | ||
|
|
22aeec45f9 | ||
|
|
aed53fb63d | ||
|
|
5ebe97aec1 | ||
|
|
3ff374a4af | ||
|
|
c5bcf853ac | ||
|
|
0624ac36cd | ||
|
|
dd906e9b01 | ||
|
|
7f99b44e5c | ||
|
|
268eb862ea | ||
|
|
467f518421 | ||
|
|
4666a87aa5 | ||
|
|
25007a743f | ||
|
|
8ce3a03136 | ||
|
|
c4d6690a71 | ||
|
|
5486bc7686 | ||
|
|
cdf44ef3d9 | ||
|
|
49ec5b9ca3 | ||
|
|
8b53b89423 | ||
|
|
3fd731d917 | ||
|
|
cb1d4ae843 | ||
|
|
039b70f502 | ||
|
|
7892cc895f | ||
|
|
77108284b8 | ||
|
|
5e21dbdd7f | ||
|
|
8274623edb | ||
|
|
e923d69083 | ||
|
|
6e8ab5ce78 | ||
|
|
f905ea631b | ||
|
|
be54c41891 | ||
|
|
33184ecfa5 | ||
|
|
11cf0c1703 | ||
|
|
528544b7a2 | ||
|
|
8571d7e7b5 | ||
|
|
0f06423b7a | ||
|
|
eb9e0ffefc | ||
|
|
903619ecef | ||
|
|
879c6ea538 | ||
|
|
5478545aeb | ||
|
|
650929dcbb | ||
|
|
a289659b49 | ||
|
|
85d15c21e1 | ||
|
|
bcd1566440 | ||
|
|
749ac2c364 | ||
|
|
5eed3bc281 | ||
|
|
d78f378493 | ||
|
|
eef44c15cf | ||
|
|
3d1b2418f9 | ||
|
|
6b49a86ee5 | ||
|
|
cd13cd3cd8 | ||
|
|
2b8d8d6636 | ||
|
|
409fe1a125 | ||
|
|
ab5db4641c | ||
|
|
064e8ee365 | ||
|
|
02dcff7eae | ||
|
|
e1e5f8de54 | ||
|
|
d5ba822a79 | ||
|
|
f448c6b8fa | ||
|
|
5e1d80be35 | ||
|
|
01546f32da | ||
|
|
aeeaaaefc5 | ||
|
|
b6c8060af1 | ||
|
|
99685838da | ||
|
|
8917b29255 | ||
|
|
f0c4d7c5eb | ||
|
|
6a00c62d3c | ||
|
|
fc3116fca5 | ||
|
|
98c1397b3a | ||
|
|
2464bb6c2f | ||
|
|
709142acee | ||
|
|
af4e3e5e1c | ||
|
|
d51a18c6ea | ||
|
|
d5c3d4c0c9 | ||
|
|
a4474d8df8 | ||
|
|
d66f7c7c06 | ||
|
|
b6879869d6 | ||
|
|
815b8e0c48 | ||
|
|
ef4e3baa7f | ||
|
|
270ddb5a53 | ||
|
|
6133fe0808 | ||
|
|
909fd326a0 | ||
|
|
876de4065a | ||
|
|
60e159f0d0 | ||
|
|
80f3aae30c | ||
|
|
98b1862433 | ||
|
|
d2311c193f | ||
|
|
f05ed96461 | ||
|
|
dc23dfaf4d | ||
|
|
62315f7c2e | ||
|
|
b2d121e780 | ||
|
|
fb4b029122 | ||
|
|
66239d23ea | ||
|
|
dbb45f1c13 | ||
|
|
6284e16b64 | ||
|
|
f6c55085fe | ||
|
|
30eafd26e7 | ||
|
|
63423d96b4 | ||
|
|
474334aff2 | ||
|
|
be102f86bf | ||
|
|
d7962c7190 | ||
|
|
7fe9385c3b | ||
|
|
a9d9d1348a | ||
|
|
4eaf624555 | ||
|
|
e37c131fb4 | ||
|
|
9df4606492 | ||
|
|
3be8070274 | ||
|
|
8a440d705f | ||
|
|
5849474022 | ||
|
|
b7d67c0ece | ||
|
|
b04cf71bc0 | ||
|
|
ea87df649a | ||
|
|
dab7a9112f | ||
|
|
d3dc89832a | ||
|
|
3ade9ca447 | ||
|
|
149e9a2613 | ||
|
|
d164148ce2 | ||
|
|
d898b52449 | ||
|
|
dcf7a1e580 | ||
|
|
65c6bb74eb | ||
|
|
d6d88bea91 | ||
|
|
d6467f768a | ||
|
|
4f0f020f56 | ||
|
|
5ce8369fb9 | ||
|
|
2446e64033 | ||
|
|
bdd65cda4b | ||
|
|
77e949bfe8 | ||
|
|
d4171351f4 | ||
|
|
e67d0ad3d6 | ||
|
|
aff5711fde | ||
|
|
a886222946 | ||
|
|
5843f1087e | ||
|
|
93c0ce815f | ||
|
|
1c64fa1f28 | ||
|
|
c825c1e413 | ||
|
|
f30fb47834 | ||
|
|
5d255e06c8 |
20
.github/CODEOWNERS
vendored
20
.github/CODEOWNERS
vendored
@@ -1,18 +1,12 @@
|
||||
# Last match in file takes precedence.
|
||||
|
||||
# Ping for all PRs
|
||||
* @Acruid @PJB3005 @ZoldorfTheWizard
|
||||
* @PJB3005 @DrSmugleaf
|
||||
|
||||
/Robust.Client.NameGenerator @PaulRitter
|
||||
/Robust.Client.Injectors @PaulRitter
|
||||
/Robust.Generators @PaulRitter
|
||||
/Robust.Analyzers @PaulRitter
|
||||
/Robust.*/GameStates @PaulRitter
|
||||
/Robust.Shared/Analyzers @PaulRitter
|
||||
/Robust.*/Serialization @PaulRitter @DrSmugleaf
|
||||
/Robust.*/Prototypes @PaulRitter
|
||||
/Robust.Shared/GameObjects/ComponentDependencies @PaulRitter
|
||||
/Robust.*/Containers @PaulRitter
|
||||
# commands commands commands commands
|
||||
**/Toolshed/** @moonheart08
|
||||
*Command.cs @moonheart08
|
||||
*Commands.cs @moonheart08
|
||||
|
||||
# Be they Fluent translations or Freemarker templates, I know them both!
|
||||
*.ftl @RemieRichards
|
||||
# Physics
|
||||
**/Robust.Shared/Physics/** @metalgearsloth
|
||||
|
||||
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
|
||||
|
||||
|
||||
10
.github/workflows/publish-client.yml
vendored
10
.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
|
||||
|
||||
@@ -33,10 +33,10 @@ jobs:
|
||||
mkdir "release/${{ steps.parse_version.outputs.version }}"
|
||||
mv release/*.zip "release/${{ steps.parse_version.outputs.version }}"
|
||||
|
||||
- name: Upload files to Suns
|
||||
- name: Upload files to centcomm
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: suns.spacestation14.com
|
||||
host: centcomm.spacestation14.io
|
||||
username: robust-build-push
|
||||
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
|
||||
source: "release/${{ steps.parse_version.outputs.version }}"
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Update manifest JSON
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: suns.spacestation14.com
|
||||
host: centcomm.spacestation14.io
|
||||
username: robust-build-push
|
||||
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
|
||||
script: /home/robust-build-push/push.ps1 ${{ steps.parse_version.outputs.version }}
|
||||
|
||||
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
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,3 +13,6 @@
|
||||
[submodule "cefglue"]
|
||||
path = cefglue
|
||||
url = https://github.com/space-wizards/cefglue.git
|
||||
[submodule "Arch/Arch"]
|
||||
path = Arch/Arch
|
||||
url = https://github.com/space-wizards/Arch.git
|
||||
|
||||
1
Arch/Arch
Submodule
1
Arch/Arch
Submodule
Submodule Arch/Arch added at c76d18feb7
94
Arch/Arch.csproj
Normal file
94
Arch/Arch.csproj
Normal file
@@ -0,0 +1,94 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<Nullable>enable</Nullable>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||
|
||||
<PackageId>Arch</PackageId>
|
||||
<Title>Arch</Title>
|
||||
<Version>1.2.7.1-alpha</Version>
|
||||
<Authors>genaray</Authors>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<Description>A high performance c# net.6 and net.7 archetype based ECS ( Entity component system ).</Description>
|
||||
<PackageReleaseNotes>Updated LowLevel which fixes bugs. </PackageReleaseNotes>
|
||||
<PackageTags>c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system;stride;unity;godot;</PackageTags>
|
||||
|
||||
<PackageProjectUrl>https://github.com/genaray/Arch</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/genaray/Arch.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<IsPackable>true</IsPackable>
|
||||
|
||||
<LangVersion>11</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Copyright>Apache2.0</Copyright>
|
||||
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
|
||||
<Configurations>Debug;Debug-PureECS;Debug-Events;Release;Release-PureECS;Release-Events;</Configurations>
|
||||
|
||||
<AssemblyName>Arch</AssemblyName>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<DefaultItemExcludes>src/Arch/**/*</DefaultItemExcludes>
|
||||
<DefineConstants>$(DefineConstants);PURE_ECS;CONTRACTS_FULL</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DefineConstants>$(DefineConstants);PURE_ECS;CONTRACTS_FULL;TRACE;</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="Arch.Benchmarks" />
|
||||
<InternalsVisibleTo Include="Arch.Tests" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="System" />
|
||||
<Using Include="System.Collections" />
|
||||
<Using Include="System.Collections.Generic" />
|
||||
<Using Include="System.Diagnostics" />
|
||||
<Using Include="System.Diagnostics.CodeAnalysis" />
|
||||
<Using Include="System.IO" />
|
||||
<Using Include="System.Linq" />
|
||||
<Using Include="System.Runtime.CompilerServices" />
|
||||
<Using Include="System.Runtime.InteropServices" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include=".\Arch\src\Arch.SourceGen\Arch.SourceGen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Arch.LowLevel" Version="1.1.0" />
|
||||
<PackageReference Include="Collections.Pooled" Version="2.0.0-preview.27" />
|
||||
<PackageReference Include="CommunityToolkit.HighPerformance" Version="7.1.2" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="ZeroAllocJobScheduler" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Arch\src\Arch\**\*.cs">
|
||||
<Link>Arch\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
<Compile Remove="Arch\src\Arch\obj\**\*.cs" />
|
||||
<InternalsVisibleTo Include="Arch.Benchmarks" />
|
||||
<InternalsVisibleTo Include="Arch.Tests" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../MSBuild/Robust.Properties.targets" />
|
||||
|
||||
</Project>
|
||||
@@ -1,74 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
We actually set ManagePackageVersionsCentrally manually in another import file.
|
||||
Since .NET SDK 8.0.300, ManagePackageVersionsCentrally is automatically set if Directory.Packages.props exists.
|
||||
https://github.com/NuGet/NuGet.Client/pull/5572
|
||||
We actively negate this here, as we have some packages in tree we don't want such automatic behavior for.
|
||||
We use Directory.Build.props to get copy the state *after* our MSBuild config but before Nuget's config.
|
||||
-->
|
||||
<ManagePackageVersionsCentrally />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="ILReader.Core" Version="1.0.0.4" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.0" />
|
||||
<PackageVersion Include="Linguini.Bundle" Version="0.1.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeCoverage" Version="17.8.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.DotNet.RemoteExecutor" Version="8.0.0-beta.24059.4" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.ILVerification" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.70" />
|
||||
<PackageVersion Include="NUnit" Version="4.0.1" />
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="3.10.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageVersion Include="Nett" Version="0.15.0" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
|
||||
<PackageVersion Include="Pidgin" Version="3.2.2" />
|
||||
<PackageVersion Include="Robust.Natives" Version="0.1.1" />
|
||||
<PackageVersion Include="Robust.Natives.Cef" Version="120.1.9" />
|
||||
<PackageVersion Include="Robust.Shared.AuthLib" Version="0.1.2" />
|
||||
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.7" />
|
||||
<PackageVersion Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.7" />
|
||||
<PackageVersion Include="Serilog" Version="3.1.1" />
|
||||
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
|
||||
<PackageVersion Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
<PackageVersion Include="System.Memory" Version="4.5.5" />
|
||||
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.22621.5" />
|
||||
<PackageVersion Include="TerraFX.Interop.Xlib" Version="6.4.0" />
|
||||
<PackageVersion Include="VorbisPizza" Version="1.3.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="13.7.1" />
|
||||
<PackageVersion Include="prometheus-net" Version="8.2.1" />
|
||||
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
|
||||
<PackageVersion Include="PolySharp" Version="1.14.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
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 -->
|
||||
|
||||
|
||||
@@ -25,4 +25,7 @@
|
||||
|
||||
<!-- analyzer -->
|
||||
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- serialization generator -->
|
||||
<Import Project="Robust.Serialization.Generator.targets" />
|
||||
</Project>
|
||||
|
||||
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
861
RELEASE-NOTES.md
861
RELEASE-NOTES.md
@@ -54,13 +54,864 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 144.0.4
|
||||
## 182.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Add EntityUid's generation / version to the hashcode.
|
||||
|
||||
|
||||
## 144.0.3
|
||||
## 181.0.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix exceptions from having too many lights on screen and causing the game to go black.
|
||||
* Fix components having events raised in ClientGameStateManager before fully set and causing nullable reference exceptions.
|
||||
* Replace tile intersection IEnumerables with TileEnumerator internally. Also made it public for external callers that wish to avoid IEnumerable.
|
||||
|
||||
|
||||
## 144.0.2
|
||||
## 181.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix the non-generic HasComp and add a test for good measure.
|
||||
|
||||
|
||||
## 181.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Arch is merged refactoring how components are stored on engine. There's minimal changes on the API end to facilitate component nullability with much internal refactoring.
|
||||
|
||||
|
||||
## 180.2.1
|
||||
|
||||
|
||||
## 180.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add EnsureEntity variants that take in collections.
|
||||
* Add more MapSystem helper methods.
|
||||
|
||||
### Internal
|
||||
|
||||
* Cache some more PVS data to avoid re-allocating every tick.
|
||||
|
||||
|
||||
## 180.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add the map name to lsmap.
|
||||
* Add net.pool_size to CVars to control the message data pool size in Lidgren and to also toggle pooling.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix physics contraints causing enormous heap allocations.
|
||||
* Fix potential error when writing a runtime log.
|
||||
* Fix shape lookups for non-hard fixtures in EntityLookupSystem from 180.0.0
|
||||
|
||||
|
||||
## 180.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed some obsolete methods from EntityLookupSystem.
|
||||
|
||||
### New features
|
||||
|
||||
* PhysicsSystem.TryGetNearest now supports chain shapes.
|
||||
* Add IPhysShape methods to EntityLookupSystem rather than relying on AABB checks.
|
||||
* Add some more helper methods to SharedTransformSystem.
|
||||
* Add GetOrNew dictionary extension that also returns a bool on whether the key existed.
|
||||
* Add a GetAnchoredEntities overload that takes in a list.
|
||||
|
||||
### Other
|
||||
|
||||
* Use NetEntities for the F3 debug panel to align with command usage.
|
||||
|
||||
|
||||
## 179.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* EyeComponent.Eye is no longer nullable
|
||||
|
||||
### New features
|
||||
|
||||
* Light rendering can now be enabled or disable per eye.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Deserializing old maps with empty grid chunks should now just ignore those chunks.
|
||||
|
||||
### Other
|
||||
|
||||
* UnknownPrototypeException now also tells you the prototype kind instead of just the unkown ID.
|
||||
* Adding or removing networked components while resetting predicted entities now results in a more informative exception.
|
||||
|
||||
|
||||
## 178.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Most methods in ActorSystem have been moved to ISharedPlayerManager.
|
||||
* Several actor/player related components and events have been moved to shared.
|
||||
|
||||
### New features
|
||||
|
||||
* Added `NetListAsArray<T>.Value` to the sandbox whitelist
|
||||
|
||||
|
||||
## 177.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed toInsertXform and added containerXform in SharedContainerSystem.CanInsert.
|
||||
* Removed EntityQuery parameters from SharedContainerSystem.IsEntityOrParentInContainer.
|
||||
* Changed the signature of ContainsEntity in SharedTransformSystem to use Entity<T>.
|
||||
* Removed one obsoleted SharedTransformSystem.AnchorEntity method.
|
||||
* Changed signature of SharedTransformSystem.SetCoordinates to use Entity<T>.
|
||||
|
||||
### New features
|
||||
|
||||
* Added more Entity<T> query methods.
|
||||
* Added BeforeApplyState event to replay playback.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed inverted GetAllMapGrids map id check.
|
||||
* Fixed transform test warnings.
|
||||
* Fixed PlacementManager warnings.
|
||||
* Fixed reparenting bug for entities that are being deleted.
|
||||
|
||||
### Other
|
||||
|
||||
* Changed VerticalAlignment of RichTextLabel to Center to be consistent with Label.
|
||||
* Changed PVS error log to be a warning instead.
|
||||
* Marked insert and remove container methods as obsolete, added container system methods to replace them.
|
||||
* Marked TransformComponent.MapPosition as obsolete, added GetMapCoordinates system method to replace it.
|
||||
|
||||
### Internal
|
||||
|
||||
* Moved TryGetUi/TryToggleUi/ToggleUi/TryOpen/OpenUi/TryClose/CloseUi methods from UserInterfaceSystem to SharedUserInterfaceSystem.
|
||||
|
||||
|
||||
## 176.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Reverted audio rework temporarily until packaging is fixed.
|
||||
* Changes to Robust.Packaging to facilitate Content.Packaging ports from the python packaging scripts.
|
||||
|
||||
### New features
|
||||
|
||||
* Add a cvar for max game state buffer size.
|
||||
* Add an overload for GetEntitiesInRange that takes in a set.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PVS initial list capacity always being 0.
|
||||
* Fix replay lerp error spam.
|
||||
|
||||
|
||||
## 175.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed static SoundSystem.Play methods.
|
||||
* Moved IPlayingAudioStream onto AudioComponent and entities instead of an abstract stream.
|
||||
* IResourceCache is in shared and IClientResourceCache is the client version to use for textures.
|
||||
* Default audio attenuation changed from InverseDistanceClamped to LinearDistanceClamped.
|
||||
* Removed per-source audio attenuation.
|
||||
|
||||
### New features
|
||||
|
||||
* Add preliminary support for EFX Reverb presets + auxiliary slots; these are also entities.
|
||||
* Audio on grid entities is now attached to the grid.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* If an audio entity comes into PVS range its track will start at the relevant offset and not the beginning.
|
||||
* Z-Axis offset is considered for ReferenceDistance / MaxDistance for audio.
|
||||
* Audio will now pause if the attached entity is paused.
|
||||
|
||||
### Other
|
||||
|
||||
* Changed audio Z-Axis offset from -5m to -1m.
|
||||
|
||||
|
||||
## 174.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* ActorComponent has been moved to `Robust.Shared.Player` (namespace changed).
|
||||
|
||||
### New features
|
||||
|
||||
* Added `SpriteSystem.GetFrame()` method, which takes in an animated RSI and a time and returns a frame/texture.
|
||||
* Added `IRobustRandom.NextAngle()`
|
||||
|
||||
|
||||
## 173.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add physics chain shapes from Box2D.
|
||||
|
||||
|
||||
## 173.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove GridModifiedEvent in favor of TileChangedEvent.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix some grid rendering bugs where chunks don't get destroyed correctly.
|
||||
|
||||
|
||||
## 172.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove TryLifestage helper methods.
|
||||
* Refactor IPlayerManager to remove more IPlayerSession, changed PlayerAttachedEvent etc on client to have the Local prefix, and shuffled namespaces around.
|
||||
|
||||
### New features
|
||||
|
||||
* Add EnsureComponent(ref Entity<\T?>)
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Re-add force ask threshold and fix other PVS bugs.
|
||||
|
||||
|
||||
## 171.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Change PlaceNextTo method names to be more descriptive.
|
||||
* Rename RefreshRelay for joints to SetRelay to match its behaviour.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PVS error spam for joint relays not being cleaned up.
|
||||
|
||||
### Other
|
||||
|
||||
* Set EntityLastModifiedTick on entity spawn.
|
||||
|
||||
|
||||
## 170.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed obsolete methods and properties in VisibilitySystem, SharedContainerSystem and MetaDataComponent.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed duplicate command error.
|
||||
* Fixed not being able to delete individual entities with the delete command.
|
||||
|
||||
### Other
|
||||
|
||||
* FileLogHandler logs can now be deleted while the engine is running.
|
||||
|
||||
|
||||
## 169.0.1
|
||||
|
||||
### Other
|
||||
|
||||
* The client now knows about registered server-side toolshed commands.
|
||||
|
||||
## 169.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Entity<T> has been introduced to hold a component and its owning entity. Some methods that returned and accepted components directly have been removed or obsoleted to reflect this.
|
||||
|
||||
### Other
|
||||
|
||||
* By-value events may now be subscribed to by-ref.
|
||||
* The manifest's assemblyPrefix value is now respected on the server.
|
||||
|
||||
|
||||
## 168.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The Component.OnRemove method has been removed. Use SubscribeLocalEvent<TComp, ComponentRemove>(OnRemove) from an EntitySystem instead.
|
||||
|
||||
|
||||
## 167.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove ComponentExtensions.
|
||||
* Remove ContainerHelpers.
|
||||
* Change some TransformSystem methods to fix clientside lerping.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed PVS bugs from dropped entity states.
|
||||
|
||||
### Other
|
||||
|
||||
* Add more joint debug asserts.
|
||||
|
||||
|
||||
## 166.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* EntityUid-NetEntity conversion methods now return null when given a null value, rather than returning an invalid id.
|
||||
* ExpandPvsEvent now defaults to using null lists to reduce allocations.
|
||||
* Various component lifestage related methods have been moved from the `Component` class to `EntityManager`.
|
||||
* Session/client specific PVS overrides are now always recursive, which means that all children of the overriden entity will also get sent.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a SortedSet yaml serializer.
|
||||
|
||||
### Other
|
||||
|
||||
* AddComponentUninitialized is now marked as obsolete and will be removed in the future.
|
||||
* DebugTools.AssertOwner() now accepts null components.
|
||||
|
||||
|
||||
## 165.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The arguments of `SplitContainer`s resize-finished event have changed.
|
||||
|
||||
### New features
|
||||
|
||||
* The YAML validator now checks the default values of ProtoId<T> and EntProtoId data fields.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* The minimum draggable area of split containers now blocks mouse inputs.
|
||||
|
||||
|
||||
## 164.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Make automatic component states infer cloneData.
|
||||
* Removed cloneData from AutoNetworkedFieldAttribute. This is now automatically inferred.
|
||||
|
||||
### Internal
|
||||
|
||||
* Reduce Transform GetComponents in RecursiveDeleteEntity.
|
||||
|
||||
|
||||
## 163.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Moved TimedDespawn to engine for a component that deletes the attached entity after a timer has elapsed.
|
||||
|
||||
### New features
|
||||
|
||||
* Add ExecuteCommand for integration tests.
|
||||
* Allow adding / removing widgets of cub-controls.
|
||||
* Give maps / grids a default name to help with debugging.
|
||||
* Use ToPrettyString in component resolve errors to help with debugging.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix console backspace exception.
|
||||
* Fix rendering invalid maps spamming exceptions every frame.
|
||||
|
||||
### Internal
|
||||
|
||||
* Move ClientGameStatemanager local variables to fields to avoid re-allocating every tick.
|
||||
|
||||
|
||||
## 162.2.1
|
||||
|
||||
|
||||
## 162.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add support for automatically networking entity lists and sets.
|
||||
* Add nullable conversion operators for ProtoIds.
|
||||
* Add LocId serializer for validation.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix deleting a contact inside of collision events throwing.
|
||||
* Localize VV.
|
||||
|
||||
### Internal
|
||||
|
||||
* Use CollectionsMarshal in GameStateManager.
|
||||
|
||||
|
||||
## 162.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixes "NoSpawn" entities appearing in the spawn menu.
|
||||
|
||||
|
||||
## 162.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Mark ProtoId as NetSerializable.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Temporarily revert NetForceAckThreshold change as it can lead to client stalling.
|
||||
* Fix eye visibility layers not updating on children when a parent changes.
|
||||
|
||||
### Internal
|
||||
|
||||
* Use CollectionsMarshal in RobustTree and AddComponentInternal.
|
||||
|
||||
|
||||
## 162.0.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add entity categories for prototypes and deprecate the `noSpawn` tag.
|
||||
* Add missing proxy method for `TryGetEntityData`.
|
||||
* Add NetForceAckThreshold cvar to forcibly update acks for late clients.
|
||||
|
||||
### Internal
|
||||
|
||||
* Use CollectionMarshals in PVS and DynamicTree.
|
||||
* Make the proxy methods use MetaQuery / TransformQuery.
|
||||
|
||||
|
||||
## 161.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add more DebugTools assert variations.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Don't attempt to insert entities into deleted containers.
|
||||
* Try to fix oldestAck not being set correctly leading to deletion history getting bloated for pvs.
|
||||
|
||||
|
||||
## 161.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Point light animations now need to use different component fields in order to animate the lights. `Enabled` should be replaced with `AnimatedEnable` and `Radius` should be replaced with `AnimatedRadius`
|
||||
|
||||
### New features
|
||||
|
||||
* EntProtoId is now net-serializable
|
||||
* Added print_pvs_ack command to debug PVS issues.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixes AngleTypeParser not using InvariantCulture
|
||||
* Fixed a bug that was causing `MetaDataComponent.LastComponentRemoved` to be updated improperly.
|
||||
|
||||
### Other
|
||||
|
||||
* The string representation of client-side entities now looks nicer and simply uses a 'c' prefix.
|
||||
|
||||
|
||||
## 160.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add optional MetaDataComponent args to Entitymanager methods.
|
||||
|
||||
### Internal
|
||||
|
||||
* Move _netComponents onto MetaDataComponent.
|
||||
* Remove some component resolves internally on adding / removing components.
|
||||
|
||||
|
||||
## 160.0.2
|
||||
|
||||
### Other
|
||||
|
||||
* Transform component and containers have new convenience fields to make using VIewVariables easier.
|
||||
|
||||
|
||||
## 160.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* ComponentReference has now been entirely removed.
|
||||
* Sensor / non-hard physics bodies are now included in EntityLookup by default.
|
||||
|
||||
|
||||
## 159.1.0
|
||||
|
||||
|
||||
## 159.0.3
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix potentially deleted entities having states re-applied when NetEntities come in.
|
||||
|
||||
|
||||
## 159.0.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PointLight state handling not queueing ComponentTree updates.
|
||||
|
||||
|
||||
## 159.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix pending entity states not being removed when coming in (only on entity deletion).
|
||||
|
||||
### Internal
|
||||
|
||||
* Remove PhysicsComponent ref from Fixture.
|
||||
|
||||
|
||||
## 159.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove ComponentReference from PointLights.
|
||||
* Move more of UserInterfaceSystem to shared.
|
||||
* Mark some EntitySystem proxy methods as protected instead of public.
|
||||
|
||||
### New features
|
||||
|
||||
* Make entity deletion take in a nullable EntityUid.
|
||||
* Added a method to send predicted messages via BUIs.
|
||||
|
||||
### Other
|
||||
|
||||
* Add Obsoletions to more sourcegen serv4 methods.
|
||||
* Remove inactive reviewers from CODEOWNERs.
|
||||
|
||||
|
||||
## 158.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove SharedEyeComponent.
|
||||
* Add Tile Overlay edge priority.
|
||||
|
||||
|
||||
## 157.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* UI tooltips now use rich text labels.
|
||||
|
||||
|
||||
## 157.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Unrevert container changes from 155.0.0.
|
||||
* Added server-client EntityUid separation. A given EntityUid will no longer refer to the same entity on the server & client.
|
||||
* EntityUid is no longer net-serializable, use NetEntity instead, EntityManager & entity systems have helper methods for converting between the two,
|
||||
|
||||
|
||||
## 156.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Revert container changes from 155.0.0.
|
||||
|
||||
|
||||
## 155.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* MapInitEvent now gets raised for components that get added to entities that have already been map-initialized.
|
||||
|
||||
### New features
|
||||
|
||||
* VirtualWritableDirProvider now supports file renaming/moving.
|
||||
* Added a new command for toggling the replay UI (`replay_toggleui`).
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed formatting of localization file errors.
|
||||
* Directed event subscriptions will no longer error if the corresponding component is queued for deletion.
|
||||
|
||||
|
||||
## 154.2.0
|
||||
|
||||
|
||||
|
||||
### New features
|
||||
|
||||
* Added support for advertising to multiple hubs simultaneously.
|
||||
* Added new functions to ContainerSystem that recursively look for a component on a contained entity's parents.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix Direction.TurnCw/TurnCcw to South returning Invalid.
|
||||
|
||||
|
||||
## 154.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add MathHelper.Max for TimeSpans.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Make joint initialisation only log under IsFirstTimePredicted on client.
|
||||
|
||||
### Other
|
||||
|
||||
* Mark the proxy Dirty(component) as obsolete in line with EntityManager (Dirty(EntityUid, Component) should be used in its place).
|
||||
|
||||
|
||||
## 154.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Change ignored prototypes to skip prototypes even if the prototype type is found.
|
||||
* Moved IPlayerData interface to shared.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a multiline text submit keybind function.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed multiline edits scrollbar margins.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added more event sources.
|
||||
* Made Toolshed types oneOff IoC injections.
|
||||
|
||||
|
||||
## 153.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed SharedUserInterfaceComponent component references.
|
||||
* Removed EntityDeletedMessage.
|
||||
|
||||
### Other
|
||||
|
||||
* Performance improvements for replay recording.
|
||||
* Lidgren has been updated to [v0.2.6](https://github.com/space-wizards/SpaceWizards.Lidgren.Network/blob/v0.2.6/RELEASE-NOTES.md).
|
||||
* Make EntityManager.AddComponent with a component instance set the owner if its default, add system proxy for it.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added some `EventSource` providers for PVS and replay recording: `Robust.Pvs` and `Robust.ReplayRecording`.
|
||||
* Added RecursiveMoveBenchmark.
|
||||
* Removed redundant prototype resolving.
|
||||
* Removed CollisionWake component removal subscription.
|
||||
* Removed redundant DebugTools.AssertNotNull(netId) in ClientGameStateManager
|
||||
|
||||
|
||||
## 152.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `Robust.Server.GameObjects.BoundUserInterface.InteractionRangeSqrd` is now a get-only property. Modify `InteractionRange` instead if you want to change it on active UIs.
|
||||
* Remove IContainerManager.
|
||||
* Remove and obsolete ComponentExt methods.
|
||||
* Remove EntityStarted and ComponentDeleted C# events.
|
||||
* Convert Tile.TypeId to an int. Old maps that were saved with TypeId being an ushort will still be properly deserialized.
|
||||
|
||||
### New features
|
||||
|
||||
* `BoundUserInterfaceCheckRangeEvent` can be used to implement custom logic for BUI range checks.
|
||||
* Add support for long values in CVars.
|
||||
* Allow user code to implement own logic for bound user interface range checks.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix timers counting down slower than real time and drifting.
|
||||
* Add missing System using statement to generated component states.
|
||||
* Fix build with USE_SYSTEM_SQLITE.
|
||||
* Fix prototype manager not being initialized in robust server simulation tests.
|
||||
* Fix not running serialization hooks when copying non-byref data definition fields without a custom type serializer.
|
||||
|
||||
### Other
|
||||
|
||||
* Remove warning for glibc 2.37.
|
||||
* Remove personally-identifiable file paths from client logs.
|
||||
|
||||
### Internal
|
||||
|
||||
* Disable obsoletion and inherited member hidden warnings in serialization source generated code.
|
||||
* Update CI workflows to use setup-dotnet 3.2.0 and checkout 3.6.0.
|
||||
* Fix entity spawn tests having instance per test lifecycle with a non static OneTimeTearDown method.
|
||||
* Add new PVS test to check that there is no issue with entity states referencing other entities that the client is not yet aware of.
|
||||
|
||||
|
||||
## 151.0.0
|
||||
|
||||
|
||||
## 150.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix some partial datadefs.
|
||||
|
||||
|
||||
## 150.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove the Id field from Fixtures as the Id is already stored on FixturesComponent.
|
||||
|
||||
### New features
|
||||
|
||||
* Add AbstractDictionarySerializer for abstract classes.
|
||||
* Add many new spawn functions for entities for common operations.
|
||||
|
||||
|
||||
## 149.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix serialization sharing instances when copying data definitions and not assigning null when the source is null.
|
||||
* Fixed resizing a window to be bigger than its set maxsize crashing the client.
|
||||
|
||||
|
||||
## 149.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Data definitions must now be partial, their data fields must not be readonly and their data field properties must have a setter.
|
||||
|
||||
### Internal
|
||||
|
||||
* Copying data definitions through the serialization manager is now faster and consumes less memory.
|
||||
|
||||
|
||||
## 148.4.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add recursive PVS overrides and remove IsOverride()
|
||||
|
||||
|
||||
## 148.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Happy eyeballs delay can be configured.
|
||||
* Added more colors.
|
||||
* Allow pre-startup components to be shut down.
|
||||
* Added tile texture reload command.
|
||||
* Add implementation of Random.Pick(ValueList<T> ..).
|
||||
* Add IntegrationInstance fields for common dependencies.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Prevent invalid prototypes from being spawned.
|
||||
* Change default value of EntityLastModifiedTick from zero to one.
|
||||
* Make DiscordRichPresence icon CVars server-side with replication.
|
||||
|
||||
|
||||
## 148.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* `SpinBox.LineEditControl` exposes the underlying `LineEdit`.
|
||||
* Add VV attributes to various fields across overlay and sessions.
|
||||
* Add IsPaused to EntityManager to check if an entity is paused.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix SetActiveTheme not updating the theme.
|
||||
|
||||
|
||||
## 148.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added IgnoreUIChecksComponent that lets entities ignore bound user interface range checks which would normally close the UI.
|
||||
* Add support for F16-F24 keybinds.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix gamestate bug where PVS is disabled.
|
||||
|
||||
### Other
|
||||
|
||||
* EntityQuery.HasComponent override for nullable entity uids.
|
||||
|
||||
|
||||
## 148.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Several NuGet dependencies are now private assets.
|
||||
* Added `IViewportControl.PixelToMap()` and `PixelToMapEvent`. These are variants of the existing screen-to-map functions that should account for distortion effects.
|
||||
|
||||
### New features
|
||||
|
||||
* Added several new rich-text tags, including italic and bold-italic.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed log messages for unknown components not working due to threaded IoC issues.
|
||||
* Replay recordings no longer record invalid prototype uploads.
|
||||
|
||||
|
||||
## 147.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Renamed one of the EntitySystem.Dirty() methods to `DirtyEntity()` to avoid confusion with the component-dirtying methods.
|
||||
|
||||
### New features
|
||||
|
||||
* Added debug commands that return the entity system update order.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed a bug in MetaDataSystem that was causing the metadata component to not be marked as dirty.
|
||||
|
||||
|
||||
## 146.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove readOnly for DataFields and rename some ShaderPrototype C# fields internally to align with the normal schema.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Add InvariantCulture to angle validation.
|
||||
|
||||
### Internal
|
||||
|
||||
* Add some additional EntityQuery<T> usages and remove a redundant CanCollide call on fixture shutdown.
|
||||
|
||||
|
||||
## 145.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed some old SpriteComponent data-fields ("rsi", and "layerDatums").
|
||||
|
||||
### New features
|
||||
|
||||
* Added `ActorSystem.TryGetActorFromUserId()`.
|
||||
* Added IPrototypeManager.EnumerateKinds().
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed SpriteSpecifierSerializer yaml validation not working properly.
|
||||
* Fixed IoC/Threading exceptions in `Resource.Load()`.
|
||||
* Fixed `TransformSystem.SetCoordinates()` throwing uninformative client-side errors.
|
||||
* Fixed `IResourceManager.ContentFileExists()` and `TryContentFileRead()` throwing exceptions on windows when trying to open a directory.
|
||||
|
||||
|
||||
## 144.0.1
|
||||
@@ -164,7 +1015,7 @@ END TEMPLATE-->
|
||||
* `IHttpClientHolder` holds a shared `HttpClient` for use by content. It has Happy Eyeballs fixed and an appropriate `User-Agent`.
|
||||
* Added `DataNode.ToString()`. Makes it easier to save yaml files and debug code.
|
||||
* Added some cvars to modify discord rich presence icons.
|
||||
* .ogg files now read the `Artist` and `Title` tags and make them available via new fields in `AudioStream`.
|
||||
* .ogg files now read the `Artist` and `Title` tags and make them available via new fields in `AudioStream`.
|
||||
* The default fragment shaders now have access to the local light level (`lowp vec3 lightSample`).
|
||||
* Added `IPrototypeManager.ValidateAllPrototypesSerializable()`, which can be used to check that all currently loaded prototypes can be serialised & deserialised.
|
||||
|
||||
@@ -173,7 +1024,7 @@ END TEMPLATE-->
|
||||
* Fix certain debug commands and tools crashing on non-SS14 RobustToolbox games due to a missing font.
|
||||
* Discord rich presence strings are now truncated if they are too long.
|
||||
* Fixed a couple of broadphase/entity-lookup update bugs that were affecting containers and entities attached to other (non-grid/map) entities.
|
||||
* Fixed `INetChannel.Disconnect()` not properly disconnecting clients in integration tests.
|
||||
* Fixed `INetChannel.Disconnect()` not properly disconnecting clients in integration tests.
|
||||
|
||||
### Other
|
||||
|
||||
|
||||
@@ -2203,3 +2203,207 @@
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: Arch
|
||||
license: |
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Lars Matthäus/genaray
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
- type: entity
|
||||
id: debugRotation
|
||||
abstract: true
|
||||
suffix: DEBUG
|
||||
categories: [ debug ]
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- type: uiTheme
|
||||
id: Default
|
||||
path: /Textures/Interface/Default
|
||||
path: /Textures/Interface/Default/
|
||||
colors:
|
||||
# Root
|
||||
rootBackground: "#000000"
|
||||
|
||||
17
Resources/EnginePrototypes/entityCategory.yml
Normal file
17
Resources/EnginePrototypes/entityCategory.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# debug related entities
|
||||
- type: entityCategory
|
||||
id: debug
|
||||
name: entity-category-name-debug
|
||||
description: entity-category-desc-debug
|
||||
|
||||
# entities that spawn other entities
|
||||
- type: entityCategory
|
||||
id: spawner
|
||||
name: entity-category-name-spawner
|
||||
description: entity-category-desc-spawner
|
||||
|
||||
# entities that should be hidden from the spawn menu
|
||||
- type: entityCategory
|
||||
id: hideSpawnMenu
|
||||
name: entity-category-name-hide
|
||||
description: entity-category-desc-hide
|
||||
@@ -17,15 +17,15 @@ cmd-error-dir-not-found = Could not find directory: {$dir}.
|
||||
cmd-failure-no-attached-entity = There is no entity attached to this shell.
|
||||
|
||||
## 'help' command
|
||||
cmd-oldhelp-desc = Display general help or help text for a specific command
|
||||
cmd-oldhelp-help = Usage: help [command name]
|
||||
cmd-help-desc = Display general help or help text for a specific command
|
||||
cmd-help-help = Usage: help [command name]
|
||||
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
|
||||
|
||||
cmd-oldhelp-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
cmd-oldhelp-unknown = Unknown command: { $command }
|
||||
cmd-oldhelp-top = { $command } - { $description }
|
||||
cmd-oldhelp-invalid-args = Invalid amount of arguments.
|
||||
cmd-oldhelp-arg-cmdname = [command name]
|
||||
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
cmd-help-unknown = Unknown command: { $command }
|
||||
cmd-help-top = { $command } - { $description }
|
||||
cmd-help-invalid-args = Invalid amount of arguments.
|
||||
cmd-help-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Gets or sets a CVar.
|
||||
@@ -558,3 +558,6 @@ cmd-vfs_ls-help = Usage: vfs_list <path>
|
||||
|
||||
cmd-vfs_ls-err-args = Need exactly 1 argument.
|
||||
cmd-vfs_ls-hint-path = <path>
|
||||
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
|
||||
cmd-reloadtiletextures-help = Usage: reloadtiletextures
|
||||
|
||||
8
Resources/Locale/en-US/entity-category.ftl
Normal file
8
Resources/Locale/en-US/entity-category.ftl
Normal file
@@ -0,0 +1,8 @@
|
||||
entity-category-name-debug = Debug
|
||||
entity-category-desc-debug = Entity prototypes intended for debugging & testing.
|
||||
|
||||
entity-category-name-spawner = Spawner
|
||||
entity-category-desc-spawner = Entity prototypes that spawn other entities.
|
||||
|
||||
entity-category-name-hide = Hidden
|
||||
entity-category-desc-hide = Entity prototypes that should be hidden from the spawn menu
|
||||
@@ -18,6 +18,15 @@ input-key-F12 = F12
|
||||
input-key-F13 = F13
|
||||
input-key-F14 = F14
|
||||
input-key-F15 = F15
|
||||
input-key-F16 = F16
|
||||
input-key-F17 = F17
|
||||
input-key-F18 = F18
|
||||
input-key-F19 = F19
|
||||
input-key-F20 = F20
|
||||
input-key-F21 = F21
|
||||
input-key-F22 = F22
|
||||
input-key-F23 = F23
|
||||
input-key-F24 = F24
|
||||
input-key-Pause = Pause
|
||||
input-key-Left = Left
|
||||
input-key-Up = Up
|
||||
|
||||
@@ -161,3 +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.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
## ViewVariablesInstanceEntity
|
||||
|
||||
view-variables = View Variables
|
||||
view-variable-instance-entity-server-components-add-component-button-placeholder = Add Component
|
||||
view-variable-instance-entity-client-variables-tab-title = Client Variables
|
||||
view-variable-instance-entity-client-components-tab-title = Client Components
|
||||
@@ -8,4 +9,4 @@ view-variable-instance-entity-server-components-tab-title = Server Components
|
||||
view-variable-instance-entity-client-components-search-bar-placeholder = Search
|
||||
view-variable-instance-entity-server-components-search-bar-placeholder = Search
|
||||
view-variable-instance-entity-add-window-server-components = Add Component [S]
|
||||
view-variable-instance-entity-add-window-client-components = Add Component [C]
|
||||
view-variable-instance-entity-add-window-client-components = Add Component [C]
|
||||
|
||||
@@ -23,16 +23,6 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
"Make sure that methods subscribing to a ref event have the ref keyword for the event argument."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor ByValueEventSubscribedByRefRule = new(
|
||||
Diagnostics.IdValueEventRaisedByRef,
|
||||
"Value event subscribed to by-ref",
|
||||
"Tried to subscribe to a value event '{0}' by-ref.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure that methods subscribing to value events do not have the ref keyword for the event argument."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor ByRefEventRaisedByValueRule = new(
|
||||
Diagnostics.IdByRefEventRaisedByValue,
|
||||
"By-ref event raised by value",
|
||||
@@ -55,7 +45,6 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
ByRefEventSubscribedByValueRule,
|
||||
ByValueEventSubscribedByRefRule,
|
||||
ByRefEventRaisedByValueRule,
|
||||
ByValueEventRaisedByRefRule
|
||||
);
|
||||
@@ -64,71 +53,9 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterOperationAction(CheckEventSubscription, OperationKind.Invocation);
|
||||
context.RegisterOperationAction(CheckEventRaise, OperationKind.Invocation);
|
||||
}
|
||||
|
||||
private void CheckEventSubscription(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation operation)
|
||||
return;
|
||||
|
||||
var subscribeMethods = context.Compilation
|
||||
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
|
||||
.GetMembers()
|
||||
.Where(m => m.Name.Contains("SubscribeLocalEvent"))
|
||||
.Cast<IMethodSymbol>();
|
||||
|
||||
if (subscribeMethods == null)
|
||||
return;
|
||||
|
||||
if (!subscribeMethods.Any(m => m.Equals(operation.TargetMethod.OriginalDefinition, Default)))
|
||||
return;
|
||||
|
||||
var typeArguments = operation.TargetMethod.TypeArguments;
|
||||
if (typeArguments.Length < 1 || typeArguments.Length > 2)
|
||||
return;
|
||||
|
||||
if (operation.Arguments.First().Value is not IDelegateCreationOperation delegateCreation)
|
||||
return;
|
||||
|
||||
if (delegateCreation.Target is not IMethodReferenceOperation methodReference)
|
||||
return;
|
||||
|
||||
var eventParameter = methodReference.Method.Parameters.LastOrDefault();
|
||||
if (eventParameter == null)
|
||||
return;
|
||||
|
||||
ITypeSymbol eventArgument;
|
||||
switch (typeArguments.Length)
|
||||
{
|
||||
case 1:
|
||||
eventArgument = typeArguments[0];
|
||||
break;
|
||||
case 2:
|
||||
eventArgument = typeArguments[1];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var byRefAttribute = context.Compilation.GetTypeByMetadataName(ByRefAttribute);
|
||||
if (byRefAttribute == null)
|
||||
return;
|
||||
|
||||
var isByRefEventType = eventArgument
|
||||
.GetAttributes()
|
||||
.Any(attribute => attribute.AttributeClass?.Equals(byRefAttribute, Default) ?? false);
|
||||
var parameterIsRef = eventParameter.RefKind == RefKind.Ref;
|
||||
|
||||
if (isByRefEventType != parameterIsRef)
|
||||
{
|
||||
var descriptor = isByRefEventType ? ByRefEventSubscribedByValueRule : ByValueEventSubscribedByRefRule;
|
||||
var diagnostic = Diagnostic.Create(descriptor, operation.Syntax.GetLocation(), eventArgument);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckEventRaise(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation operation)
|
||||
|
||||
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
||||
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
||||
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
|
||||
Diagnostics.IdDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} is a DataDefinition but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type that is a data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
|
||||
Diagnostics.IdNestedDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} contains nested data definition {1} but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type containing a nested data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
|
||||
Diagnostics.IdDataFieldWritable,
|
||||
"Data field must not be readonly",
|
||||
"Data field {0} in data definition {1} is readonly.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to remove the readonly modifier."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
|
||||
Diagnostics.IdDataFieldPropertyWritable,
|
||||
"Data field property must have a setter",
|
||||
"Data field property {0} in data definition {1} does not have a setter.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to add a setter."
|
||||
);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
|
||||
);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.ClassDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.StructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordStructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.InterfaceDeclaration);
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataField, SyntaxKind.FieldDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataFieldProperty, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
private void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not TypeDeclarationSyntax declaration)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(declaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
if (!IsPartial(declaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataDefinitionPartialRule, declaration.Keyword.GetLocation(), type.Name));
|
||||
}
|
||||
|
||||
var containingType = type.ContainingType;
|
||||
while (containingType != null)
|
||||
{
|
||||
var containingTypeDeclaration = (TypeDeclarationSyntax) containingType.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
if (!IsPartial(containingTypeDeclaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(NestedDataDefinitionPartialRule, containingTypeDeclaration.Keyword.GetLocation(), containingType.Name, type.Name));
|
||||
}
|
||||
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not FieldDeclarationSyntax field)
|
||||
return;
|
||||
|
||||
var typeDeclaration = field.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
foreach (var variable in field.Declaration.Variables)
|
||||
{
|
||||
var fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
|
||||
if (fieldSymbol == null)
|
||||
continue;
|
||||
|
||||
if (IsReadOnlyDataField(type, fieldSymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not PropertyDeclarationSyntax property)
|
||||
return;
|
||||
|
||||
var typeDeclaration = property.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type) || type.IsRecord || type.IsValueType)
|
||||
return;
|
||||
|
||||
var propertySymbol = context.SemanticModel.GetDeclaredSymbol(property);
|
||||
if (propertySymbol == null)
|
||||
return;
|
||||
|
||||
if (IsReadOnlyDataField(type, propertySymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
|
||||
{
|
||||
if (!IsDataField(field, out _, out _))
|
||||
return false;
|
||||
|
||||
return IsReadOnlyMember(type, field);
|
||||
}
|
||||
|
||||
private static bool IsPartial(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.Modifiers.IndexOf(SyntaxKind.PartialKeyword) != -1;
|
||||
}
|
||||
|
||||
private static bool IsDataDefinition(ITypeSymbol? type)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
return HasAttribute(type, DataDefinitionNamespace) ||
|
||||
IsImplicitDataDefinition(type);
|
||||
}
|
||||
|
||||
private static bool IsDataField(ISymbol member, out ITypeSymbol type, out AttributeData attribute)
|
||||
{
|
||||
// TODO data records and other attributes
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
foreach (var attr in field.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = field.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
foreach (var attr in property.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = property.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = null!;
|
||||
attribute = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool Inherits(ITypeSymbol type, string parent)
|
||||
{
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (baseType.ToDisplayString() == parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
|
||||
{
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
return field.IsReadOnly;
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
if (property.SetMethod == null)
|
||||
return true;
|
||||
|
||||
if (property.SetMethod.IsInitOnly)
|
||||
return type.IsReferenceType;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasAttribute(ITypeSymbol type, string attributeName)
|
||||
{
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (HasAttribute(baseType, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var @interface in type.AllInterfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinitionInterface(ITypeSymbol @interface)
|
||||
{
|
||||
if (HasAttribute(@interface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var subInterface in @interface.AllInterfaces)
|
||||
{
|
||||
if (HasAttribute(subInterface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
yield return baseType;
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
||||
using static Robust.Analyzers.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public sealed class DefinitionFixer : CodeFixProvider
|
||||
{
|
||||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
|
||||
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
|
||||
);
|
||||
|
||||
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
foreach (var diagnostic in context.Diagnostics)
|
||||
{
|
||||
switch (diagnostic.Id)
|
||||
{
|
||||
case IdDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdNestedDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdDataFieldWritable:
|
||||
return RegisterDataFieldFix(context, diagnostic);
|
||||
case IdDataFieldPropertyWritable:
|
||||
return RegisterDataFieldPropertyFix(context, diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
private static async Task RegisterPartialTypeFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<TypeDeclarationSyntax>().First();
|
||||
|
||||
if (token == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make type partial",
|
||||
c => MakeDataDefinitionPartial(context.Document, token, c),
|
||||
"Make type partial"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeDataDefinitionPartial(Document document, TypeDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = SyntaxFactory.Token(PartialKeyword);
|
||||
var newDeclaration = declaration.AddModifiers(token);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var field = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (field == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakeFieldWritable(context.Document, field, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldPropertyFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var property = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<PropertyDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakePropertyWritable(context.Document, property, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeFieldWritable(Document document, FieldDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = declaration.Modifiers.First(t => t.IsKind(ReadOnlyKeyword));
|
||||
var newDeclaration = declaration.WithModifiers(declaration.Modifiers.Remove(token));
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakePropertyWritable(Document document, PropertyDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var newDeclaration = declaration;
|
||||
var privateSet = newDeclaration
|
||||
.AccessorList?
|
||||
.Accessors
|
||||
.FirstOrDefault(s => s.IsKind(SetAccessorDeclaration) || s.IsKind(InitAccessorDeclaration));
|
||||
|
||||
if (newDeclaration.AccessorList != null && privateSet != null)
|
||||
{
|
||||
newDeclaration = newDeclaration.WithAccessorList(
|
||||
newDeclaration.AccessorList.WithAccessors(
|
||||
newDeclaration.AccessorList.Accessors.Remove(privateSet)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
AccessorDeclarationSyntax setter;
|
||||
if (declaration.Modifiers.Any(m => m.IsKind(PrivateKeyword)))
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
SyntaxFactory.TokenList(SyntaxFactory.Token(PrivateKeyword)),
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
|
||||
newDeclaration = newDeclaration.AddAccessorListAccessors(setter);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,12 @@ public static class Diagnostics
|
||||
public const string IdInvalidNotNullableFlagType = "RA0011";
|
||||
public const string IdNotNullableFlagValueType = "RA0012";
|
||||
public const string IdByRefEventSubscribedByValue = "RA0013";
|
||||
public const string IdValueEventSubscribedByRef = "RA0014";
|
||||
public const string IdByRefEventRaisedByValue = "RA0015";
|
||||
public const string IdValueEventRaisedByRef = "RA0016";
|
||||
public const string IdDataDefinitionPartial = "RA0017";
|
||||
public const string IdNestedDataDefinitionPartial = "RA0018";
|
||||
public const string IdDataFieldWritable = "RA0019";
|
||||
public const string IdDataFieldPropertyWritable = "RA0020";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
177
Robust.Benchmarks/Arch/ArchComponentAccessBenchmark.cs
Normal file
177
Robust.Benchmarks/Arch/ArchComponentAccessBenchmark.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Engines;
|
||||
using Robust.Shared.Analyzers;
|
||||
using static Robust.Benchmarks.EntityManager.ArchetypeComponentAccessBenchmark;
|
||||
|
||||
namespace Robust.Benchmarks.Arch;
|
||||
|
||||
[MemoryDiagnoser]
|
||||
[Virtual]
|
||||
public class ArchComponentAccessBenchmark
|
||||
{
|
||||
private const int N = 10000;
|
||||
|
||||
private static readonly Consumer Consumer = new();
|
||||
private Entity _entity;
|
||||
private World _world = default!;
|
||||
private QueryDescription _singleQuery;
|
||||
private QueryDescription _tenQuery;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
var _ = new JobScheduler.JobScheduler("ArchBenchmark");
|
||||
|
||||
_world = World.Create();
|
||||
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
var entity = _world.Create();
|
||||
|
||||
// Randomly chosen id
|
||||
if (entity.Id == 1584)
|
||||
_entity = entity;
|
||||
|
||||
_world.Add(
|
||||
entity,
|
||||
new Struct1(),
|
||||
new Struct2(),
|
||||
new Struct3(),
|
||||
new Struct4(),
|
||||
new Struct5(),
|
||||
new Struct6(),
|
||||
new Struct7(),
|
||||
new Struct8(),
|
||||
new Struct9(),
|
||||
new Struct10()
|
||||
);
|
||||
}
|
||||
|
||||
_singleQuery = new QueryDescription().WithAll<Struct1>();
|
||||
_tenQuery = new QueryDescription().WithAll<Struct1, Struct2, Struct3, Struct4, Struct5, Struct6, Struct7, Struct8, Struct9, Struct10>();
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public void GlobalCleanup()
|
||||
{
|
||||
JobScheduler.JobScheduler.Instance.Dispose();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public Struct1 GetSingle()
|
||||
{
|
||||
return _world.Get<Struct1>(_entity);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public (Struct1, Struct2, Struct3, Struct4, Struct5, Struct6, Struct7, Struct8, Struct9, Struct10)
|
||||
GetTen()
|
||||
{
|
||||
return (
|
||||
_world.Get<Struct1>(_entity),
|
||||
_world.Get<Struct2>(_entity),
|
||||
_world.Get<Struct3>(_entity),
|
||||
_world.Get<Struct4>(_entity),
|
||||
_world.Get<Struct5>(_entity),
|
||||
_world.Get<Struct6>(_entity),
|
||||
_world.Get<Struct7>(_entity),
|
||||
_world.Get<Struct8>(_entity),
|
||||
_world.Get<Struct9>(_entity),
|
||||
_world.Get<Struct10>(_entity)
|
||||
);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool HasSingle()
|
||||
{
|
||||
return _world.Has<Struct1>(_entity);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool HasTen()
|
||||
{
|
||||
return _world.Has<Struct1>(_entity) &&
|
||||
_world.Has<Struct2>(_entity) &&
|
||||
_world.Has<Struct3>(_entity) &&
|
||||
_world.Has<Struct4>(_entity) &&
|
||||
_world.Has<Struct5>(_entity) &&
|
||||
_world.Has<Struct6>(_entity) &&
|
||||
_world.Has<Struct7>(_entity) &&
|
||||
_world.Has<Struct8>(_entity) &&
|
||||
_world.Has<Struct9>(_entity) &&
|
||||
_world.Has<Struct10>(_entity);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateSingle()
|
||||
{
|
||||
_world.Query(_singleQuery, static (ref Struct1 s) => Consumer.Consume(s));
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateSingleInline()
|
||||
{
|
||||
_world.InlineQuery<QueryConsumer>(_singleQuery);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateSingleParallel()
|
||||
{
|
||||
_world.ParallelQuery(_singleQuery, static (ref Struct1 s) => Consumer.Consume(s));
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateSingleInlineParallel()
|
||||
{
|
||||
_world.InlineParallelQuery<QueryConsumer>(_singleQuery);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateTen()
|
||||
{
|
||||
_world.Query(_tenQuery,
|
||||
static (
|
||||
ref Struct1 s1, ref Struct2 s2, ref Struct3 s3, ref Struct4 s4,
|
||||
ref Struct5 s5, ref Struct6 s6, ref Struct7 s7, ref Struct8 s8,
|
||||
ref Struct9 s9, ref Struct10 s10) =>
|
||||
Consumer.Consume((s1, s2, s3, s4, s5, s6, s7, s8, s9, s10)));
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateTenInline()
|
||||
{
|
||||
_world.InlineQuery<QueryConsumer>(_tenQuery);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateTenParallel()
|
||||
{
|
||||
_world.ParallelQuery(_tenQuery,
|
||||
static (
|
||||
ref Struct1 s1, ref Struct2 s2, ref Struct3 s3, ref Struct4 s4,
|
||||
ref Struct5 s5, ref Struct6 s6, ref Struct7 s7, ref Struct8 s8,
|
||||
ref Struct9 s9, ref Struct10 s10) =>
|
||||
Consumer.Consume((s1, s2, s3, s4, s5, s6, s7, s8, s9, s10)));
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IterateTenInlineParallel()
|
||||
{
|
||||
_world.InlineParallelQuery<QueryConsumer>(_tenQuery);
|
||||
}
|
||||
|
||||
private struct QueryConsumer : IForEach
|
||||
{
|
||||
private static readonly Consumer Consumer = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Update(Entity entity)
|
||||
{
|
||||
Consumer.Consume(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class AddRemoveComponentBenchmark
|
||||
public partial class AddRemoveComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -41,14 +41,14 @@ public class AddRemoveComponentBenchmark
|
||||
{
|
||||
for (var i = 2; i <= N+1; i++)
|
||||
{
|
||||
var uid = new EntityUid(i);
|
||||
var uid = new EntityUid(i, -1);
|
||||
_entityManager.AddComponent<A>(uid);
|
||||
_entityManager.RemoveComponent<A>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ComponentIndexBenchmark
|
||||
private static class CompArrayIndex<T>
|
||||
{
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
public static readonly CompIdx Idx = new(Interlocked.Increment(ref _compIndexMaster));
|
||||
public static readonly CompIdx Idx = new(Interlocked.Increment(ref _compIndexMaster), typeof(T));
|
||||
}
|
||||
|
||||
private static CompIdx GetCompIdIndex(Type type)
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
public class ComponentIteratorBenchmark
|
||||
public partial class ComponentIteratorBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -69,7 +69,7 @@ public class ComponentIteratorBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Analyzers;
|
||||
@@ -9,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class GetComponentBenchmark
|
||||
public partial class GetComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -47,7 +46,7 @@ public class GetComponentBenchmark
|
||||
{
|
||||
for (var i = 2; i <= N+1; i++)
|
||||
{
|
||||
Comps[i] = _entityManager.GetComponent<A>(new EntityUid(i));
|
||||
Comps[i] = _entityManager.GetComponent<A>(new EntityUid(i, -1));
|
||||
}
|
||||
|
||||
// Return something so the JIT doesn't optimize out all the GetComponent calls.
|
||||
@@ -55,7 +54,7 @@ public class GetComponentBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class SpawnDeleteEntityBenchmark
|
||||
public partial class SpawnDeleteEntityBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -56,7 +56,7 @@ public class SpawnDeleteEntityBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<NoWarn>RA0003</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Arch\Arch.csproj" />
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
[Virtual]
|
||||
public class DataDefinitionWithString
|
||||
public partial class DataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed class SealedDataDefinitionWithString
|
||||
public sealed partial class SealedDataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; private set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -10,8 +11,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
/// Arbitrarily large data definition for benchmarks.
|
||||
/// Taken from content.
|
||||
/// </summary>
|
||||
[Prototype("seed")]
|
||||
public sealed class SeedDataDefinition : IPrototype
|
||||
public sealed partial class SeedDataDefinition : Component
|
||||
{
|
||||
public const string Prototype = @"
|
||||
- type: seed
|
||||
@@ -106,7 +106,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public struct SeedChemQuantity
|
||||
public partial struct SeedChemQuantity
|
||||
{
|
||||
[DataField("Min")]
|
||||
public int Min;
|
||||
|
||||
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.CreateGridEntity(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
_mapCoords = new EntityCoordinates(map, 100, 100);
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
|
||||
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
|
||||
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
|
||||
_ent = _entMan.Spawn();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Robust.Client.Configuration;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
@@ -10,13 +8,12 @@ using Robust.Client.Utility;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -65,12 +62,12 @@ namespace Robust.Client
|
||||
|
||||
_configManager.OnValueChanged(CVars.NetTickrate, TickRateChanged, invokeImmediately: true);
|
||||
|
||||
_playMan.Initialize();
|
||||
_playMan.Initialize(0);
|
||||
_playMan.PlayerListUpdated += OnPlayerListUpdated;
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void OnPlayerListUpdated(object? sender, EventArgs e)
|
||||
private void OnPlayerListUpdated()
|
||||
{
|
||||
var serverPlayers = _playMan.PlayerCount;
|
||||
if (_net.ServerChannel != null && GameInfo != null && _net.IsConnected)
|
||||
@@ -130,9 +127,10 @@ namespace Robust.Client
|
||||
{
|
||||
DebugTools.Assert(RunLevel < ClientRunLevel.Connecting);
|
||||
DebugTools.Assert(!_net.IsConnected);
|
||||
_playMan.Startup();
|
||||
_playMan.LocalPlayer!.Name = PlayerNameOverride ?? _configManager.GetCVar(CVars.PlayerName);
|
||||
var name = PlayerNameOverride ?? _configManager.GetCVar(CVars.PlayerName);
|
||||
_playMan.SetupSinglePlayer(name);
|
||||
OnRunLevelChanged(ClientRunLevel.SinglePlayerGame);
|
||||
_playMan.JoinGame(_playMan.LocalSession!);
|
||||
GameStartedSetup();
|
||||
}
|
||||
|
||||
@@ -173,22 +171,14 @@ namespace Robust.Client
|
||||
info.ServerName = serverName;
|
||||
}
|
||||
|
||||
var maxPlayers = _configManager.GetCVar<int>("game.maxplayers");
|
||||
info.ServerMaxPlayers = maxPlayers;
|
||||
|
||||
var userName = _net.ServerChannel!.UserName;
|
||||
var userId = _net.ServerChannel.UserId;
|
||||
var channel = _net.ServerChannel!;
|
||||
|
||||
// start up player management
|
||||
_playMan.Startup();
|
||||
|
||||
_playMan.LocalPlayer!.UserId = userId;
|
||||
_playMan.LocalPlayer.Name = userName;
|
||||
|
||||
_playMan.LocalPlayer.StatusChanged += OnLocalStatusChanged;
|
||||
_playMan.SetupMultiplayer(channel);
|
||||
_playMan.PlayerStatusChanged += OnStatusChanged;
|
||||
|
||||
var serverPlayers = _playMan.PlayerCount;
|
||||
_discord.Update(info.ServerName, userName, info.ServerMaxPlayers.ToString(), serverPlayers.ToString());
|
||||
_discord.Update(info.ServerName, channel.UserName, info.ServerMaxPlayers.ToString(), serverPlayers.ToString());
|
||||
|
||||
}
|
||||
|
||||
@@ -221,6 +211,8 @@ namespace Robust.Client
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
_configManager.ReceivedInitialNwVars -= OnReceivedClientData;
|
||||
_playMan.PlayerStatusChanged -= OnStatusChanged;
|
||||
_configManager.ClearReceivedInitialNwVars();
|
||||
OnRunLevelChanged(ClientRunLevel.Initialize);
|
||||
}
|
||||
@@ -263,19 +255,17 @@ namespace Robust.Client
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void OnLocalStatusChanged(object? obj, StatusEventArgs eventArgs)
|
||||
private void OnStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.Session != _playMan.LocalSession)
|
||||
return;
|
||||
|
||||
// player finished fully connecting to the server.
|
||||
// OldStatus is used here because it can go from connecting-> connected or connecting-> ingame
|
||||
if (eventArgs.OldStatus == SessionStatus.Connecting)
|
||||
{
|
||||
OnPlayerJoinedServer(_playMan.LocalPlayer!.Session);
|
||||
}
|
||||
|
||||
if (eventArgs.NewStatus == SessionStatus.InGame)
|
||||
{
|
||||
OnPlayerJoinedGame(_playMan.LocalPlayer!.Session);
|
||||
}
|
||||
if (e.OldStatus == SessionStatus.Connecting)
|
||||
OnPlayerJoinedServer(e.Session);
|
||||
else if (e.NewStatus == SessionStatus.InGame)
|
||||
OnPlayerJoinedGame(e.Session);
|
||||
}
|
||||
|
||||
private void OnRunLevelChanged(ClientRunLevel newRunLevel)
|
||||
|
||||
@@ -37,7 +37,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Replays;
|
||||
@@ -68,6 +68,7 @@ namespace Robust.Client
|
||||
deps.Register<IComponentFactory, ComponentFactory>();
|
||||
deps.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<ClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<GameController, GameController>();
|
||||
deps.Register<IGameController, GameController>();
|
||||
deps.Register<IGameControllerInternal, GameController>();
|
||||
|
||||
@@ -2,11 +2,13 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
public sealed partial class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
{
|
||||
[ViewVariables]
|
||||
public DynamicTree<ComponentTreeEntry<PointLightComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Physics;
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
public sealed partial class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
{
|
||||
public DynamicTree<ComponentTreeEntry<SpriteComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -22,13 +22,11 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = EntityUid.Parse(args[0]);
|
||||
var netEntity = NetEntity.Parse(args[0]);
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
var componentName = args[1];
|
||||
|
||||
var component = (Component) _componentFactory.GetComponent(componentName);
|
||||
|
||||
component.Owner = entity;
|
||||
|
||||
var component = _componentFactory.GetComponent(componentName);
|
||||
_entityManager.AddComponent(entity, component);
|
||||
}
|
||||
}
|
||||
@@ -49,7 +47,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var entityUid = EntityUid.Parse(args[0]);
|
||||
var netEntity = NetEntity.Parse(args[0]);
|
||||
var entityUid = _entityManager.GetEntity(netEntity);
|
||||
var componentName = args[1];
|
||||
|
||||
var registration = _componentFactory.GetRegistration(componentName);
|
||||
|
||||
@@ -78,14 +78,7 @@ namespace Robust.Client.Console.Commands
|
||||
message.Append($"net ID: {registration.NetID}");
|
||||
}
|
||||
|
||||
message.Append($", References:");
|
||||
|
||||
shell.WriteLine(message.ToString());
|
||||
|
||||
foreach (var type in registration.References)
|
||||
{
|
||||
shell.WriteLine($" {type}");
|
||||
}
|
||||
}
|
||||
catch (UnknownComponentException)
|
||||
{
|
||||
@@ -263,7 +256,7 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var uid = EntityUid.Parse(args[0]);
|
||||
var uid = EntityUid.Parse(args[0], "-1");
|
||||
var entmgr = _entityManager;
|
||||
if (!entmgr.EntityExists(uid))
|
||||
{
|
||||
@@ -296,6 +289,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class SnapGridGetCell : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public override string Command => "sggcell";
|
||||
@@ -310,7 +304,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
string indices = args[1];
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
if (!NetEntity.TryParse(args[0], out var gridNet))
|
||||
{
|
||||
shell.WriteError($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
@@ -322,7 +316,7 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (_map.TryGetGrid(gridUid, out var grid))
|
||||
if (_map.TryGetGrid(_entManager.GetEntity(gridNet), out var grid))
|
||||
{
|
||||
foreach (var entity in grid.GetAnchoredEntities(new Vector2i(
|
||||
int.Parse(indices.Split(',')[0], CultureInfo.InvariantCulture),
|
||||
@@ -430,6 +424,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class GridTileCount : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public override string Command => "gridtc";
|
||||
@@ -442,7 +437,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
if (!NetEntity.TryParse(args[0], out var gridUidNet) ||
|
||||
!_entManager.TryGetEntity(gridUidNet, out var gridUid))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
@@ -630,7 +626,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mousePos = _eye.ScreenToMap(_input.MouseScreenPosition);
|
||||
var mousePos = _eye.PixelToMap(_input.MouseScreenPosition);
|
||||
|
||||
if (!_map.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = GetType(args[0]);
|
||||
var type = Type.GetType(args[0]);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
@@ -25,17 +25,6 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteLine(sig);
|
||||
}
|
||||
}
|
||||
|
||||
private Type? GetType(string name)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.GetType(name) is { } type)
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
GC.Collect();
|
||||
|
||||
Span<EntityUid> ents = stackalloc EntityUid[amount];
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
@@ -50,12 +51,17 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
_entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
|
||||
ents[i] = _entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
MeasureProfiler.SaveData();
|
||||
|
||||
shell.WriteLine($"Client: Profiled spawning {amount} entities in {stopwatch.Elapsed.TotalMilliseconds:N3} ms");
|
||||
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
_entities.DeleteEntity(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#if DEBUG
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -8,7 +9,6 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
@@ -19,6 +19,7 @@ namespace Robust.Client.Debugging
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterface = default!;
|
||||
[Dependency] private readonly MapSystem _mapSystem = default!;
|
||||
|
||||
private Label? _label;
|
||||
|
||||
@@ -61,7 +62,7 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
|
||||
var mouseSpot = _inputManager.MouseScreenPosition;
|
||||
var spot = _eyeManager.ScreenToMap(mouseSpot);
|
||||
var spot = _eyeManager.PixelToMap(mouseSpot);
|
||||
|
||||
if (!_mapManager.TryFindGridAt(spot, out var gridUid, out var grid))
|
||||
{
|
||||
@@ -70,7 +71,7 @@ namespace Robust.Client.Debugging
|
||||
return;
|
||||
}
|
||||
|
||||
var tile = grid.GetTileRef(spot);
|
||||
var tile = _mapSystem.GetTileRef(gridUid, grid, spot);
|
||||
_label.Position = mouseSpot.Position + new Vector2(32, 0);
|
||||
|
||||
if (_hovered?.GridId == gridUid && _hovered?.Tile == tile) return;
|
||||
@@ -79,7 +80,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
var text = new StringBuilder();
|
||||
|
||||
foreach (var ent in grid.GetAnchoredEntities(spot))
|
||||
foreach (var ent in _mapSystem.GetAnchoredEntities(gridUid, grid, spot))
|
||||
{
|
||||
if (EntityManager.TryGetComponent<MetaDataComponent>(ent, out var meta))
|
||||
{
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
@@ -207,6 +206,7 @@ namespace Robust.Client.Debugging
|
||||
private readonly Font _font;
|
||||
|
||||
private HashSet<Joint> _drawnJoints = new();
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public PhysicsDebugOverlay(IEntityManager entityManager, IEyeManager eyeManager, IInputManager inputManager, IMapManager mapManager, IPlayerManager playerManager, IResourceCache cache, DebugPhysicsSystem system, EntityLookupSystem lookup, SharedPhysicsSystem physicsSystem)
|
||||
{
|
||||
@@ -231,32 +231,33 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody)) continue;
|
||||
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody.Owner);
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody);
|
||||
var comp = physBody.Comp;
|
||||
|
||||
const float AlphaModifier = 0.2f;
|
||||
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody.Owner).Fixtures.Values)
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody).Fixtures.Values)
|
||||
{
|
||||
// Invalid shape - Box2D doesn't check for IsSensor but we will for sanity.
|
||||
if (physBody.BodyType == BodyType.Dynamic && fixture.Density == 0f && fixture.Hard)
|
||||
if (comp.BodyType == BodyType.Dynamic && fixture.Density == 0f && fixture.Hard)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, Color.Red.WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if (!physBody.CanCollide)
|
||||
else if (!comp.CanCollide)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.5f, 0.5f, 0.3f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if (physBody.BodyType == BodyType.Static)
|
||||
else if (comp.BodyType == BodyType.Static)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.5f, 0.9f, 0.5f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if ((physBody.BodyType & (BodyType.Kinematic | BodyType.KinematicController)) != 0x0)
|
||||
else if ((comp.BodyType & (BodyType.Kinematic | BodyType.KinematicController)) != 0x0)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.5f, 0.5f, 0.9f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if (!physBody.Awake)
|
||||
else if (!comp.Awake)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.6f, 0.6f, 0.6f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
@@ -275,15 +276,18 @@ namespace Robust.Client.Debugging
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
var color = Color.Purple.WithAlpha(Alpha);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(physBody.Owner);
|
||||
worldHandle.DrawCircle(Transform.Mul(transform, physBody.LocalCenter), 0.2f, color);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(physBody);
|
||||
worldHandle.DrawCircle(Transform.Mul(transform, physBody.Comp.LocalCenter), 0.2f, color);
|
||||
}
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, viewBounds))
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(mapId, viewBounds, ref _grids);
|
||||
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var physBody = _entityManager.GetComponent<PhysicsComponent>(grid.Owner);
|
||||
var physBody = _entityManager.GetComponent<PhysicsComponent>(grid);
|
||||
var color = Color.Orange.WithAlpha(Alpha);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(grid.Owner);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(grid);
|
||||
worldHandle.DrawCircle(Transform.Mul(transform, physBody.LocalCenter), 1f, color);
|
||||
}
|
||||
}
|
||||
@@ -292,14 +296,14 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody)) continue;
|
||||
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody.Owner);
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody);
|
||||
|
||||
const float AlphaModifier = 0.2f;
|
||||
Box2? aabb = null;
|
||||
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody.Owner).Fixtures.Values)
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody).Fixtures.Values)
|
||||
{
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
{
|
||||
@@ -318,10 +322,11 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
_drawnJoints.Clear();
|
||||
|
||||
foreach (var jointComponent in _entityManager.EntityQuery<JointComponent>(true))
|
||||
var query = _entityManager.AllEntityQueryEnumerator<JointComponent>();
|
||||
while (query.MoveNext(out var uid, out var jointComponent))
|
||||
{
|
||||
if (jointComponent.JointCount == 0 ||
|
||||
!_entityManager.TryGetComponent(jointComponent.Owner, out TransformComponent? xf1) ||
|
||||
!_entityManager.TryGetComponent(uid, out TransformComponent? xf1) ||
|
||||
!viewAABB.Contains(xf1.WorldPosition)) continue;
|
||||
|
||||
foreach (var (_, joint) in jointComponent.Joints)
|
||||
@@ -361,6 +366,9 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugPhysicsSystem.PointCount = 0;
|
||||
}
|
||||
|
||||
worldHandle.UseShader(null);
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
|
||||
@@ -370,28 +378,31 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.ShapeInfo) != 0x0)
|
||||
{
|
||||
var hoverBodies = new List<PhysicsComponent>();
|
||||
var bounds = Box2.UnitCentered.Translated(_eyeManager.ScreenToMap(mousePos.Position).Position);
|
||||
var hoverBodies = new List<Entity<PhysicsComponent>>();
|
||||
var bounds = Box2.UnitCentered.Translated(_eyeManager.PixelToMap(mousePos.Position).Position);
|
||||
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, bounds))
|
||||
{
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
hoverBodies.Add(physBody);
|
||||
var uid = physBody.Owner;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(uid)) continue;
|
||||
hoverBodies.Add((uid, physBody));
|
||||
}
|
||||
|
||||
var lineHeight = _font.GetLineHeight(1f);
|
||||
var drawPos = mousePos.Position + new Vector2(20, 0) + new Vector2(0, -(hoverBodies.Count * 4 * lineHeight / 2f));
|
||||
int row = 0;
|
||||
|
||||
foreach (var body in hoverBodies)
|
||||
foreach (var bodyEnt in hoverBodies)
|
||||
{
|
||||
if (body != hoverBodies[0])
|
||||
if (bodyEnt != hoverBodies[0])
|
||||
{
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), "------");
|
||||
row++;
|
||||
}
|
||||
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {body.Owner}");
|
||||
var body = bodyEnt.Comp;
|
||||
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {bodyEnt.Owner}");
|
||||
row++;
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Layer: {Convert.ToString(body.CollisionLayer, 2)}");
|
||||
row++;
|
||||
@@ -404,7 +415,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Distance) != 0x0)
|
||||
{
|
||||
var mapPos = _eyeManager.ScreenToMap(mousePos);
|
||||
var mapPos = _eyeManager.PixelToMap(mousePos);
|
||||
|
||||
if (mapPos.MapId != args.MapId)
|
||||
return;
|
||||
@@ -430,6 +441,9 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screenHandle.UseShader(null);
|
||||
screenHandle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
@@ -451,11 +465,26 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
switch (fixture.Shape)
|
||||
{
|
||||
case ChainShape cShape:
|
||||
{
|
||||
var count = cShape.Count;
|
||||
var vertices = cShape.Vertices;
|
||||
|
||||
var v1 = Transform.Mul(xform, vertices[0]);
|
||||
for (var i = 1; i < count; ++i)
|
||||
{
|
||||
var v2 = Transform.Mul(xform, vertices[i]);
|
||||
worldHandle.DrawLine(v1, v2, color);
|
||||
v1 = v2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PhysShapeCircle circle:
|
||||
var center = Transform.Mul(xform, circle.Position);
|
||||
worldHandle.DrawCircle(center, circle.Radius, color);
|
||||
break;
|
||||
case EdgeShape edge:
|
||||
{
|
||||
var v1 = Transform.Mul(xform, edge.Vertex1);
|
||||
var v2 = Transform.Mul(xform, edge.Vertex2);
|
||||
worldHandle.DrawLine(v1, v2, color);
|
||||
@@ -465,6 +494,7 @@ namespace Robust.Client.Debugging
|
||||
worldHandle.DrawCircle(v1, 0.1f, color);
|
||||
worldHandle.DrawCircle(v2, 0.1f, color);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case PolygonShape poly:
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Robust.Client.WebViewHook;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
||||
@@ -167,7 +167,6 @@ namespace Robust.Client
|
||||
_reflectionManager.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDefaultPrototypes();
|
||||
_prototypeManager.ResolveResults();
|
||||
_userInterfaceManager.Initialize();
|
||||
_eyeManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
@@ -288,78 +287,6 @@ namespace Robust.Client
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceManifestData LoadResourceManifest()
|
||||
{
|
||||
// Parses /manifest.yml for game-specific settings that cannot be exclusively set up by content code.
|
||||
if (!_resourceCache.TryContentFileRead("/manifest.yml", out var stream))
|
||||
return ResourceManifestData.Default;
|
||||
|
||||
var yamlStream = new YamlStream();
|
||||
using (stream)
|
||||
{
|
||||
using var streamReader = new StreamReader(stream, EncodingHelpers.UTF8);
|
||||
yamlStream.Load(streamReader);
|
||||
}
|
||||
|
||||
if (yamlStream.Documents.Count == 0)
|
||||
return ResourceManifestData.Default;
|
||||
|
||||
if (yamlStream.Documents.Count != 1 || yamlStream.Documents[0].RootNode is not YamlMappingNode mapping)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Expected a single YAML document with root mapping for /manifest.yml");
|
||||
}
|
||||
|
||||
var modules = ReadStringArray(mapping, "modules") ?? Array.Empty<string>();
|
||||
|
||||
string? assemblyPrefix = null;
|
||||
if (mapping.TryGetNode("assemblyPrefix", out var prefixNode))
|
||||
assemblyPrefix = prefixNode.AsString();
|
||||
|
||||
string? defaultWindowTitle = null;
|
||||
if (mapping.TryGetNode("defaultWindowTitle", out var winTitleNode))
|
||||
defaultWindowTitle = winTitleNode.AsString();
|
||||
|
||||
string? windowIconSet = null;
|
||||
if (mapping.TryGetNode("windowIconSet", out var iconSetNode))
|
||||
windowIconSet = iconSetNode.AsString();
|
||||
|
||||
string? splashLogo = null;
|
||||
if (mapping.TryGetNode("splashLogo", out var splashNode))
|
||||
splashLogo = splashNode.AsString();
|
||||
|
||||
bool autoConnect = true;
|
||||
if (mapping.TryGetNode("autoConnect", out var autoConnectNode))
|
||||
autoConnect = autoConnectNode.AsBool();
|
||||
|
||||
var clientAssemblies = ReadStringArray(mapping, "clientAssemblies");
|
||||
|
||||
return new ResourceManifestData(
|
||||
modules,
|
||||
assemblyPrefix,
|
||||
defaultWindowTitle,
|
||||
windowIconSet,
|
||||
splashLogo,
|
||||
autoConnect,
|
||||
clientAssemblies
|
||||
);
|
||||
|
||||
static string[]? ReadStringArray(YamlMappingNode mapping, string key)
|
||||
{
|
||||
if (!mapping.TryGetNode(key, out var node))
|
||||
return null;
|
||||
|
||||
var sequence = (YamlSequenceNode)node;
|
||||
var array = new string[sequence.Children.Count];
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = sequence[i].AsString();
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool StartupSystemSplash(
|
||||
GameControllerOptions options,
|
||||
Func<ILogHandler>? logHandlerFactory,
|
||||
@@ -458,7 +385,7 @@ namespace Robust.Client
|
||||
_modLoader.VerifierExtraLoadHandler = VerifierExtraLoadHandler;
|
||||
}
|
||||
|
||||
_resourceManifest = LoadResourceManifest();
|
||||
_resourceManifest = ResourceManifestData.LoadResourceManifest(_resourceCache);
|
||||
|
||||
{
|
||||
// Handle GameControllerOptions implicit CVar overrides.
|
||||
@@ -705,7 +632,6 @@ namespace Robust.Client
|
||||
logManager.GetSawmill("ogl.debug.other").Level = LogLevel.Warning;
|
||||
logManager.GetSawmill("gdparse").Level = LogLevel.Error;
|
||||
logManager.GetSawmill("discord").Level = LogLevel.Warning;
|
||||
logManager.GetSawmill("net.predict").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("szr").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("loc").Level = LogLevel.Warning;
|
||||
|
||||
@@ -787,20 +713,6 @@ namespace Robust.Client
|
||||
_clydeAudio.Shutdown();
|
||||
}
|
||||
|
||||
private sealed record ResourceManifestData(
|
||||
string[] Modules,
|
||||
string? AssemblyPrefix,
|
||||
string? DefaultWindowTitle,
|
||||
string? WindowIconSet,
|
||||
string? SplashLogo,
|
||||
bool AutoConnect,
|
||||
string[]? ClientAssemblies
|
||||
)
|
||||
{
|
||||
public static readonly ResourceManifestData Default =
|
||||
new ResourceManifestData(Array.Empty<string>(), null, null, null, null, true, null);
|
||||
}
|
||||
|
||||
public event Action<FrameEventArgs>? TickUpdateOverride;
|
||||
}
|
||||
}
|
||||
|
||||
53
Robust.Client/GameObjects/ClientEntityManager.Network.cs
Normal file
53
Robust.Client/GameObjects/ClientEntityManager.Network.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed partial class ClientEntityManager
|
||||
{
|
||||
protected override NetEntity GenerateNetEntity() => new(NextNetworkId++ | NetEntity.ClientEntity);
|
||||
|
||||
/// <summary>
|
||||
/// If the client fails to resolve a NetEntity then during component state handling or the likes we
|
||||
/// flag that comp state as requiring re-running if that NetEntity comes in.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal readonly Dictionary<NetEntity, List<(Type type, EntityUid Owner)>> PendingNetEntityStates = new();
|
||||
|
||||
public override bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
// Can't log false because some content code relies on invalid UIDs.
|
||||
if (!MetaQuery.Resolve(uid, ref metadata, false))
|
||||
return false;
|
||||
|
||||
return metadata.NetEntity.IsClientSide();
|
||||
}
|
||||
|
||||
public override EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity)
|
||||
{
|
||||
if (!nEntity.Valid)
|
||||
{
|
||||
return EntityUid.Invalid;
|
||||
}
|
||||
|
||||
if (NetEntityLookup.TryGetValue(nEntity, out var entity))
|
||||
{
|
||||
return entity.Item1;
|
||||
}
|
||||
|
||||
// Flag the callerEntity to have their state potentially re-run later.
|
||||
var pending = PendingNetEntityStates.GetOrNew(nEntity);
|
||||
pending.Add((typeof(T), callerEntity));
|
||||
|
||||
return entity.Item1;
|
||||
}
|
||||
|
||||
public override EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
|
||||
{
|
||||
var entity = EnsureEntity<T>(netCoordinates.NetEntity, callerEntity);
|
||||
return new EntityCoordinates(entity, netCoordinates.Position);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Prometheus;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Replays;
|
||||
@@ -18,7 +16,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Manager for entities -- controls things like template loading and instantiation
|
||||
/// </summary>
|
||||
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
public sealed partial class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientNetManager _networkManager = default!;
|
||||
@@ -27,8 +25,6 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
|
||||
|
||||
protected override int NextEntityUid { get; set; } = EntityUid.ClientUid + 1;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetupNetworking();
|
||||
@@ -39,13 +35,16 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void FlushEntities()
|
||||
{
|
||||
// Server doesn't network deletions on client shutdown so we need to
|
||||
// manually clear these out or risk stale data getting used.
|
||||
PendingNetEntityStates.Clear();
|
||||
using var _ = _gameTiming.StartStateApplicationArea();
|
||||
base.FlushEntities();
|
||||
}
|
||||
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid uid)
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, out MetaDataComponent metadata)
|
||||
{
|
||||
return base.CreateEntity(prototypeName, uid);
|
||||
return base.CreateEntity(prototypeName, out metadata, out _);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
|
||||
@@ -66,9 +65,12 @@ namespace Robust.Client.GameObjects
|
||||
base.DirtyEntity(uid, meta);
|
||||
}
|
||||
|
||||
public override void QueueDeleteEntity(EntityUid uid)
|
||||
public override void QueueDeleteEntity(EntityUid? uid)
|
||||
{
|
||||
if (uid.IsClientSide())
|
||||
if (uid == null)
|
||||
return;
|
||||
|
||||
if (IsClientSide(uid.Value))
|
||||
{
|
||||
base.QueueDeleteEntity(uid);
|
||||
return;
|
||||
@@ -79,23 +81,29 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// Client-side entity deletion is not supported and will cause errors.
|
||||
if (_client.RunLevel == ClientRunLevel.Connected || _client.RunLevel == ClientRunLevel.InGame)
|
||||
LogManager.RootSawmill.Error($"Predicting the queued deletion of a networked entity: {ToPrettyString(uid)}. Trace: {Environment.StackTrace}");
|
||||
LogManager.RootSawmill.Error($"Predicting the queued deletion of a networked entity: {ToPrettyString(uid.Value)}. Trace: {Environment.StackTrace}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dirty(EntityUid uid, Component component, MetaDataComponent? meta = null)
|
||||
public override void Dirty(EntityUid uid, IComponent component, MetaDataComponent? meta = null)
|
||||
{
|
||||
Dirty(new Entity<IComponent>(uid, component), meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dirty<T>(Entity<T> ent, MetaDataComponent? meta = null)
|
||||
{
|
||||
// Client only dirties during prediction
|
||||
if (_gameTiming.InPrediction)
|
||||
base.Dirty(uid, component, meta);
|
||||
base.Dirty(ent, meta);
|
||||
}
|
||||
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid)
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metaDataComponent = null)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == uid)
|
||||
return base.ToPrettyString(uid) with { Session = _playerManager.LocalPlayer.Session };
|
||||
else
|
||||
return base.ToPrettyString(uid);
|
||||
|
||||
return base.ToPrettyString(uid);
|
||||
}
|
||||
|
||||
public override void RaisePredictiveEvent<T>(T msg)
|
||||
@@ -162,7 +170,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, INetChannel channel)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, INetChannel? channel)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using static Robust.Client.Animations.AnimationPlaybackShared;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -11,7 +9,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Plays back <see cref="Animation"/>s on entities.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class AnimationPlayerComponent : Component
|
||||
public sealed partial class AnimationPlayerComponent : Component
|
||||
{
|
||||
// TODO: Give this component a friend someday. Way too much content shit to change atm ._.
|
||||
|
||||
@@ -21,42 +19,5 @@ namespace Robust.Client.GameObjects
|
||||
= new();
|
||||
|
||||
internal bool HasPlayingAnimation = false;
|
||||
|
||||
/// <summary>
|
||||
/// Start playing an animation.
|
||||
/// </summary>
|
||||
/// <param name="animation">The animation to play.</param>
|
||||
/// <param name="key">
|
||||
/// The key for this animation play. This key can be used to stop playback short later.
|
||||
/// </param>
|
||||
[Obsolete("Use AnimationPlayerSystem.Play() instead")]
|
||||
public void Play(Animation animation, string key)
|
||||
{
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AnimationPlayerSystem>().AddComponent(this);
|
||||
var playback = new AnimationPlayback(animation);
|
||||
|
||||
PlayingAnimations.Add(key, playback);
|
||||
}
|
||||
|
||||
[Obsolete("Use AnimationPlayerSystem.HasRunningAnimation() instead")]
|
||||
public bool HasRunningAnimation(string key)
|
||||
{
|
||||
return PlayingAnimations.ContainsKey(key);
|
||||
}
|
||||
|
||||
[Obsolete("Use AnimationPlayerSystem.Stop() instead")]
|
||||
public void Stop(string key)
|
||||
{
|
||||
PlayingAnimations.Remove(key);
|
||||
}
|
||||
|
||||
[Obsolete("Temporary method until the event is replaced with eventbus")]
|
||||
internal void AnimationComplete(string key)
|
||||
{
|
||||
AnimationCompleted?.Invoke(key);
|
||||
}
|
||||
|
||||
[Obsolete("Use AnimationCompletedEvent instead")]
|
||||
public event Action<string>? AnimationCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.GameObjects;
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(GenericVisualizerSystem))]
|
||||
public sealed class GenericVisualizerComponent : Component
|
||||
public sealed partial class GenericVisualizerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a nested dictionary that maps appearance data keys -> sprite layer keys -> appearance data values -> layer data.
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent, ComponentReference(typeof(SharedEyeComponent))]
|
||||
public sealed class EyeComponent : SharedEyeComponent
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables] internal Eye? _eye = default!;
|
||||
|
||||
// Horrible hack to get around ordering issues.
|
||||
internal bool _setCurrentOnInitialize;
|
||||
[DataField("drawFov")] internal bool _setDrawFovOnInitialize = true;
|
||||
[DataField("zoom")] internal Vector2 _setZoomOnInitialize = Vector2.One;
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this entity is used to update the eye's position instead of just using the component's owner.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful for things like vehicles that effectively need to hijack the eye. This allows them to do
|
||||
/// that without messing with the main viewport's eye. This is important as there are some overlays that are
|
||||
/// only be drawn if that viewport's eye belongs to the currently controlled entity.
|
||||
/// </remarks>
|
||||
[ViewVariables]
|
||||
public EntityUid? Target;
|
||||
|
||||
public IEye? Eye => _eye;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Current
|
||||
{
|
||||
get => _eyeManager.CurrentEye == _eye;
|
||||
set
|
||||
{
|
||||
if (_eye == null)
|
||||
{
|
||||
_setCurrentOnInitialize = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_eyeManager.CurrentEye == _eye == value)
|
||||
return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
_eyeManager.CurrentEye = _eye;
|
||||
}
|
||||
else
|
||||
{
|
||||
_eyeManager.ClearCurrentEye();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector2 Zoom
|
||||
{
|
||||
get => _eye?.Zoom ?? _setZoomOnInitialize;
|
||||
set
|
||||
{
|
||||
if (_eye == null)
|
||||
{
|
||||
_setZoomOnInitialize = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_eye.Zoom = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Angle Rotation
|
||||
{
|
||||
get => _eye?.Rotation ?? Angle.Zero;
|
||||
set
|
||||
{
|
||||
if (_eye != null)
|
||||
_eye.Rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector2 Offset
|
||||
{
|
||||
get => _eye?.Offset ?? default;
|
||||
set
|
||||
{
|
||||
if (_eye != null)
|
||||
_eye.Offset = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DrawFov
|
||||
{
|
||||
get => _eye?.DrawFov ?? _setDrawFovOnInitialize;
|
||||
set
|
||||
{
|
||||
if (_eye == null)
|
||||
{
|
||||
_setDrawFovOnInitialize = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_eye.DrawFov = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public MapCoordinates? Position => _eye?.Position;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Eye of this entity with the transform position. This has to be called every frame to
|
||||
/// keep the view following the entity.
|
||||
/// </summary>
|
||||
public void UpdateEyePosition()
|
||||
{
|
||||
if (_eye == null) return;
|
||||
|
||||
if (!_entityManager.TryGetComponent(Target, out TransformComponent? xform))
|
||||
{
|
||||
xform = _entityManager.GetComponent<TransformComponent>(Owner);
|
||||
Target = null;
|
||||
}
|
||||
|
||||
_eye.Position = xform.MapPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ namespace Robust.Client.GameObjects;
|
||||
/// updated.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed class IconComponent : Component
|
||||
public sealed partial class IconComponent : Component
|
||||
{
|
||||
[IncludeDataField]
|
||||
public readonly SpriteSpecifier.Rsi Icon = default!;
|
||||
public SpriteSpecifier.Rsi Icon = default!;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Defines data fields used in the <see cref="InputSystem"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class InputComponent : Component
|
||||
public sealed partial class InputComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that will be made active for a client that attaches to this entity.
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedPointLightComponent))]
|
||||
public sealed class PointLightComponent : SharedPointLightComponent, IComponentTreeEntry<PointLightComponent>
|
||||
{
|
||||
public EntityUid? TreeUid { get; set; }
|
||||
|
||||
public DynamicTree<ComponentTreeEntry<PointLightComponent>>? Tree { get; set; }
|
||||
|
||||
public bool AddToTree => Enabled && !ContainerOccluded;
|
||||
public bool TreeUpdateQueued { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[Animatable]
|
||||
public override Color Color
|
||||
{
|
||||
get => _color;
|
||||
set => base.Color = value;
|
||||
}
|
||||
|
||||
[Access(typeof(PointLightSystem))]
|
||||
public bool ContainerOccluded;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the light mask should automatically rotate with the entity. (like a flashlight)
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool MaskAutoRotate
|
||||
{
|
||||
get => _maskAutoRotate;
|
||||
set => _maskAutoRotate = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Local rotation of the light mask around the center origin
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[Animatable]
|
||||
public Angle Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
set => _rotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The resource path to the mask texture the light will use.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? MaskPath
|
||||
{
|
||||
get => _maskPath;
|
||||
set
|
||||
{
|
||||
if (_maskPath?.Equals(value) != false) return;
|
||||
_maskPath = value;
|
||||
EntitySystem.Get<PointLightSystem>().UpdateMask(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a mask texture that will be applied to the light while rendering.
|
||||
/// The mask's red channel will be linearly multiplied.p
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Texture? Mask { get; set; }
|
||||
|
||||
[DataField("autoRot")]
|
||||
private bool _maskAutoRotate;
|
||||
private Angle _rotation;
|
||||
|
||||
[DataField("mask")]
|
||||
internal string? _maskPath;
|
||||
}
|
||||
}
|
||||
38
Robust.Client/GameObjects/Components/PointLightComponent.cs
Normal file
38
Robust.Client/GameObjects/Components/PointLightComponent.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class PointLightComponent : SharedPointLightComponent, IComponentTreeEntry<PointLightComponent>
|
||||
{
|
||||
#region Component Tree
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public EntityUid? TreeUid { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public DynamicTree<ComponentTreeEntry<PointLightComponent>>? Tree { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public bool AddToTree => Enabled && !ContainerOccluded;
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public bool TreeUpdateQueued { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Set a mask texture that will be applied to the light while rendering.
|
||||
/// The mask's red channel will be linearly multiplied.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
internal Texture? Mask;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public interface IRenderableComponent : IComponent
|
||||
public partial interface IRenderableComponent : IComponent
|
||||
{
|
||||
int DrawDepth { get; set; }
|
||||
float Bottom { get; }
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -24,7 +26,7 @@ namespace Robust.Client.GameObjects
|
||||
int AnimationFrame { get; }
|
||||
bool AutoAnimated { get; set; }
|
||||
|
||||
RSI.State.Direction EffectiveDirection(Angle worldRotation);
|
||||
RsiDirection EffectiveDirection(Angle worldRotation);
|
||||
|
||||
/// <summary>
|
||||
/// Layer size in pixels.
|
||||
|
||||
@@ -12,6 +12,8 @@ using Robust.Shared;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -26,13 +28,13 @@ using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.ComponentTrees.SpriteTreeSystem;
|
||||
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
using RSIDirection = Robust.Client.Graphics.RSI.State.Direction;
|
||||
using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteSpecifierSerializer;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class SpriteComponent : Component, IComponentDebug, ISerializationHooks, IComponentTreeEntry<SpriteComponent>, IAnimationProperties
|
||||
public sealed partial class SpriteComponent : Component, IComponentDebug, ISerializationHooks, IComponentTreeEntry<SpriteComponent>, IAnimationProperties
|
||||
{
|
||||
[Dependency] private readonly IResourceCache resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager prototypes = default!;
|
||||
@@ -167,40 +169,9 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public bool TreeUpdateQueued { get; set; }
|
||||
|
||||
[DataField("layerDatums")]
|
||||
private List<PrototypeLayerData> LayerDatums
|
||||
{
|
||||
get
|
||||
{
|
||||
var layerDatums = new List<PrototypeLayerData>();
|
||||
foreach (var layer in Layers)
|
||||
{
|
||||
layerDatums.Add(layer.ToPrototypeData());
|
||||
}
|
||||
|
||||
return layerDatums;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
Layers.Clear();
|
||||
foreach (var layerDatum in value)
|
||||
{
|
||||
AddLayer(layerDatum);
|
||||
}
|
||||
|
||||
_layerMapShared = true;
|
||||
|
||||
QueueUpdateRenderTree();
|
||||
QueueUpdateIsInert();
|
||||
}
|
||||
}
|
||||
|
||||
private RSI? _baseRsi;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("rsi", priority: 2)]
|
||||
public RSI? BaseRSI
|
||||
{
|
||||
get => _baseRsi;
|
||||
@@ -357,7 +328,16 @@ namespace Robust.Client.GameObjects
|
||||
if (layerDatums.Count != 0)
|
||||
{
|
||||
LayerMap.Clear();
|
||||
LayerDatums = layerDatums;
|
||||
Layers.Clear();
|
||||
foreach (var datum in layerDatums)
|
||||
{
|
||||
AddLayer(datum);
|
||||
}
|
||||
|
||||
_layerMapShared = true;
|
||||
|
||||
QueueUpdateRenderTree();
|
||||
QueueUpdateIsInert();
|
||||
}
|
||||
|
||||
UpdateLocalMatrix();
|
||||
@@ -1369,11 +1349,11 @@ namespace Robust.Client.GameObjects
|
||||
state = GetFallbackState(resourceCache);
|
||||
}
|
||||
|
||||
return state.Directions switch
|
||||
return state.RsiDirections switch
|
||||
{
|
||||
RSI.State.DirectionType.Dir1 => 1,
|
||||
RSI.State.DirectionType.Dir4 => 4,
|
||||
RSI.State.DirectionType.Dir8 => 8,
|
||||
RsiDirectionType.Dir1 => 1,
|
||||
RsiDirectionType.Dir4 => 4,
|
||||
RsiDirectionType.Dir8 => 8,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
@@ -1411,7 +1391,7 @@ namespace Robust.Client.GameObjects
|
||||
builder.AppendFormat(
|
||||
"vis/depth/scl/rot/ofs/col/norot/override/dir: {0}/{1}/{2}/{3}/{4}/{5}/{6}/{8}/{7}\n",
|
||||
Visible, DrawDepth, Scale, Rotation, Offset,
|
||||
Color, NoRotation, entities.GetComponent<TransformComponent>(Owner).WorldRotation.ToRsiDirection(RSI.State.DirectionType.Dir8),
|
||||
Color, NoRotation, entities.GetComponent<TransformComponent>(Owner).WorldRotation.ToRsiDirection(RsiDirectionType.Dir8),
|
||||
DirectionOverride
|
||||
);
|
||||
|
||||
@@ -1711,7 +1691,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
int ISpriteLayer.AnimationFrame => AnimationFrame;
|
||||
|
||||
public RSIDirection EffectiveDirection(Angle worldRotation)
|
||||
public RsiDirection EffectiveDirection(Angle worldRotation)
|
||||
{
|
||||
if (State == default)
|
||||
{
|
||||
@@ -1732,23 +1712,23 @@ namespace Robust.Client.GameObjects
|
||||
return default;
|
||||
}
|
||||
|
||||
public RSIDirection EffectiveDirection(RSI.State state, Angle worldRotation,
|
||||
public RsiDirection EffectiveDirection(RSI.State state, Angle worldRotation,
|
||||
Direction? overrideDirection)
|
||||
{
|
||||
if (state.Directions == RSI.State.DirectionType.Dir1)
|
||||
if (state.RsiDirections == RsiDirectionType.Dir1)
|
||||
{
|
||||
return RSIDirection.South;
|
||||
return RsiDirection.South;
|
||||
}
|
||||
else
|
||||
{
|
||||
RSIDirection dir;
|
||||
RsiDirection dir;
|
||||
if (overrideDirection != null)
|
||||
{
|
||||
dir = overrideDirection.Value.Convert(state.Directions);
|
||||
dir = overrideDirection.Value.Convert(state.RsiDirections);
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = worldRotation.ToRsiDirection(state.Directions);
|
||||
dir = worldRotation.ToRsiDirection(state.RsiDirections);
|
||||
}
|
||||
|
||||
return dir.OffsetRsiDir(DirOffset);
|
||||
@@ -1904,20 +1884,20 @@ namespace Robust.Client.GameObjects
|
||||
else if (_parent.SnapCardinals && (!_parent.GranularLayersRendering || RenderingStrategy == LayerRenderingStrategy.UseSpriteStrategy)
|
||||
|| _parent.GranularLayersRendering && RenderingStrategy == LayerRenderingStrategy.SnapToCardinals)
|
||||
{
|
||||
DebugTools.Assert(_actualState == null || _actualState.Directions == RSI.State.DirectionType.Dir1);
|
||||
DebugTools.Assert(_actualState == null || _actualState.RsiDirections == RsiDirectionType.Dir1);
|
||||
size = new Vector2(longestSide, longestSide);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build the bounding box based on how many directions the sprite has
|
||||
size = (_actualState?.Directions) switch
|
||||
size = (_actualState?.RsiDirections) switch
|
||||
{
|
||||
// If we have four cardinal directions, take the longest side of our texture and square it, then turn that into our bounding box.
|
||||
// This accounts for all possible rotations.
|
||||
RSI.State.DirectionType.Dir4 => new Vector2(longestSide, longestSide),
|
||||
RsiDirectionType.Dir4 => new Vector2(longestSide, longestSide),
|
||||
|
||||
// If we have eight directions, find the maximum length of the texture (accounting for rotation), then square it to make
|
||||
RSI.State.DirectionType.Dir8 => new Vector2(longestRotatedSide, longestRotatedSide),
|
||||
RsiDirectionType.Dir8 => new Vector2(longestRotatedSide, longestRotatedSide),
|
||||
|
||||
// If we have only one direction or an invalid RSI state, create a simple bounding box with the size of the texture.
|
||||
_ => textureSize
|
||||
@@ -1951,9 +1931,9 @@ namespace Robust.Client.GameObjects
|
||||
/// Given the apparent rotation of an entity on screen (world + eye rotation), get layer's matrix for drawing &
|
||||
/// relevant RSI direction.
|
||||
/// </summary>
|
||||
public void GetLayerDrawMatrix(RSIDirection dir, out Matrix3 layerDrawMatrix)
|
||||
public void GetLayerDrawMatrix(RsiDirection dir, out Matrix3 layerDrawMatrix)
|
||||
{
|
||||
if (_parent.NoRotation || dir == RSIDirection.South)
|
||||
if (_parent.NoRotation || dir == RsiDirection.South)
|
||||
layerDrawMatrix = LocalMatrix;
|
||||
else
|
||||
{
|
||||
@@ -1978,11 +1958,11 @@ namespace Robust.Client.GameObjects
|
||||
/// Converts an angle (between 0 and 2pi) to an RSI direction. This will slightly bias the angle to avoid flickering for
|
||||
/// 4-directional sprites.
|
||||
/// </summary>
|
||||
public static RSIDirection GetDirection(RSI.State.DirectionType dirType, Angle angle)
|
||||
public static RsiDirection GetDirection(RsiDirectionType dirType, Angle angle)
|
||||
{
|
||||
if (dirType == RSI.State.DirectionType.Dir1)
|
||||
return RSIDirection.South;
|
||||
else if (dirType == RSI.State.DirectionType.Dir8)
|
||||
if (dirType == RsiDirectionType.Dir1)
|
||||
return RsiDirection.South;
|
||||
else if (dirType == RsiDirectionType.Dir8)
|
||||
return angle.GetDir().Convert(dirType);
|
||||
|
||||
// For 4-directional sprites, as entities are often moving & facing diagonally, we will slightly bias the
|
||||
@@ -1995,10 +1975,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
return ((int)Math.Round(modTheta / MathHelper.PiOver2) % 4) switch
|
||||
{
|
||||
0 => RSIDirection.South,
|
||||
1 => RSIDirection.East,
|
||||
2 => RSIDirection.North,
|
||||
_ => RSIDirection.West,
|
||||
0 => RsiDirection.South,
|
||||
1 => RsiDirection.East,
|
||||
2 => RsiDirection.North,
|
||||
_ => RsiDirection.West,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2010,7 +1990,7 @@ namespace Robust.Client.GameObjects
|
||||
if (!Visible || Blank)
|
||||
return;
|
||||
|
||||
var dir = _actualState == null ? RSIDirection.South : GetDirection(_actualState.Directions, angle);
|
||||
var dir = _actualState == null ? RsiDirection.South : GetDirection(_actualState.RsiDirections, angle);
|
||||
|
||||
// Set the drawing transform for this layer
|
||||
GetLayerDrawMatrix(dir, out var layerMatrix);
|
||||
@@ -2020,7 +2000,7 @@ namespace Robust.Client.GameObjects
|
||||
// The direction used to draw the sprite can differ from the one that the angle would naively suggest,
|
||||
// due to direction overrides or offsets.
|
||||
if (overrideDirection != null && _actualState != null)
|
||||
dir = overrideDirection.Value.Convert(_actualState.Directions);
|
||||
dir = overrideDirection.Value.Convert(_actualState.RsiDirections);
|
||||
dir = dir.OffsetRsiDir(DirOffset);
|
||||
|
||||
// Get the correct directional texture from the state, and draw it!
|
||||
@@ -2043,7 +2023,7 @@ namespace Robust.Client.GameObjects
|
||||
drawingHandle.UseShader(null);
|
||||
}
|
||||
|
||||
private Texture GetRenderTexture(RSI.State? state, RSIDirection dir)
|
||||
private Texture GetRenderTexture(RSI.State? state, RsiDirection dir)
|
||||
{
|
||||
if (state == null)
|
||||
return Texture ?? _parent.resourceCache.GetFallback<TextureResource>().Texture;
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public sealed class AnimationPlayerSystem : EntitySystem, IPostInjectInit
|
||||
public sealed class AnimationPlayerSystem : EntitySystem
|
||||
{
|
||||
private readonly List<AnimationPlayerComponent> _activeAnimations = new();
|
||||
private readonly List<Entity<AnimationPlayerComponent>> _activeAnimations = new();
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -39,22 +35,22 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Update(uid, anim, frameTime))
|
||||
if (!Update(uid, anim.Comp, frameTime))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_activeAnimations.RemoveSwap(i);
|
||||
i--;
|
||||
anim.HasPlayingAnimation = false;
|
||||
anim.Comp.HasPlayingAnimation = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddComponent(AnimationPlayerComponent component)
|
||||
internal void AddComponent(Entity<AnimationPlayerComponent> ent)
|
||||
{
|
||||
if (component.HasPlayingAnimation) return;
|
||||
_activeAnimations.Add(component);
|
||||
component.HasPlayingAnimation = true;
|
||||
if (ent.Comp.HasPlayingAnimation) return;
|
||||
_activeAnimations.Add(ent);
|
||||
ent.Comp.HasPlayingAnimation = true;
|
||||
}
|
||||
|
||||
private bool Update(EntityUid uid, AnimationPlayerComponent component, float frameTime)
|
||||
@@ -79,7 +75,6 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, new AnimationCompletedEvent {Uid = uid, Key = key}, true);
|
||||
component.AnimationComplete(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -90,22 +85,29 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void Play(EntityUid uid, Animation animation, string key)
|
||||
{
|
||||
var component = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
|
||||
Play(component, animation, key);
|
||||
var component = EnsureComp<AnimationPlayerComponent>(uid);
|
||||
Play(new Entity<AnimationPlayerComponent>(uid, component), animation, key);
|
||||
}
|
||||
|
||||
[Obsolete("Use Play(EntityUid<AnimationPlayerComponent> ent, Animation animation, string key) instead")]
|
||||
public void Play(EntityUid uid, AnimationPlayerComponent? component, Animation animation, string key)
|
||||
{
|
||||
component ??= EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
|
||||
Play(component, animation, key);
|
||||
Play(new Entity<AnimationPlayerComponent>(uid, component), animation, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start playing an animation.
|
||||
/// </summary>
|
||||
[Obsolete("Use Play(EntityUid<AnimationPlayerComponent> ent, Animation animation, string key) instead")]
|
||||
public void Play(AnimationPlayerComponent component, Animation animation, string key)
|
||||
{
|
||||
AddComponent(component);
|
||||
Play(new Entity<AnimationPlayerComponent>(component.Owner, component), animation, key);
|
||||
}
|
||||
|
||||
public void Play(Entity<AnimationPlayerComponent> ent, Animation animation, string key)
|
||||
{
|
||||
AddComponent(ent);
|
||||
var playback = new AnimationPlaybackShared.AnimationPlayback(animation);
|
||||
|
||||
#if DEBUG
|
||||
@@ -117,18 +119,18 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
if (compTrack.ComponentType == null)
|
||||
{
|
||||
_sawmill.Error($"Attempted to play a component animation without any component specified.");
|
||||
Log.Error("Attempted to play a component animation without any component specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetComponent(component.Owner, compTrack.ComponentType, out var animatedComp))
|
||||
if (!EntityManager.TryGetComponent(ent, compTrack.ComponentType, out var animatedComp))
|
||||
{
|
||||
_sawmill.Error(
|
||||
$"Attempted to play a component animation, but the entity {ToPrettyString(component.Owner)} does not have the component to be animated: {compTrack.ComponentType}.");
|
||||
Log.Error(
|
||||
$"Attempted to play a component animation, but the entity {ToPrettyString(ent)} does not have the component to be animated: {compTrack.ComponentType}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Owner.IsClientSide() || !animatedComp.NetSyncEnabled)
|
||||
if (IsClientSide(ent) || !animatedComp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
var reg = _compFact.GetRegistration(animatedComp);
|
||||
@@ -136,12 +138,18 @@ namespace Robust.Client.GameObjects
|
||||
// In principle there is nothing wrong with this, as long as the property of the component being
|
||||
// animated is not part of the networked state and setting it does not dirty the component. Hence only a
|
||||
// warning in debug mode.
|
||||
if (reg.NetID != null)
|
||||
_sawmill.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(component.Owner)}");
|
||||
if (reg.NetID != null && compTrack.Property != null)
|
||||
{
|
||||
if (animatedComp.GetType().GetProperty(compTrack.Property) is { } property &&
|
||||
property.HasCustomAttribute<AutoNetworkedFieldAttribute>())
|
||||
{
|
||||
Log.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(ent)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
component.PlayingAnimations.Add(key, playback);
|
||||
ent.Comp.PlayingAnimations.Add(key, playback);
|
||||
}
|
||||
|
||||
public bool HasRunningAnimation(EntityUid uid, string key)
|
||||
@@ -170,19 +178,18 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public void Stop(EntityUid uid, string key)
|
||||
{
|
||||
if (!TryComp<AnimationPlayerComponent>(uid, out var player)) return;
|
||||
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
|
||||
return;
|
||||
|
||||
player.PlayingAnimations.Remove(key);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false)) return;
|
||||
component.PlayingAnimations.Remove(key);
|
||||
}
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill("anim");
|
||||
component.PlayingAnimations.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Threading;
|
||||
@@ -81,9 +80,13 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
#region Event Handlers
|
||||
private void PlayAudioEntityHandler(PlayAudioEntityMessage ev)
|
||||
{
|
||||
var stream = EntityManager.EntityExists(ev.EntityUid)
|
||||
? (PlayingStream?) Play(ev.FileName, ev.EntityUid, ev.FallbackCoordinates, ev.AudioParams, false)
|
||||
: (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams, false);
|
||||
var uid = GetEntity(ev.NetEntity);
|
||||
var coords = GetCoordinates(ev.Coordinates);
|
||||
var fallback = GetCoordinates(ev.FallbackCoordinates);
|
||||
|
||||
var stream = EntityManager.EntityExists(uid)
|
||||
? (PlayingStream?) Play(ev.FileName, uid, fallback, ev.AudioParams, false)
|
||||
: (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false);
|
||||
|
||||
if (stream != null)
|
||||
stream.NetIdentifier = ev.Identifier;
|
||||
@@ -98,7 +101,10 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
|
||||
private void PlayAudioPositionalHandler(PlayAudioPositionalMessage ev)
|
||||
{
|
||||
var stream = (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams, false);
|
||||
var coords = GetCoordinates(ev.Coordinates);
|
||||
var fallback = GetCoordinates(ev.FallbackCoordinates);
|
||||
|
||||
var stream = (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false);
|
||||
if (stream != null)
|
||||
stream.NetIdentifier = ev.Identifier;
|
||||
}
|
||||
@@ -383,8 +389,8 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
_replayRecording.RecordReplayMessage(new PlayAudioEntityMessage
|
||||
{
|
||||
FileName = filename,
|
||||
EntityUid = entity,
|
||||
FallbackCoordinates = fallbackCoordinates ?? default,
|
||||
NetEntity = GetNetEntity(entity),
|
||||
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates) ?? default,
|
||||
AudioParams = audioParams ?? AudioParams.Default
|
||||
});
|
||||
}
|
||||
@@ -437,8 +443,8 @@ public sealed class AudioSystem : SharedAudioSystem
|
||||
_replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage
|
||||
{
|
||||
FileName = filename,
|
||||
Coordinates = coordinates,
|
||||
FallbackCoordinates = fallbackCoordinates,
|
||||
Coordinates = GetNetCoordinates(coordinates),
|
||||
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates),
|
||||
AudioParams = audioParams ?? AudioParams.Default
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -5,13 +6,11 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
using static Robust.Shared.Containers.ContainerManagerComponent;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -23,14 +22,20 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
[Dependency] private readonly PointLightSystem _lightSys = default!;
|
||||
|
||||
private EntityQuery<PointLightComponent> _pointLightQuery;
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
private readonly HashSet<EntityUid> _updateQueue = new();
|
||||
|
||||
public readonly Dictionary<EntityUid, IContainer> ExpectedEntities = new();
|
||||
public readonly Dictionary<NetEntity, BaseContainer> ExpectedEntities = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_pointLightQuery = GetEntityQuery<PointLightComponent>();
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
EntityManager.EntityInitialized += HandleEntityInitialized;
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentHandleState>(HandleComponentState);
|
||||
|
||||
@@ -43,20 +48,18 @@ namespace Robust.Client.GameObjects
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
protected override void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing)
|
||||
protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing)
|
||||
{
|
||||
DebugTools.Assert(ExpectedEntities.TryGetValue(missing, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(missing));
|
||||
var netEntity = GetNetEntity(missing);
|
||||
DebugTools.Assert(ExpectedEntities.TryGetValue(netEntity, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(netEntity));
|
||||
}
|
||||
|
||||
private void HandleEntityInitialized(EntityUid uid)
|
||||
{
|
||||
if (!RemoveExpectedEntity(uid, out var container))
|
||||
if (!RemoveExpectedEntity(GetNetEntity(uid), out var container))
|
||||
return;
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
|
||||
container.Insert(uid);
|
||||
container.Insert(uid, EntityManager, transform: TransformQuery.GetComponent(uid), meta: MetaQuery.GetComponent(uid));
|
||||
}
|
||||
|
||||
private void HandleComponentState(EntityUid uid, ContainerManagerComponent component, ref ComponentHandleState args)
|
||||
@@ -64,23 +67,24 @@ namespace Robust.Client.GameObjects
|
||||
if (args.Current is not ContainerManagerComponentState cast)
|
||||
return;
|
||||
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xform = xformQuery.GetComponent(uid);
|
||||
var xform = TransformQuery.GetComponent(uid);
|
||||
|
||||
// Delete now-gone containers.
|
||||
var toDelete = new ValueList<string>();
|
||||
foreach (var (id, container) in component.Containers)
|
||||
{
|
||||
if (cast.Containers.ContainsKey(id))
|
||||
{
|
||||
DebugTools.Assert(cast.Containers[id].ContainerType == container.GetType().Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var entity in container.ContainedEntities.ToArray())
|
||||
{
|
||||
container.Remove(entity,
|
||||
EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
metaQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true,
|
||||
reparent: false);
|
||||
|
||||
@@ -98,26 +102,32 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// Add new containers and update existing contents.
|
||||
|
||||
foreach (var (containerType, id, showEnts, occludesLight, entityUids) in cast.Containers.Values)
|
||||
foreach (var (id, data) in cast.Containers)
|
||||
{
|
||||
if (!component.Containers.TryGetValue(id, out var container))
|
||||
{
|
||||
container = ContainerFactory(component, containerType, id);
|
||||
var type = _serializer.FindSerializedType(typeof(BaseContainer), data.ContainerType);
|
||||
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type!, inject:false);
|
||||
container.Init(id, uid, component);
|
||||
component.Containers.Add(id, container);
|
||||
}
|
||||
|
||||
// sync show flag
|
||||
container.ShowContents = showEnts;
|
||||
container.OccludesLight = occludesLight;
|
||||
DebugTools.Assert(container.ID == id);
|
||||
container.ShowContents = data.ShowContents;
|
||||
container.OccludesLight = data.OccludesLight;
|
||||
|
||||
// Remove gone entities.
|
||||
var toRemove = new ValueList<EntityUid>();
|
||||
|
||||
DebugTools.Assert(!container.Contains(EntityUid.Invalid));
|
||||
|
||||
var stateNetEnts = data.ContainedEntities;
|
||||
var stateEnts = GetEntityArray(stateNetEnts); // No need to ensure entities.
|
||||
|
||||
foreach (var entity in container.ContainedEntities)
|
||||
{
|
||||
if (!entityUids.Contains(entity))
|
||||
{
|
||||
if (!stateEnts.Contains(entity))
|
||||
toRemove.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in toRemove)
|
||||
@@ -125,8 +135,8 @@ namespace Robust.Client.GameObjects
|
||||
container.Remove(
|
||||
entity,
|
||||
EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
metaQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true,
|
||||
reparent: false);
|
||||
|
||||
@@ -134,13 +144,11 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Remove entities that were expected, but have been removed from the container.
|
||||
var removedExpected = new ValueList<EntityUid>();
|
||||
foreach (var entityUid in container.ExpectedEntities)
|
||||
var removedExpected = new ValueList<NetEntity>();
|
||||
foreach (var netEntity in container.ExpectedEntities)
|
||||
{
|
||||
if (!entityUids.Contains(entityUid))
|
||||
{
|
||||
removedExpected.Add(entityUid);
|
||||
}
|
||||
if (!stateNetEnts.Contains(netEntity))
|
||||
removedExpected.Add(netEntity);
|
||||
}
|
||||
|
||||
foreach (var entityUid in removedExpected)
|
||||
@@ -149,14 +157,20 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Add new entities.
|
||||
foreach (var entity in entityUids)
|
||||
for (var i = 0; i < stateNetEnts.Length; i++)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(entity, out MetaDataComponent? meta))
|
||||
var entity = stateEnts[i];
|
||||
var netEnt = stateNetEnts[i];
|
||||
if (!entity.IsValid())
|
||||
{
|
||||
AddExpectedEntity(entity, container);
|
||||
DebugTools.Assert(netEnt.IsValid());
|
||||
AddExpectedEntity(netEnt, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
var meta = MetaData(entity);
|
||||
DebugTools.Assert(meta.NetEntity == netEnt);
|
||||
|
||||
// If an entity is currently in the shadow realm, it means we probably left PVS and are now getting
|
||||
// back into range. We do not want to directly insert this entity, as IF the container and entity
|
||||
// transform states did not get sent simultaneously, the entity's transform will be modified by the
|
||||
@@ -166,18 +180,18 @@ namespace Robust.Client.GameObjects
|
||||
// containers/players.
|
||||
if ((meta.Flags & MetaDataFlags.Detached) != 0)
|
||||
{
|
||||
AddExpectedEntity(entity, container);
|
||||
AddExpectedEntity(netEnt, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.Contains(entity))
|
||||
continue;
|
||||
|
||||
RemoveExpectedEntity(entity, out _);
|
||||
RemoveExpectedEntity(netEnt, out _);
|
||||
container.Insert(entity, EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
xform,
|
||||
metaQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true);
|
||||
|
||||
DebugTools.Assert(container.Contains(entity));
|
||||
@@ -198,7 +212,7 @@ namespace Robust.Client.GameObjects
|
||||
if (message.OldParent != null && message.OldParent.Value.IsValid())
|
||||
return;
|
||||
|
||||
if (!RemoveExpectedEntity(message.Entity, out var container))
|
||||
if (!RemoveExpectedEntity(GetNetEntity(message.Entity), out var container))
|
||||
return;
|
||||
|
||||
if (xform.ParentUid != container.Owner)
|
||||
@@ -208,84 +222,69 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
|
||||
container.Insert(message.Entity, EntityManager);
|
||||
}
|
||||
|
||||
private IContainer ContainerFactory(ContainerManagerComponent component, string containerType, string id)
|
||||
public void AddExpectedEntity(NetEntity netEntity, BaseContainer container)
|
||||
{
|
||||
var type = _serializer.FindSerializedType(typeof(IContainer), containerType);
|
||||
if (type is null) throw new ArgumentException($"Container of type {containerType} for id {id} cannot be found.");
|
||||
#if DEBUG
|
||||
var uid = GetEntity(netEntity);
|
||||
|
||||
var newContainer = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
|
||||
newContainer.ID = id;
|
||||
newContainer.Manager = component;
|
||||
return newContainer;
|
||||
}
|
||||
if (TryComp<MetaDataComponent>(uid, out var meta))
|
||||
{
|
||||
DebugTools.Assert((meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
|
||||
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
|
||||
}
|
||||
#endif
|
||||
|
||||
public void AddExpectedEntity(EntityUid uid, IContainer container)
|
||||
{
|
||||
DebugTools.Assert(!TryComp(uid, out MetaDataComponent? meta) ||
|
||||
(meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
|
||||
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
|
||||
|
||||
if (!ExpectedEntities.TryAdd(uid, container))
|
||||
if (!ExpectedEntities.TryAdd(netEntity, container))
|
||||
{
|
||||
// It is possible that we were expecting this entity in one container, but it has now moved to another
|
||||
// container, and this entity's state is just being applied before the old container is getting updated.
|
||||
var oldContainer = ExpectedEntities[uid];
|
||||
ExpectedEntities[uid] = container;
|
||||
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(uid),
|
||||
$"Entity {ToPrettyString(uid)} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
|
||||
oldContainer.ExpectedEntities.Remove(uid);
|
||||
var oldContainer = ExpectedEntities[netEntity];
|
||||
ExpectedEntities[netEntity] = container;
|
||||
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(netEntity),
|
||||
$"Entity {netEntity} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
|
||||
oldContainer.ExpectedEntities.Remove(netEntity);
|
||||
}
|
||||
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(uid),
|
||||
$"Contained entity {ToPrettyString(uid)} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Add(uid);
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(netEntity),
|
||||
$"Contained entity {netEntity} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Add(netEntity);
|
||||
}
|
||||
|
||||
public bool RemoveExpectedEntity(EntityUid uid, [NotNullWhen(true)] out IContainer? container)
|
||||
public bool RemoveExpectedEntity(NetEntity netEntity, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
if (!ExpectedEntities.Remove(uid, out container))
|
||||
if (!ExpectedEntities.Remove(netEntity, out container))
|
||||
return false;
|
||||
|
||||
DebugTools.Assert(container.ExpectedEntities.Contains(uid),
|
||||
$"While removing expected contained entity {ToPrettyString(uid)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Remove(uid);
|
||||
DebugTools.Assert(container.ExpectedEntities.Contains(netEntity),
|
||||
$"While removing expected contained entity {ToPrettyString(netEntity)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Remove(netEntity);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
|
||||
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var toUpdate in _updateQueue)
|
||||
{
|
||||
if (Deleted(toUpdate))
|
||||
continue;
|
||||
|
||||
UpdateEntityRecursively(toUpdate, xformQuery, pointQuery, spriteQuery);
|
||||
UpdateEntityRecursively(toUpdate);
|
||||
}
|
||||
|
||||
_updateQueue.Clear();
|
||||
}
|
||||
|
||||
private void UpdateEntityRecursively(
|
||||
EntityUid entity,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery)
|
||||
private void UpdateEntityRecursively(EntityUid entity)
|
||||
{
|
||||
// Recursively go up parents and containers to see whether both sprites and lights need to be occluded
|
||||
// Could maybe optimise this more by checking nearest parent that has sprite / light and whether it's container
|
||||
// occluded but this probably isn't a big perf issue.
|
||||
var xform = xformQuery.GetComponent(entity);
|
||||
var xform = TransformQuery.GetComponent(entity);
|
||||
var parent = xform.ParentUid;
|
||||
var child = entity;
|
||||
var spriteOccluded = false;
|
||||
@@ -293,7 +292,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
while (parent.IsValid() && (!spriteOccluded || !lightOccluded))
|
||||
{
|
||||
var parentXform = xformQuery.GetComponent(parent);
|
||||
var parentXform = TransformQuery.GetComponent(parent);
|
||||
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
|
||||
{
|
||||
spriteOccluded = spriteOccluded || !container.ShowContents;
|
||||
@@ -308,24 +307,21 @@ namespace Robust.Client.GameObjects
|
||||
// This is the CBT bit.
|
||||
// The issue is we need to go through the children and re-check whether they are or are not contained.
|
||||
// if they are contained then the occlusion values may need updating for all those children
|
||||
UpdateEntity(entity, xform, xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
UpdateEntity(entity, xform, spriteOccluded, lightOccluded);
|
||||
}
|
||||
|
||||
private void UpdateEntity(
|
||||
EntityUid entity,
|
||||
TransformComponent xform,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery,
|
||||
bool spriteOccluded,
|
||||
bool lightOccluded)
|
||||
{
|
||||
if (spriteQuery.TryGetComponent(entity, out var sprite))
|
||||
if (_spriteQuery.TryGetComponent(entity, out var sprite))
|
||||
{
|
||||
sprite.ContainerOccluded = spriteOccluded;
|
||||
}
|
||||
|
||||
if (pointQuery.TryGetComponent(entity, out var light))
|
||||
if (_pointLightQuery.TryGetComponent(entity, out var light))
|
||||
_lightSys.SetContainerOccluded(entity, lightOccluded, light);
|
||||
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
@@ -346,14 +342,14 @@ namespace Robust.Client.GameObjects
|
||||
childLightOccluded = childLightOccluded || container.OccludesLight;
|
||||
}
|
||||
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, childSpriteOccluded, childLightOccluded);
|
||||
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), childSpriteOccluded, childLightOccluded);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), spriteOccluded, lightOccluded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
@@ -13,48 +14,61 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EyeComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<EyeComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<EyeComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeLocalEvent<EyeComponent, LocalPlayerDetachedEvent>(OnEyeDetached);
|
||||
SubscribeLocalEvent<EyeComponent, LocalPlayerAttachedEvent>(OnEyeAttached);
|
||||
SubscribeLocalEvent<EyeComponent, AfterAutoHandleStateEvent>(OnEyeAutoState);
|
||||
|
||||
// Make sure this runs *after* entities have been moved by interpolation and movement.
|
||||
UpdatesAfter.Add(typeof(TransformSystem));
|
||||
UpdatesAfter.Add(typeof(PhysicsSystem));
|
||||
}
|
||||
|
||||
private void OnEyeAutoState(EntityUid uid, EyeComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateEye((uid, component));
|
||||
}
|
||||
|
||||
private void OnEyeAttached(EntityUid uid, EyeComponent component, LocalPlayerAttachedEvent args)
|
||||
{
|
||||
UpdateEye((uid, component));
|
||||
_eyeManager.CurrentEye = component.Eye;
|
||||
var ev = new EyeAttachedEvent(uid, component);
|
||||
RaiseLocalEvent(uid, ref ev, true);
|
||||
}
|
||||
|
||||
private void OnEyeDetached(EntityUid uid, EyeComponent component, LocalPlayerDetachedEvent args)
|
||||
{
|
||||
_eyeManager.ClearCurrentEye();
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, EyeComponent component, ComponentInit args)
|
||||
{
|
||||
component._eye = new Eye
|
||||
{
|
||||
Position = Transform(uid).MapPosition,
|
||||
Zoom = component._setZoomOnInitialize,
|
||||
DrawFov = component._setDrawFovOnInitialize
|
||||
};
|
||||
|
||||
if ((_eyeManager.CurrentEye == component._eye) != component._setCurrentOnInitialize)
|
||||
{
|
||||
if (component._setCurrentOnInitialize)
|
||||
{
|
||||
_eyeManager.ClearCurrentEye();
|
||||
}
|
||||
else
|
||||
{
|
||||
_eyeManager.CurrentEye = component._eye;
|
||||
}
|
||||
}
|
||||
UpdateEye((uid, component));
|
||||
}
|
||||
|
||||
private void OnRemove(EntityUid uid, EyeComponent component, ComponentRemove args)
|
||||
/// <inheritdoc />
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
component.Current = false;
|
||||
}
|
||||
var query = AllEntityQuery<EyeComponent>();
|
||||
|
||||
private void OnHandleState(EntityUid uid, EyeComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not EyeComponentState state)
|
||||
while (query.MoveNext(out var uid, out var eyeComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (eyeComponent.Eye == null)
|
||||
continue;
|
||||
|
||||
component.DrawFov = state.DrawFov;
|
||||
// TODO: Should be a way for content to override lerping and lerp the zoom
|
||||
component.Zoom = state.Zoom;
|
||||
component.Offset = state.Offset;
|
||||
component.VisibilityMask = state.VisibilityMask;
|
||||
if (!TryComp<TransformComponent>(eyeComponent.Target, out var xform))
|
||||
{
|
||||
xform = Transform(uid);
|
||||
eyeComponent.Target = null;
|
||||
}
|
||||
|
||||
eyeComponent.Eye.Position = xform.MapPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity when it is attached to one with an <see cref="EyeComponent"/>
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct EyeAttachedEvent(EntityUid Entity, EyeComponent Component);
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the position of every Eye every frame, so that the camera follows the player around.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class EyeUpdateSystem : EntitySystem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
// Make sure this runs *after* entities have been moved by interpolation and movement.
|
||||
UpdatesAfter.Add(typeof(TransformSystem));
|
||||
UpdatesAfter.Add(typeof(PhysicsSystem));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
foreach (var eyeComponent in EntityManager.EntityQuery<EyeComponent>(true))
|
||||
{
|
||||
eyeComponent.UpdateEyePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
@@ -58,6 +59,8 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public GridChunkBoundsOverlay(IEntityManager entManager, IEyeManager eyeManager, IMapManager mapManager)
|
||||
{
|
||||
_entityManager = entManager;
|
||||
@@ -71,17 +74,19 @@ namespace Robust.Client.GameObjects
|
||||
var viewport = args.WorldBounds;
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(currentMap, viewport))
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(currentMap, viewport, ref _grids);
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var worldMatrix = _entityManager.GetComponent<TransformComponent>(grid.Owner).WorldMatrix;
|
||||
var worldMatrix = _entityManager.GetComponent<TransformComponent>(grid).WorldMatrix;
|
||||
worldHandle.SetTransform(worldMatrix);
|
||||
var transform = new Transform(Vector2.Zero, Angle.Zero);
|
||||
|
||||
var chunkEnumerator = grid.GetMapChunks(viewport);
|
||||
var chunkEnumerator = grid.Comp.GetMapChunks(viewport);
|
||||
|
||||
while (chunkEnumerator.MoveNext(out var chunk))
|
||||
{
|
||||
foreach (var fixture in chunk.Fixtures)
|
||||
foreach (var fixture in chunk.Fixtures.Values)
|
||||
{
|
||||
var poly = (PolygonShape) fixture.Shape;
|
||||
|
||||
|
||||
@@ -3,16 +3,13 @@ using System.Numerics;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -52,7 +49,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="message">Arguments for this event.</param>
|
||||
/// <param name="replay">if true, current cmd state will not be checked or updated - use this for "replaying" an
|
||||
/// old input that was saved or buffered until further processing could be done</param>
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, IFullInputCmdMessage message, bool replay = false)
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
@@ -78,14 +75,27 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
|
||||
// local handlers can block sending over the network.
|
||||
if (handler.HandleCmdMessage(session, message))
|
||||
if (handler.HandleCmdMessage(EntityManager, session, message))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// send it off to the server
|
||||
DispatchInputCommand(message);
|
||||
var clientMsg = (ClientFullInputCmdMessage)message;
|
||||
var fullMsg = new FullInputCmdMessage(
|
||||
clientMsg.Tick,
|
||||
clientMsg.SubTick,
|
||||
(int)clientMsg.InputSequence,
|
||||
clientMsg.InputFunctionId,
|
||||
clientMsg.State,
|
||||
GetNetCoordinates(clientMsg.Coordinates),
|
||||
clientMsg.ScreenCoordinates)
|
||||
{
|
||||
Uid = GetNetEntity(clientMsg.Uid)
|
||||
};
|
||||
|
||||
DispatchInputCommand(clientMsg, fullMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -93,7 +103,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Handle a predicted input command.
|
||||
/// </summary>
|
||||
/// <param name="inputCmd">Input command to handle as predicted.</param>
|
||||
public void PredictInputCommand(FullInputCmdMessage inputCmd)
|
||||
public void PredictInputCommand(IFullInputCmdMessage inputCmd)
|
||||
{
|
||||
DebugTools.AssertNotNull(_playerManager.LocalPlayer);
|
||||
|
||||
@@ -103,21 +113,22 @@ namespace Robust.Client.GameObjects
|
||||
var session = _playerManager.LocalPlayer!.Session;
|
||||
foreach (var handler in BindRegistry.GetHandlers(keyFunc))
|
||||
{
|
||||
if (handler.HandleCmdMessage(session, inputCmd)) break;
|
||||
if (handler.HandleCmdMessage(EntityManager, session, inputCmd))
|
||||
break;
|
||||
}
|
||||
Predicted = false;
|
||||
|
||||
}
|
||||
|
||||
private void DispatchInputCommand(FullInputCmdMessage message)
|
||||
private void DispatchInputCommand(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message)
|
||||
{
|
||||
_stateManager.InputCommandDispatched(message);
|
||||
_stateManager.InputCommandDispatched(clientMsg, message);
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message, message.InputSequence);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<PlayerAttachSysMessage>(OnAttachedEntityChanged);
|
||||
SubscribeLocalEvent<LocalPlayerAttachedEvent>(OnAttachedEntityChanged);
|
||||
|
||||
_conHost.RegisterCommand("incmd",
|
||||
"Inserts an input command into the simulation",
|
||||
@@ -152,16 +163,16 @@ namespace Robust.Client.GameObjects
|
||||
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
|
||||
|
||||
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
|
||||
coords, new ScreenCoordinates(0, 0, default), EntityUid.Invalid);
|
||||
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
|
||||
|
||||
HandleInputCommand(localPlayer.Session, keyFunction, message);
|
||||
}
|
||||
|
||||
private void OnAttachedEntityChanged(PlayerAttachSysMessage message)
|
||||
private void OnAttachedEntityChanged(LocalPlayerAttachedEvent message)
|
||||
{
|
||||
if (message.AttachedEntity != default) // attach
|
||||
if (message.Entity != default) // attach
|
||||
{
|
||||
SetEntityContextActive(_inputManager, message.AttachedEntity);
|
||||
SetEntityContextActive(_inputManager, message.Entity);
|
||||
}
|
||||
else // detach
|
||||
{
|
||||
@@ -206,49 +217,11 @@ namespace Robust.Client.GameObjects
|
||||
SetEntityContextActive(_inputManager, controlled);
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
protected override void PostInject()
|
||||
{
|
||||
base.PostInject();
|
||||
|
||||
_sawmillInputContext = _logManager.GetSawmill("input.context");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entity system message that is raised when the player changes attached entities.
|
||||
/// </summary>
|
||||
public sealed class PlayerAttachSysMessage : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// New entity the player is attached to.
|
||||
/// </summary>
|
||||
public EntityUid AttachedEntity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PlayerAttachSysMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="attachedEntity">New entity the player is attached to.</param>
|
||||
public PlayerAttachSysMessage(EntityUid attachedEntity)
|
||||
{
|
||||
AttachedEntity = attachedEntity;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerAttachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public EntityUid Entity { get; }
|
||||
}
|
||||
|
||||
public sealed class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerDetachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public EntityUid Entity { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -28,10 +25,5 @@ namespace Robust.Client.GameObjects
|
||||
base.Shutdown();
|
||||
_overlayManager.RemoveOverlay<TileEdgeOverlay>();
|
||||
}
|
||||
|
||||
protected override void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args)
|
||||
{
|
||||
EnsureComp<PhysicsMapComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -15,59 +17,108 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PointLightComponent, ComponentInit>(HandleInit);
|
||||
SubscribeLocalEvent<PointLightComponent, ComponentHandleState>(OnLightHandleState);
|
||||
}
|
||||
|
||||
private void OnLightHandleState(EntityUid uid, PointLightComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not PointLightComponentState state)
|
||||
return;
|
||||
|
||||
component.Enabled = state.Enabled;
|
||||
component.Offset = state.Offset;
|
||||
component.Softness = state.Softness;
|
||||
component.CastShadows = state.CastShadows;
|
||||
component.Energy = state.Energy;
|
||||
component.Radius = state.Radius;
|
||||
component.Color = state.Color;
|
||||
|
||||
_lightTree.QueueTreeUpdate(uid, component);
|
||||
}
|
||||
|
||||
public override SharedPointLightComponent EnsureLight(EntityUid uid)
|
||||
{
|
||||
return EnsureComp<PointLightComponent>(uid);
|
||||
}
|
||||
|
||||
public override bool ResolveLight(EntityUid uid, [NotNullWhen(true)] ref SharedPointLightComponent? component)
|
||||
{
|
||||
if (component is not null)
|
||||
return true;
|
||||
|
||||
TryComp<PointLightComponent>(uid, out var comp);
|
||||
component = comp;
|
||||
return component != null;
|
||||
}
|
||||
|
||||
public override bool TryGetLight(EntityUid uid, [NotNullWhen(true)] out SharedPointLightComponent? component)
|
||||
{
|
||||
if (TryComp<PointLightComponent>(uid, out var comp))
|
||||
{
|
||||
component = comp;
|
||||
return true;
|
||||
}
|
||||
|
||||
component = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RemoveLightDeferred(EntityUid uid)
|
||||
{
|
||||
return RemCompDeferred<PointLightComponent>(uid);
|
||||
}
|
||||
|
||||
private void HandleInit(EntityUid uid, PointLightComponent component, ComponentInit args)
|
||||
{
|
||||
UpdateMask(component);
|
||||
SetMask(component.MaskPath, component);
|
||||
}
|
||||
|
||||
internal void UpdateMask(PointLightComponent component)
|
||||
public void SetMask(string? maskPath, PointLightComponent component)
|
||||
{
|
||||
if (component._maskPath is not null)
|
||||
component.Mask = _resourceCache.GetResource<TextureResource>(component._maskPath);
|
||||
if (maskPath is not null)
|
||||
component.Mask = _resourceCache.GetResource<TextureResource>(maskPath);
|
||||
else
|
||||
component.Mask = null;
|
||||
}
|
||||
|
||||
#region Setters
|
||||
public void SetContainerOccluded(EntityUid uid, bool occluded, PointLightComponent? comp = null)
|
||||
|
||||
public void SetContainerOccluded(EntityUid uid, bool occluded, SharedPointLightComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp) || occluded == comp.ContainerOccluded)
|
||||
if (!ResolveLight(uid, ref comp) || occluded == comp.ContainerOccluded || comp is not PointLightComponent clientComp)
|
||||
return;
|
||||
|
||||
comp.ContainerOccluded = occluded;
|
||||
Dirty(uid, comp);
|
||||
|
||||
if (comp.Enabled)
|
||||
_lightTree.QueueTreeUpdate(uid, comp);
|
||||
_lightTree.QueueTreeUpdate(uid, clientComp);
|
||||
}
|
||||
|
||||
public override void SetEnabled(EntityUid uid, bool enabled, SharedPointLightComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp) || enabled == comp.Enabled)
|
||||
if (!ResolveLight(uid, ref comp) || enabled == comp.Enabled || comp is not PointLightComponent clientComp)
|
||||
return;
|
||||
|
||||
comp._enabled = enabled;
|
||||
comp.Enabled = enabled;
|
||||
RaiseLocalEvent(uid, new PointLightToggleEvent(comp.Enabled));
|
||||
Dirty(uid, comp);
|
||||
|
||||
var cast = (PointLightComponent)comp;
|
||||
if (!cast.ContainerOccluded)
|
||||
_lightTree.QueueTreeUpdate(uid, cast);
|
||||
if (!comp.ContainerOccluded)
|
||||
_lightTree.QueueTreeUpdate(uid, clientComp);
|
||||
}
|
||||
|
||||
public override void SetRadius(EntityUid uid, float radius, SharedPointLightComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp) || MathHelper.CloseToPercent(radius, comp.Radius))
|
||||
if (!ResolveLight(uid, ref comp) || MathHelper.CloseToPercent(radius, comp.Radius) ||
|
||||
comp is not PointLightComponent clientComp)
|
||||
return;
|
||||
|
||||
comp._radius = radius;
|
||||
comp.Radius = radius;
|
||||
Dirty(uid, comp);
|
||||
|
||||
var cast = (PointLightComponent)comp;
|
||||
if (cast.TreeUid != null)
|
||||
_lightTree.QueueTreeUpdate(uid, cast);
|
||||
if (clientComp.TreeUid != null)
|
||||
_lightTree.QueueTreeUpdate(uid, clientComp);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -15,6 +18,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -183,6 +187,48 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
_queuedFrameUpdate.Add(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified frame for this sprite at the specified time.
|
||||
/// </summary>
|
||||
public Texture GetFrame(SpriteSpecifier spriteSpec, TimeSpan curTime)
|
||||
{
|
||||
Texture? sprite = null;
|
||||
|
||||
switch (spriteSpec)
|
||||
{
|
||||
case SpriteSpecifier.Rsi rsi:
|
||||
var rsiActual = _resourceCache.GetResource<RSIResource>(rsi.RsiPath).RSI;
|
||||
rsiActual.TryGetState(rsi.RsiState, out var state);
|
||||
var frames = state!.GetFrames(RsiDirection.South);
|
||||
var delays = state.GetDelays();
|
||||
var totalDelay = delays.Sum();
|
||||
var time = curTime.TotalSeconds % totalDelay;
|
||||
var delaySum = 0f;
|
||||
|
||||
for (var i = 0; i < delays.Length; i++)
|
||||
{
|
||||
var delay = delays[i];
|
||||
delaySum += delay;
|
||||
|
||||
if (time > delaySum)
|
||||
continue;
|
||||
|
||||
sprite = frames[i];
|
||||
break;
|
||||
}
|
||||
|
||||
sprite ??= Frame0(spriteSpec);
|
||||
break;
|
||||
case SpriteSpecifier.Texture texture:
|
||||
sprite = texture.GetTexture(_resourceCache);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return sprite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,51 +1,41 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed partial class TransformSystem
|
||||
{
|
||||
public override void SetLocalPosition(TransformComponent xform, Vector2 value)
|
||||
public override void SetLocalPosition(EntityUid uid, Vector2 value, TransformComponent? xform = null)
|
||||
{
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
if (!XformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
xform.NextPosition = value;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
base.SetLocalPosition(xform, value);
|
||||
ActivateLerp(xform);
|
||||
ActivateLerp(uid, xform);
|
||||
base.SetLocalPosition(uid, value, xform);
|
||||
}
|
||||
|
||||
public override void SetLocalPositionNoLerp(TransformComponent xform, Vector2 value)
|
||||
public override void SetLocalRotation(EntityUid uid, Angle value, TransformComponent? xform = null)
|
||||
{
|
||||
xform.NextPosition = null;
|
||||
xform.LerpParent = EntityUid.Invalid;
|
||||
base.SetLocalPositionNoLerp(xform, value);
|
||||
if (!XformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
xform.NextRotation = value;
|
||||
ActivateLerp(uid, xform);
|
||||
base.SetLocalRotation(uid, value, xform);
|
||||
}
|
||||
|
||||
public override void SetLocalRotationNoLerp(TransformComponent xform, Angle angle)
|
||||
public override void SetLocalPositionRotation(EntityUid uid, Vector2 pos, Angle rot, TransformComponent? xform = null)
|
||||
{
|
||||
xform.NextRotation = null;
|
||||
xform.LerpParent = EntityUid.Invalid;
|
||||
base.SetLocalRotationNoLerp(xform, angle);
|
||||
}
|
||||
if (!XformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
public override void SetLocalRotation(TransformComponent xform, Angle angle)
|
||||
{
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.NextRotation = angle;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
base.SetLocalRotation(xform, angle);
|
||||
ActivateLerp(xform);
|
||||
}
|
||||
|
||||
public override void SetLocalPositionRotation(TransformComponent xform, Vector2 pos, Angle rot)
|
||||
{
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.NextPosition = pos;
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.NextRotation = rot;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
base.SetLocalPositionRotation(xform, pos, rot);
|
||||
ActivateLerp(xform);
|
||||
ActivateLerp(uid, xform);
|
||||
base.SetLocalPositionRotation(uid, pos, rot, xform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
@@ -25,20 +24,15 @@ namespace Robust.Client.GameObjects
|
||||
private const float MinInterpolationDistance = 0.001f;
|
||||
private const float MinInterpolationDistanceSquared = MinInterpolationDistance * MinInterpolationDistance;
|
||||
|
||||
private const double MinInterpolationAngle = Math.PI / 720;
|
||||
|
||||
// 45 degrees.
|
||||
private const double MaxInterpolationAngle = Math.PI / 4;
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
// Only keep track of transforms actively lerping.
|
||||
// Much faster than iterating 3000+ transforms every frame.
|
||||
[ViewVariables] private readonly List<TransformComponent> _lerpingTransforms = new();
|
||||
[ViewVariables] private readonly List<Entity<TransformComponent>> _lerpingTransforms = new();
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
foreach (var xform in _lerpingTransforms)
|
||||
foreach (var (_, xform) in _lerpingTransforms)
|
||||
{
|
||||
xform.ActivelyLerping = false;
|
||||
xform.NextPosition = null;
|
||||
@@ -48,21 +42,78 @@ namespace Robust.Client.GameObjects
|
||||
_lerpingTransforms.Clear();
|
||||
}
|
||||
|
||||
public override void ActivateLerp(TransformComponent xform)
|
||||
public override void ActivateLerp(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
if (xform.ActivelyLerping)
|
||||
// This lerping logic is pretty convoluted and generally assumes that the client does not mispredict.
|
||||
// A more foolproof solution would be to just cache the coordinates at which any given entity was most
|
||||
// recently rendered and using that as the lerp origin. However that'd require enumerating over all entities
|
||||
// every tick which is pretty icky.
|
||||
|
||||
// The general considerations are:
|
||||
// - If the client receives a server state for an entity moving from a->b and predicts nothing else, then it
|
||||
// should show the entity lerping.
|
||||
// - If the client predicts an entity will move while already lerping due to a state-application, it should
|
||||
// clear the state's lerp, under the assumption that the client predicted the state and already rendered
|
||||
// the entity in the state's final position.
|
||||
// - If the client predicts that an entity moves, then we only lerp if this is the first time that the tick
|
||||
// was predicted. I.e., we assume the entity was already rendered in the final position that was
|
||||
// previously predicted.
|
||||
// - If the client predicts that an entity should lerp twice in the same tick, then we need to combine them.
|
||||
// I.e. moving from a->b then b->c, the client should lerp from a->c.
|
||||
|
||||
// If the client predicts an entity moves while already lerping, it should clear the
|
||||
// predict a->b, lerp a->b
|
||||
// predicted a->b, then predict b->c. Lerp b->c
|
||||
// predicted a->b, then predict b->c. Lerp b->c
|
||||
// predicted a->b, predicted b->c, then predict c->d. Lerp c->d
|
||||
// server state a->b, then predicted b->c, lerp b->c
|
||||
// server state a->b, then predicted b->c, then predict d, lerp b->c
|
||||
|
||||
if (_gameTiming.ApplyingState)
|
||||
{
|
||||
if (xform.ActivelyLerping)
|
||||
{
|
||||
// This should not happen, but can happen if some bad component state application code modifies an entity's coordinates.
|
||||
Log.Error($"Entity {(ToPrettyString(uid))} tried to lerp twice while applying component states.");
|
||||
return;
|
||||
}
|
||||
|
||||
_lerpingTransforms.Add((uid, xform));
|
||||
xform.ActivelyLerping = true;
|
||||
xform.PredictedLerp = false;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.LastLerp = _gameTiming.CurTick;
|
||||
return;
|
||||
}
|
||||
|
||||
xform.ActivelyLerping = true;
|
||||
_lerpingTransforms.Add(xform);
|
||||
}
|
||||
xform.LastLerp = _gameTiming.CurTick;
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
{
|
||||
xform.ActivelyLerping = false;
|
||||
return;
|
||||
}
|
||||
|
||||
public override void DeactivateLerp(TransformComponent component)
|
||||
{
|
||||
// this should cause the lerp to do nothing
|
||||
component.NextPosition = null;
|
||||
component.NextRotation = null;
|
||||
component.LerpParent = EntityUid.Invalid;
|
||||
if (!xform.ActivelyLerping)
|
||||
{
|
||||
_lerpingTransforms.Add((uid, xform));
|
||||
xform.ActivelyLerping = true;
|
||||
xform.PredictedLerp = true;
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xform.PredictedLerp || xform.LerpParent != xform.ParentUid)
|
||||
{
|
||||
// Existing lerp was not due to prediction, but due to state application. That lerp should already
|
||||
// have been rendered, so we will start a new lerp from the current position.
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
}
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
@@ -73,12 +124,13 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
for (var i = 0; i < _lerpingTransforms.Count; i++)
|
||||
{
|
||||
var transform = _lerpingTransforms[i];
|
||||
var (uid, transform) = _lerpingTransforms[i];
|
||||
var found = false;
|
||||
|
||||
// Only lerp if parent didn't change.
|
||||
// E.g. entering lockers would do it.
|
||||
if (transform.LerpParent == transform.ParentUid
|
||||
if (transform.ActivelyLerping
|
||||
&& transform.LerpParent == transform.ParentUid
|
||||
&& transform.ParentUid.IsValid()
|
||||
&& !transform.Deleted)
|
||||
{
|
||||
@@ -90,8 +142,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
if (distance is > MinInterpolationDistanceSquared and < MaxInterpolationDistanceSquared)
|
||||
{
|
||||
transform.LocalPosition = Vector2.Lerp(lerpSource, lerpDest, step);
|
||||
// Setting LocalPosition clears LerpPosition so fix that.
|
||||
SetLocalPositionNoLerp(uid, Vector2.Lerp(lerpSource, lerpDest, step), transform);
|
||||
transform.NextPosition = lerpDest;
|
||||
found = true;
|
||||
}
|
||||
@@ -101,15 +152,9 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
var lerpDest = transform.NextRotation.Value;
|
||||
var lerpSource = transform.PrevRotation;
|
||||
var distance = Math.Abs(Angle.ShortestDistance(lerpDest, lerpSource));
|
||||
|
||||
if (distance is > MinInterpolationAngle and < MaxInterpolationAngle)
|
||||
{
|
||||
transform.LocalRotation = Angle.Lerp(lerpSource, lerpDest, step);
|
||||
// Setting LocalRotation clears LerpAngle so fix that.
|
||||
transform.NextRotation = lerpDest;
|
||||
found = true;
|
||||
}
|
||||
SetLocalRotationNoLerp(uid, Angle.Lerp(lerpSource, lerpDest, step), transform);
|
||||
transform.NextRotation = lerpDest;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using System;
|
||||
using UserInterfaceComponent = Robust.Shared.GameObjects.UserInterfaceComponent;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
|
||||
{
|
||||
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
|
||||
@@ -19,41 +18,22 @@ namespace Robust.Client.GameObjects
|
||||
base.Initialize();
|
||||
|
||||
SubscribeNetworkEvent<BoundUIWrapMessage>(MessageReceived);
|
||||
SubscribeLocalEvent<ClientUserInterfaceComponent, ComponentInit>(OnUserInterfaceInit);
|
||||
SubscribeLocalEvent<ClientUserInterfaceComponent, ComponentShutdown>(OnUserInterfaceShutdown);
|
||||
}
|
||||
|
||||
private void OnUserInterfaceInit(EntityUid uid, ClientUserInterfaceComponent component, ComponentInit args)
|
||||
{
|
||||
component._interfaces.Clear();
|
||||
|
||||
foreach (var data in component._interfaceData)
|
||||
{
|
||||
component._interfaces[data.UiKey] = data;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUserInterfaceShutdown(EntityUid uid, ClientUserInterfaceComponent component, ComponentShutdown args)
|
||||
{
|
||||
foreach (var bui in component.OpenInterfaces.Values)
|
||||
{
|
||||
bui.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void MessageReceived(BoundUIWrapMessage ev)
|
||||
{
|
||||
var uid = ev.Entity;
|
||||
if (!TryComp<ClientUserInterfaceComponent>(uid, out var cmp))
|
||||
var uid = GetEntity(ev.Entity);
|
||||
|
||||
if (!TryComp<UserInterfaceComponent>(uid, out var cmp))
|
||||
return;
|
||||
|
||||
var uiKey = ev.UiKey;
|
||||
var message = ev.Message;
|
||||
// This should probably not happen at this point, but better make extra sure!
|
||||
if(_playerManager.LocalPlayer != null)
|
||||
if (_playerManager.LocalPlayer != null)
|
||||
message.Session = _playerManager.LocalPlayer.Session;
|
||||
|
||||
message.Entity = uid;
|
||||
message.Entity = GetNetEntity(uid);
|
||||
message.UiKey = uiKey;
|
||||
|
||||
// Raise as object so the correct type is used.
|
||||
@@ -66,7 +46,7 @@ namespace Robust.Client.GameObjects
|
||||
break;
|
||||
|
||||
case CloseBoundInterfaceMessage _:
|
||||
TryCloseUi(uid, uiKey, remoteCall: true, uiComp: cmp);
|
||||
TryCloseUi(message.Session, uid, uiKey, remoteCall: true, uiComp: cmp);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -77,7 +57,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryOpenUi(EntityUid uid, Enum uiKey, ClientUserInterfaceComponent? uiComp = null)
|
||||
private bool TryOpenUi(EntityUid uid, Enum uiKey, UserInterfaceComponent? uiComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref uiComp))
|
||||
return false;
|
||||
@@ -85,7 +65,7 @@ namespace Robust.Client.GameObjects
|
||||
if (uiComp.OpenInterfaces.ContainsKey(uiKey))
|
||||
return false;
|
||||
|
||||
var data = uiComp._interfaces[uiKey];
|
||||
var data = uiComp.MappedInterfaceData[uiKey];
|
||||
|
||||
// TODO: This type should be cached, but I'm too lazy.
|
||||
var type = _reflectionManager.LooseGetType(data.ClientType);
|
||||
@@ -96,36 +76,13 @@ namespace Robust.Client.GameObjects
|
||||
uiComp.OpenInterfaces[uiKey] = boundInterface;
|
||||
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if(playerSession != null)
|
||||
if (playerSession != null)
|
||||
{
|
||||
uiComp.Interfaces[uiKey]._subscribedSessions.Add(playerSession);
|
||||
RaiseLocalEvent(uid, new BoundUIOpenedEvent(uiKey, uid, playerSession), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool TryCloseUi(EntityUid uid, Enum uiKey, bool remoteCall = false, ClientUserInterfaceComponent? uiComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref uiComp))
|
||||
return false;
|
||||
|
||||
if (!uiComp.OpenInterfaces.TryGetValue(uiKey, out var boundUserInterface))
|
||||
return false;
|
||||
|
||||
if (!remoteCall)
|
||||
SendUiMessage(boundUserInterface, new CloseBoundInterfaceMessage());
|
||||
|
||||
uiComp.OpenInterfaces.Remove(uiKey);
|
||||
boundUserInterface.Dispose();
|
||||
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if(playerSession != null)
|
||||
RaiseLocalEvent(uid, new BoundUIClosedEvent(uiKey, uid, playerSession), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
|
||||
{
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, msg, bui.UiKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// These methods are used by the Game State Manager.
|
||||
|
||||
EntityUid CreateEntity(string? prototypeName, EntityUid uid = default);
|
||||
EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata);
|
||||
|
||||
void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -15,7 +14,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
|
||||
|
||||
// Entities that have removed networked components
|
||||
// could pool the ushort sets, but predicted component changes are rare... soo...
|
||||
internal readonly Dictionary<EntityUid, HashSet<ushort>> RemovedComponents = new();
|
||||
@@ -40,11 +39,11 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnTerminate(ref EntityTerminatingEvent ev)
|
||||
{
|
||||
if (!_timing.InPrediction || ev.Entity.IsClientSide())
|
||||
if (!_timing.InPrediction || IsClientSide(ev.Entity, ev.Metadata))
|
||||
return;
|
||||
|
||||
// Client-side entity deletion is not supported and will cause errors.
|
||||
Logger.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
|
||||
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
|
||||
}
|
||||
|
||||
private void OnCompRemoved(RemovedComponentEventArgs args)
|
||||
@@ -52,8 +51,9 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
if (args.Terminating)
|
||||
return;
|
||||
|
||||
var uid = args.BaseArgs.Owner;
|
||||
var comp = args.BaseArgs.Component;
|
||||
if (!_timing.InPrediction || comp.Owner.IsClientSide() || !comp.NetSyncEnabled)
|
||||
if (!_timing.InPrediction || !comp.NetSyncEnabled || IsClientSide(uid, args.Meta))
|
||||
return;
|
||||
|
||||
// Was this component added during prediction? If yes, then there is no need to re-add it when resetting.
|
||||
@@ -62,7 +62,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
var netId = _compFact.GetRegistration(comp).NetID;
|
||||
if (netId != null)
|
||||
RemovedComponents.GetOrNew(comp.Owner).Add(netId.Value);
|
||||
RemovedComponents.GetOrNew(uid).Add(netId.Value);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -73,7 +73,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnEntityDirty(EntityUid e)
|
||||
{
|
||||
if (_timing.InPrediction && !e.IsClientSide())
|
||||
if (_timing.InPrediction && !IsClientSide(e))
|
||||
DirtyEntities.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal sealed class GameStateProcessor : IGameStateProcessor, IPostInjectInit
|
||||
internal sealed class GameStateProcessor : IGameStateProcessor
|
||||
{
|
||||
[Dependency] private ILogManager _logMan = default!;
|
||||
|
||||
private readonly IClientGameTiming _timing;
|
||||
private readonly IClientGameStateManager _state;
|
||||
private readonly ISawmill _logger;
|
||||
|
||||
private readonly List<GameState> _stateBuffer = new();
|
||||
|
||||
private readonly Dictionary<GameTick, List<EntityUid>> _pvsDetachMessages = new();
|
||||
|
||||
private ISawmill _logger = default!;
|
||||
private ISawmill _stateLogger = default!;
|
||||
|
||||
private readonly Dictionary<GameTick, List<NetEntity>> _pvsDetachMessages = new();
|
||||
public GameState? LastFullState { get; private set; }
|
||||
public bool WaitingForFull => LastFullStateRequested.HasValue;
|
||||
public GameTick? LastFullStateRequested
|
||||
{
|
||||
get => _lastFullStateRequested;
|
||||
set
|
||||
{
|
||||
_lastFullStateRequested = value;
|
||||
LastFullState = null;
|
||||
}
|
||||
}
|
||||
|
||||
public GameTick? _lastFullStateRequested = GameTick.Zero;
|
||||
public (GameTick Tick, DateTime Time)? LastFullStateRequested { get; private set; } = (GameTick.Zero, DateTime.MaxValue);
|
||||
|
||||
private int _bufferSize;
|
||||
private int _maxBufferSize = 512;
|
||||
public const int MinimumMaxBufferSize = 256;
|
||||
|
||||
/// <summary>
|
||||
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
|
||||
/// </summary>
|
||||
internal readonly Dictionary<EntityUid, Dictionary<ushort, ComponentState>> _lastStateFullRep
|
||||
internal readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _lastStateFullRep
|
||||
= new();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -60,7 +48,14 @@ namespace Robust.Client.GameStates
|
||||
public int BufferSize
|
||||
{
|
||||
get => _bufferSize;
|
||||
set => _bufferSize = value < 0 ? 0 : value;
|
||||
set => _bufferSize = Math.Max(value, 0);
|
||||
}
|
||||
|
||||
public int MaxBufferSize
|
||||
{
|
||||
get => _maxBufferSize;
|
||||
// We place a lower bound on the maximum size to avoid spamming servers with full game state requests.
|
||||
set => _maxBufferSize = Math.Max(value, MinimumMaxBufferSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -70,9 +65,12 @@ namespace Robust.Client.GameStates
|
||||
/// Constructs a new instance of <see cref="GameStateProcessor"/>.
|
||||
/// </summary>
|
||||
/// <param name="timing">Timing information of the current state.</param>
|
||||
public GameStateProcessor(IClientGameTiming timing)
|
||||
/// <param name="clientGameStateManager"></param>
|
||||
public GameStateProcessor(IClientGameStateManager state, IClientGameTiming timing, ISawmill logger)
|
||||
{
|
||||
_timing = timing;
|
||||
_state = state;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -82,7 +80,7 @@ namespace Robust.Client.GameStates
|
||||
if (state.ToSequence <= _timing.LastRealTick)
|
||||
{
|
||||
if (Logging)
|
||||
_stateLogger.Debug($"Received Old GameState: lastRealTick={_timing.LastRealTick}, fSeq={state.FromSequence}, tSeq={state.ToSequence}, sz={state.PayloadSize}, buf={_stateBuffer.Count}");
|
||||
_logger.Debug($"Received Old GameState: lastRealTick={_timing.LastRealTick}, fSeq={state.FromSequence}, tSeq={state.ToSequence}, sz={state.PayloadSize}, buf={_stateBuffer.Count}");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -94,7 +92,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
|
||||
if (Logging)
|
||||
_stateLogger.Debug($"Received Dupe GameState: lastRealTick={_timing.LastRealTick}, fSeq={state.FromSequence}, tSeq={state.ToSequence}, sz={state.PayloadSize}, buf={_stateBuffer.Count}");
|
||||
_logger.Debug($"Received Dupe GameState: lastRealTick={_timing.LastRealTick}, fSeq={state.FromSequence}, tSeq={state.ToSequence}, sz={state.PayloadSize}, buf={_stateBuffer.Count}");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -103,34 +101,68 @@ namespace Robust.Client.GameStates
|
||||
if (!WaitingForFull)
|
||||
{
|
||||
// This is a good state that we will be using.
|
||||
_stateBuffer.Add(state);
|
||||
TryAdd(state);
|
||||
if (Logging)
|
||||
_stateLogger.Debug($"Received New GameState: lastRealTick={_timing.LastRealTick}, fSeq={state.FromSequence}, tSeq={state.ToSequence}, sz={state.PayloadSize}, buf={_stateBuffer.Count}");
|
||||
_logger.Debug($"Received New GameState: lastRealTick={_timing.LastRealTick}, fSeq={state.FromSequence}, tSeq={state.ToSequence}, sz={state.PayloadSize}, buf={_stateBuffer.Count}");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LastFullState == null && state.FromSequence == GameTick.Zero && state.ToSequence >= LastFullStateRequested!.Value)
|
||||
if (LastFullState == null && state.FromSequence == GameTick.Zero)
|
||||
{
|
||||
LastFullState = state;
|
||||
|
||||
if (Logging)
|
||||
if (state.ToSequence >= LastFullStateRequested!.Value.Tick)
|
||||
{
|
||||
LastFullState = state;
|
||||
_logger.Info($"Received Full GameState: to={state.ToSequence}, sz={state.PayloadSize}");
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
_logger.Info($"Received a late full game state. Received: {state.ToSequence}. Requested: {LastFullStateRequested.Value.Tick}");
|
||||
}
|
||||
|
||||
if (LastFullState != null && state.ToSequence <= LastFullState.ToSequence)
|
||||
{
|
||||
if (Logging)
|
||||
_logger.Info($"While waiting for full, received late GameState with lower to={state.ToSequence} than the last full state={LastFullState.ToSequence}");
|
||||
|
||||
_logger.Info($"While waiting for full, received late GameState with lower to={state.ToSequence} than the last full state={LastFullState.ToSequence}");
|
||||
return false;
|
||||
}
|
||||
|
||||
_stateBuffer.Add(state);
|
||||
TryAdd(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TryAdd(GameState state)
|
||||
{
|
||||
if (_stateBuffer.Count <= MaxBufferSize)
|
||||
{
|
||||
_stateBuffer.Add(state);
|
||||
return;
|
||||
}
|
||||
|
||||
// This can happen if a required state gets dropped somehow and the client keeps receiving future
|
||||
// game states that they can't apply. I.e., GetApplicableStateCount() is zero, even though there are many
|
||||
// states in the list.
|
||||
//
|
||||
// This can seemingly happen when the server sends ""reliable"" game states while the client is paused?
|
||||
// For example, when debugging the client, while the server is running:
|
||||
// - The client stops sending acks for states that the server sends out.
|
||||
// - Thus the client will exceed the net.force_ack_threshold cvar
|
||||
// - The server starts sending some packets ""reliably"" and just force updates the clients last ack.
|
||||
//
|
||||
// What should happen is that when the client resumes, it receives the reliably sent states and can just
|
||||
// resume. However, even though the packets are sent ""reliably"", they just seem to get dropped.
|
||||
// I don't quite understand how/why yet, but this ensures the client doesn't get stuck.
|
||||
#if FULL_RELEASE
|
||||
_logger.Warning(@$"Exceeded maximum state buffer size!
|
||||
Tick: {_timing.CurTick}/{_timing.LastProcessedTick}/{_timing.LastRealTick}
|
||||
Size: {_stateBuffer.Count}
|
||||
Applicable states: {GetApplicableStateCount()}
|
||||
Was waiting for full: {WaitingForFull} {LastFullStateRequested}
|
||||
Had full state: {LastFullState != null}"
|
||||
);
|
||||
#endif
|
||||
|
||||
_state.RequestFullState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the current and next states to apply.
|
||||
/// </summary>
|
||||
@@ -151,7 +183,7 @@ namespace Robust.Client.GameStates
|
||||
"Tried to apply a non-extrapolated state that has too high of a FromSequence!");
|
||||
|
||||
if (Logging)
|
||||
_stateLogger.Debug($"Applying State: cTick={_timing.LastProcessedTick}, fSeq={curState.FromSequence}, tSeq={curState.ToSequence}, buf={_stateBuffer.Count}");
|
||||
_logger.Debug($"Applying State: cTick={_timing.LastProcessedTick}, fSeq={curState.FromSequence}, tSeq={curState.ToSequence}, buf={_stateBuffer.Count}");
|
||||
}
|
||||
|
||||
return applyNextState;
|
||||
@@ -178,10 +210,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var entityState in state.EntityStates.Span)
|
||||
{
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.Uid, out var compData))
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
|
||||
{
|
||||
compData = new Dictionary<ushort, ComponentState>();
|
||||
_lastStateFullRep.Add(entityState.Uid, compData);
|
||||
_lastStateFullRep.Add(entityState.NetEntity, compData);
|
||||
}
|
||||
|
||||
foreach (var change in entityState.ComponentChanges.Span)
|
||||
@@ -263,7 +295,7 @@ namespace Robust.Client.GameStates
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void AddLeavePvsMessage(List<EntityUid> entities, GameTick tick)
|
||||
internal void AddLeavePvsMessage(List<NetEntity> entities, GameTick tick)
|
||||
{
|
||||
// Late message may still need to be processed,
|
||||
DebugTools.Assert(entities.Count > 0);
|
||||
@@ -272,9 +304,8 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public void ClearDetachQueue() => _pvsDetachMessages.Clear();
|
||||
|
||||
public List<(GameTick Tick, List<EntityUid> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
|
||||
public void GetEntitiesToDetach(IList<(GameTick Tick, List<NetEntity> Entities)> result, GameTick toTick, int budget)
|
||||
{
|
||||
var result = new List<(GameTick Tick, List<EntityUid> Entities)>();
|
||||
foreach (var (tick, entities) in _pvsDetachMessages)
|
||||
{
|
||||
if (tick > toTick)
|
||||
@@ -293,7 +324,6 @@ namespace Robust.Client.GameStates
|
||||
entities.RemoveRange(index, budget);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TryGetDeltaState(out GameState? curState, out GameState? nextState)
|
||||
@@ -343,27 +373,35 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
_stateBuffer.Clear();
|
||||
LastFullState = null;
|
||||
LastFullStateRequested = GameTick.Zero;
|
||||
LastFullStateRequested = (GameTick.Zero, DateTime.MaxValue);
|
||||
}
|
||||
|
||||
public void RequestFullState()
|
||||
public void OnFullStateRequested(GameTick tick)
|
||||
{
|
||||
_stateBuffer.Clear();
|
||||
LastFullState = null;
|
||||
LastFullStateRequested = _timing.LastRealTick;
|
||||
LastFullStateRequested = (tick, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
public void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> implicitData)
|
||||
public void OnFullStateReceived()
|
||||
{
|
||||
foreach (var (uid, implicitEntState) in implicitData)
|
||||
LastFullState = null;
|
||||
LastFullStateRequested = null;
|
||||
}
|
||||
|
||||
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> implicitData)
|
||||
{
|
||||
foreach (var (netEntity, implicitEntState) in implicitData)
|
||||
{
|
||||
var fullRep = _lastStateFullRep[uid];
|
||||
var fullRep = _lastStateFullRep[netEntity];
|
||||
|
||||
foreach (var (netId, implicitCompState) in implicitEntState)
|
||||
{
|
||||
if (!fullRep.TryGetValue(netId, out var serverState))
|
||||
ref var serverState = ref CollectionsMarshal.GetValueRefOrAddDefault(fullRep, netId, out var exists);
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
fullRep.Add(netId, implicitCompState);
|
||||
serverState = implicitCompState;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -374,37 +412,50 @@ namespace Robust.Client.GameStates
|
||||
// state from the entity prototype.
|
||||
if (implicitCompState is not IComponentDeltaState implicitDelta || !implicitDelta.FullState)
|
||||
{
|
||||
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {uid}");
|
||||
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {netEntity}");
|
||||
continue;
|
||||
}
|
||||
|
||||
serverDelta.ApplyToFullState(implicitCompState);
|
||||
fullRep[netId] = implicitCompState;
|
||||
serverState = implicitCompState;
|
||||
DebugTools.Assert(implicitCompState is IComponentDeltaState d && d.FullState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity)
|
||||
public Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity netEntity)
|
||||
{
|
||||
return _lastStateFullRep[entity];
|
||||
return _lastStateFullRep[netEntity];
|
||||
}
|
||||
|
||||
public Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
{
|
||||
return _lastStateFullRep;
|
||||
}
|
||||
|
||||
public bool TryGetLastServerStates(EntityUid entity,
|
||||
public bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary)
|
||||
{
|
||||
return _lastStateFullRep.TryGetValue(entity, out dictionary);
|
||||
}
|
||||
|
||||
public int CalculateBufferSize(GameTick fromTick)
|
||||
public bool IsQueuedForDetach(NetEntity entity)
|
||||
{
|
||||
// This isn't fast, but its just meant for use in tests & debug asserts.
|
||||
foreach (var msg in _pvsDetachMessages.Values)
|
||||
{
|
||||
if (msg.Contains(entity))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetApplicableStateCount(GameTick? fromTick = null)
|
||||
{
|
||||
fromTick ??= _timing.LastRealTick;
|
||||
bool foundState;
|
||||
var nextTick = fromTick;
|
||||
var nextTick = fromTick.Value;
|
||||
|
||||
do
|
||||
{
|
||||
@@ -422,13 +473,9 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
while (foundState);
|
||||
|
||||
return (int) (nextTick.Value - fromTick.Value);
|
||||
return (int) (nextTick.Value - fromTick.Value.Value);
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_logger = _logMan.GetSawmill("net");
|
||||
_stateLogger = _logMan.GetSawmill("net.state");
|
||||
}
|
||||
public int StateCount => _stateBuffer.Count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,15 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Number of applicable game states currently in the state buffer.
|
||||
/// </summary>
|
||||
int CurrentBufferSize { get; }
|
||||
int GetApplicableStateCount();
|
||||
|
||||
[Obsolete("use GetApplicableStateCount()")]
|
||||
int CurrentBufferSize => GetApplicableStateCount();
|
||||
|
||||
/// <summary>
|
||||
/// Total number of game states currently in the state buffer.
|
||||
/// </summary>
|
||||
int StateCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If the buffer size is this many states larger than the target buffer size,
|
||||
@@ -75,7 +83,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Applies a given set of game states.
|
||||
/// </summary>
|
||||
IEnumerable<EntityUid> ApplyGameState(GameState curState, GameState? nextState);
|
||||
IEnumerable<NetEntity> ApplyGameState(GameState curState, GameState? nextState);
|
||||
|
||||
/// <summary>
|
||||
/// Resets any entities that have changed while predicting future ticks.
|
||||
@@ -86,12 +94,12 @@ namespace Robust.Client.GameStates
|
||||
/// An input command has been dispatched.
|
||||
/// </summary>
|
||||
/// <param name="message">Message being dispatched.</param>
|
||||
void InputCommandDispatched(FullInputCmdMessage message);
|
||||
void InputCommandDispatched(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Requests a full state from the server. This should override even implicit entity data.
|
||||
/// </summary>
|
||||
void RequestFullState(EntityUid? missingEntity = null);
|
||||
void RequestFullState(NetEntity? missingEntity = null, GameTick? tick = null);
|
||||
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
|
||||
|
||||
@@ -105,7 +113,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Returns the full collection of cached game states that are used to reset predicted entities.
|
||||
/// </summary>
|
||||
Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep();
|
||||
Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep();
|
||||
|
||||
/// <summary>
|
||||
/// This will perform some setup in order to reset the game to an earlier state. To fully reset the state
|
||||
@@ -144,12 +152,12 @@ namespace Robust.Client.GameStates
|
||||
/// Queue a collection of entities that are to be detached to null-space & marked as PVS-detached.
|
||||
/// This store and modify the list given to it.
|
||||
/// </summary>
|
||||
void QueuePvsDetach(List<EntityUid> entities, GameTick tick);
|
||||
void QueuePvsDetach(List<NetEntity> entities, GameTick tick);
|
||||
|
||||
/// <summary>
|
||||
/// Immediately detach several entities.
|
||||
/// </summary>
|
||||
void DetachImmediate(List<EntityUid> entities);
|
||||
void DetachImmediate(List<NetEntity> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the PVS detach queue.
|
||||
|
||||
@@ -83,22 +83,22 @@ namespace Robust.Client.GameStates
|
||||
/// The data to merge.
|
||||
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
|
||||
/// </param>
|
||||
void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> data);
|
||||
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> data);
|
||||
|
||||
/// <summary>
|
||||
/// Get the last state data from the server for an entity.
|
||||
/// </summary>
|
||||
/// <returns>Dictionary (net ID -> ComponentState)</returns>
|
||||
Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity);
|
||||
Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the number of applicable states in the game state buffer from a given tick.
|
||||
/// This includes only applicable states. If there is a gap, future buffers are not included.
|
||||
/// </summary>
|
||||
/// <param name="fromTick">The tick to calculate from.</param>
|
||||
int CalculateBufferSize(GameTick fromTick);
|
||||
int GetApplicableStateCount(GameTick? fromTick);
|
||||
|
||||
bool TryGetLastServerStates(EntityUid entity,
|
||||
bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private readonly Font _font;
|
||||
private readonly int _lineHeight;
|
||||
private readonly Dictionary<EntityUid, NetEntData> _netEnts = new();
|
||||
private readonly Dictionary<NetEntity, NetEntData> _netEnts = new();
|
||||
|
||||
public NetEntityOverlay()
|
||||
{
|
||||
@@ -77,12 +77,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var entityState in gameState.EntityStates.Span)
|
||||
{
|
||||
if (!_netEnts.TryGetValue(entityState.Uid, out var netEnt))
|
||||
if (!_netEnts.TryGetValue(entityState.NetEntity, out var netEnt))
|
||||
{
|
||||
if (_netEnts.Count >= _maxEnts)
|
||||
continue;
|
||||
|
||||
_netEnts[entityState.Uid] = netEnt = new();
|
||||
_netEnts[entityState.NetEntity] = netEnt = new();
|
||||
}
|
||||
|
||||
if (!netEnt.InPVS && netEnt.LastUpdate < gameState.ToSequence)
|
||||
@@ -119,11 +119,13 @@ namespace Robust.Client.GameStates
|
||||
var screenHandle = args.ScreenHandle;
|
||||
|
||||
int i = 0;
|
||||
foreach (var (uid, netEnt) in _netEnts)
|
||||
foreach (var (nent, netEnt) in _netEnts)
|
||||
{
|
||||
var uid = _entityManager.GetEntity(nent);
|
||||
|
||||
if (!_entityManager.EntityExists(uid))
|
||||
{
|
||||
_netEnts.Remove(uid);
|
||||
_netEnts.Remove(nent);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Robust.Client.GameStates
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
private const int HistorySize = 60 * 5; // number of ticks to keep in history.
|
||||
private const int TargetPayloadBps = 56000 / 8; // Target Payload size in Bytes per second. A mind-numbing fifty-six thousand bits per second, who would ever need more?
|
||||
@@ -67,13 +68,13 @@ namespace Robust.Client.GameStates
|
||||
var lag = _netManager.ServerChannel!.Ping;
|
||||
|
||||
// calc interp info
|
||||
var buffer = _gameStateManager.CurrentBufferSize;
|
||||
var buffer = _gameStateManager.GetApplicableStateCount();
|
||||
|
||||
_totalHistoryPayload += sz;
|
||||
_history.Add((toSeq, sz, lag, buffer));
|
||||
|
||||
// not watching an ent
|
||||
if(!WatchEntId.IsValid() || WatchEntId.IsClientSide())
|
||||
if(!WatchEntId.IsValid() || _entManager.IsClientSide(WatchEntId))
|
||||
return;
|
||||
|
||||
string? entStateString = null;
|
||||
@@ -86,7 +87,9 @@ namespace Robust.Client.GameStates
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entState in entStates.Span)
|
||||
{
|
||||
if (entState.Uid != WatchEntId)
|
||||
var uid = _entManager.GetEntity(entState.NetEntity);
|
||||
|
||||
if (uid != WatchEntId)
|
||||
continue;
|
||||
|
||||
if (!entState.ComponentChanges.HasContents)
|
||||
@@ -115,7 +118,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var ent in args.Detached)
|
||||
{
|
||||
if (ent != WatchEntId)
|
||||
var uid = _entManager.GetEntity(ent);
|
||||
|
||||
if (uid != WatchEntId)
|
||||
continue;
|
||||
|
||||
conShell.WriteLine($"watchEnt: Left PVS at tick {args.AppliedState.ToSequence}, eid={WatchEntId}" + "\n");
|
||||
@@ -126,7 +131,9 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var entDelete in entDeletes.Span)
|
||||
{
|
||||
if (entDelete == WatchEntId)
|
||||
var uid = _entManager.GetEntity(entDelete);
|
||||
|
||||
if (uid == WatchEntId)
|
||||
entDelString = "\n Deleted";
|
||||
}
|
||||
}
|
||||
@@ -261,7 +268,7 @@ namespace Robust.Client.GameStates
|
||||
handle.DrawString(_font, new Vector2(LeftMargin + width, lastLagY), $"{lastLagMs.ToString()}ms");
|
||||
|
||||
// buffer text
|
||||
handle.DrawString(_font, new Vector2(LeftMargin, height + LowerGraphOffset), $"{_gameStateManager.CurrentBufferSize.ToString()} states");
|
||||
handle.DrawString(_font, new Vector2(LeftMargin, height + LowerGraphOffset), $"{_gameStateManager.GetApplicableStateCount().ToString()} states");
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
@@ -294,30 +301,33 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private sealed class NetWatchEntCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public override string Command => "net_watchent";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntityUid eValue;
|
||||
EntityUid? entity;
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
eValue = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
entity = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
}
|
||||
else if (!EntityUid.TryParse(args[0], out eValue))
|
||||
else if (!NetEntity.TryParse(args[0], out var netEntity) || !_entManager.TryGetEntity(netEntity, out entity))
|
||||
{
|
||||
shell.WriteError("Invalid argument: Needs to be 0 or an entityId.");
|
||||
return;
|
||||
}
|
||||
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if (!overlayMan.TryGetOverlay(out NetGraphOverlay? overlay))
|
||||
if (!_overlayManager.TryGetOverlay(out NetGraphOverlay? overlay))
|
||||
{
|
||||
overlay = new();
|
||||
overlayMan.AddOverlay(overlay);
|
||||
overlay = new NetGraphOverlay();
|
||||
_overlayManager.AddOverlay(overlay);
|
||||
}
|
||||
|
||||
overlay.WatchEntId = eValue;
|
||||
overlay.WatchEntId = entity.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
{
|
||||
internal sealed class NetInterpOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
@@ -24,6 +24,11 @@ namespace Robust.Client.GameStates
|
||||
private readonly SharedContainerSystem _container;
|
||||
private readonly SharedTransformSystem _xform;
|
||||
|
||||
/// <summary>
|
||||
/// When an entity stops lerping the overlay will continue to draw a box around the entity for this amount of time.
|
||||
/// </summary>
|
||||
public static readonly TimeSpan Delay = TimeSpan.FromSeconds(2f);
|
||||
|
||||
public NetInterpOverlay(EntityLookupSystem lookup)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -40,8 +45,8 @@ namespace Robust.Client.GameStates
|
||||
var worldHandle = (DrawingHandleWorld) handle;
|
||||
var viewport = args.WorldAABB;
|
||||
|
||||
var query = _entityManager.AllEntityQueryEnumerator<PhysicsComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var physics, out var transform))
|
||||
var query = _entityManager.AllEntityQueryEnumerator<TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var transform))
|
||||
{
|
||||
// if not on the same map, continue
|
||||
if (transform.MapID != _eyeManager.CurrentMap || _container.IsEntityInContainer(uid))
|
||||
@@ -50,8 +55,8 @@ namespace Robust.Client.GameStates
|
||||
if (transform.GridUid == uid)
|
||||
continue;
|
||||
|
||||
// This entity isn't lerping, no need to draw debug info for it
|
||||
if(transform.NextPosition == null)
|
||||
var delta = (_timing.CurTick.Value - transform.LastLerp.Value) * _timing.TickPeriod;
|
||||
if(!transform.ActivelyLerping && delta > Delay)
|
||||
continue;
|
||||
|
||||
var aabb = _lookup.GetWorldAABB(uid);
|
||||
@@ -61,7 +66,9 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
|
||||
var (pos, rot) = _xform.GetWorldPositionRotation(transform, _entityManager.GetEntityQuery<TransformComponent>());
|
||||
var boxOffset = transform.NextPosition.Value - transform.LocalPosition;
|
||||
var boxOffset = transform.NextPosition != null
|
||||
? transform.NextPosition.Value - transform.LocalPosition
|
||||
: default;
|
||||
var worldOffset = (rot - transform.LocalRotation).RotateVec(boxOffset);
|
||||
|
||||
var nextPos = pos + worldOffset;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Numerics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -24,6 +25,7 @@ namespace Robust.Client.Graphics
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
private ISawmill _logMill = default!;
|
||||
|
||||
// We default to this when we get set to a null eye.
|
||||
private readonly FixedEye _defaultEye = new();
|
||||
@@ -53,6 +55,7 @@ namespace Robust.Client.Graphics
|
||||
void IEyeManager.Initialize()
|
||||
{
|
||||
MainViewport = _uiManager.MainViewport;
|
||||
_logMill = IoCManager.Resolve<ILogManager>().RootSawmill;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -129,14 +132,14 @@ namespace Robust.Client.Graphics
|
||||
/// <inheritdoc />
|
||||
public ScreenCoordinates CoordinatesToScreen(EntityCoordinates point)
|
||||
{
|
||||
return MapToScreen(point.ToMap(_entityManager));
|
||||
return MapToScreen(point.ToMap(_entityManager, _entityManager.System<SharedTransformSystem>()));
|
||||
}
|
||||
|
||||
public ScreenCoordinates MapToScreen(MapCoordinates point)
|
||||
{
|
||||
if (CurrentEye.Position.MapId != point.MapId)
|
||||
{
|
||||
Logger.Error($"Attempted to convert map coordinates ({point}) to screen coordinates with an eye on another map ({CurrentEye.Position.MapId})");
|
||||
_logMill.Error($"Attempted to convert map coordinates ({point}) to screen coordinates with an eye on another map ({CurrentEye.Position.MapId})");
|
||||
return new(default, WindowId.Invalid);
|
||||
}
|
||||
|
||||
@@ -146,12 +149,10 @@ namespace Robust.Client.Graphics
|
||||
/// <inheritdoc />
|
||||
public MapCoordinates ScreenToMap(ScreenCoordinates point)
|
||||
{
|
||||
var (pos, window) = point;
|
||||
|
||||
if (_uiManager.MouseGetControl(point) is not IViewportControl viewport)
|
||||
return default;
|
||||
|
||||
return viewport.ScreenToMap(pos);
|
||||
return viewport.ScreenToMap(point.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -159,6 +160,21 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
return MainViewport.ScreenToMap(point);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapCoordinates PixelToMap(ScreenCoordinates point)
|
||||
{
|
||||
if (_uiManager.MouseGetControl(point) is not IViewportControl viewport)
|
||||
return default;
|
||||
|
||||
return viewport.PixelToMap(point.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapCoordinates PixelToMap(Vector2 point)
|
||||
{
|
||||
return MainViewport.PixelToMap(point);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CurrentEyeChangedEvent : EntityEventArgs
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Robust.Client.Graphics
|
||||
using Robust.Shared.Graphics;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// A fixed eye is an eye which is fixed to one point, its position.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -79,6 +80,16 @@ namespace Robust.Client.Graphics
|
||||
/// <returns>Corresponding point in the world.</returns>
|
||||
MapCoordinates ScreenToMap(Vector2 point);
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="ScreenToMap(ScreenCoordinates)"/>, except it should compensate for the effects of shaders on viewports.
|
||||
/// </summary>
|
||||
MapCoordinates PixelToMap(ScreenCoordinates point);
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="ScreenToMap(Vector2)"/>, except it should compensate for the effects of shaders on viewports.
|
||||
/// </summary>
|
||||
MapCoordinates PixelToMap(Vector2 point);
|
||||
|
||||
void ClearCurrentEye();
|
||||
void Initialize();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -40,22 +40,28 @@ namespace Robust.Client.Graphics.Clyde
|
||||
gridProgram.SetUniformTextureMaybe(UniILightTexture, TextureUnit.Texture1);
|
||||
gridProgram.SetUniform(UniIModUV, new Vector4(0, 0, 1, 1));
|
||||
|
||||
foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
|
||||
var grids = new List<Entity<MapGridComponent>>();
|
||||
_mapManager.FindGridsIntersecting(mapId, worldBounds, ref grids);
|
||||
foreach (var mapGrid in grids)
|
||||
{
|
||||
if (!_mapChunkData.ContainsKey(mapGrid.Owner))
|
||||
if (!_mapChunkData.ContainsKey(mapGrid))
|
||||
continue;
|
||||
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(mapGrid.Owner);
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(mapGrid);
|
||||
gridProgram.SetUniform(UniIModelMatrix, transform.WorldMatrix);
|
||||
var enumerator = mapGrid.GetMapChunks(worldBounds);
|
||||
var enumerator = mapGrid.Comp.GetMapChunks(worldBounds);
|
||||
var data = _mapChunkData[mapGrid];
|
||||
|
||||
while (enumerator.MoveNext(out var chunk))
|
||||
{
|
||||
if (_isChunkDirty(mapGrid, chunk))
|
||||
_updateChunkMesh(mapGrid, chunk);
|
||||
DebugTools.Assert(chunk.FilledTiles > 0);
|
||||
if (!data.TryGetValue(chunk.Indices, out MapChunkData? datum))
|
||||
data[chunk.Indices] = datum = _initChunkBuffers(mapGrid, chunk);
|
||||
|
||||
var datum = _mapChunkData[mapGrid.Owner][chunk.Indices];
|
||||
if (datum.Dirty)
|
||||
_updateChunkMesh(mapGrid, chunk, datum);
|
||||
|
||||
DebugTools.Assert(datum.TileCount > 0);
|
||||
if (datum.TileCount == 0)
|
||||
continue;
|
||||
|
||||
@@ -67,22 +73,36 @@ namespace Robust.Client.Graphics.Clyde
|
||||
CheckGlError();
|
||||
}
|
||||
}
|
||||
|
||||
CullEmptyChunks();
|
||||
}
|
||||
|
||||
private void _updateChunkMesh(MapGridComponent grid, MapChunk chunk)
|
||||
private void CullEmptyChunks()
|
||||
{
|
||||
var data = _mapChunkData[grid.Owner];
|
||||
|
||||
if (!data.TryGetValue(chunk.Indices, out var datum))
|
||||
foreach (var (grid, chunks) in _mapChunkData)
|
||||
{
|
||||
datum = _initChunkBuffers(grid, chunk);
|
||||
}
|
||||
var gridComp = _mapManager.GetGridComp(grid);
|
||||
foreach (var (index, chunk) in chunks)
|
||||
{
|
||||
if (!chunk.Dirty || gridComp.Chunks.ContainsKey(index))
|
||||
{
|
||||
DebugTools.Assert(gridComp.Chunks[index].FilledTiles > 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
DeleteChunk(chunk);
|
||||
chunks.Remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _updateChunkMesh(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
|
||||
{
|
||||
Span<ushort> indexBuffer = stackalloc ushort[_indicesPerChunk(chunk)];
|
||||
Span<Vertex2D> vertexBuffer = stackalloc Vertex2D[_verticesPerChunk(chunk)];
|
||||
|
||||
var i = 0;
|
||||
var cSz = grid.ChunkSize;
|
||||
var cSz = grid.Comp.ChunkSize;
|
||||
var cScaled = chunk.Indices * cSz;
|
||||
for (ushort x = 0; x < cSz; x++)
|
||||
{
|
||||
@@ -129,7 +149,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
datum.TileCount = i;
|
||||
}
|
||||
|
||||
private unsafe MapChunkData _initChunkBuffers(MapGridComponent grid, MapChunk chunk)
|
||||
private unsafe MapChunkData _initChunkBuffers(Entity<MapGridComponent> grid, MapChunk chunk)
|
||||
{
|
||||
var vao = GenVertexArray();
|
||||
BindVertexArray(vao);
|
||||
@@ -157,41 +177,22 @@ namespace Robust.Client.Graphics.Clyde
|
||||
Dirty = true
|
||||
};
|
||||
|
||||
_mapChunkData[grid.Owner].Add(chunk.Indices, datum);
|
||||
return datum;
|
||||
}
|
||||
|
||||
private bool _isChunkDirty(MapGridComponent grid, MapChunk chunk)
|
||||
private void DeleteChunk(MapChunkData data)
|
||||
{
|
||||
var data = _mapChunkData[grid.Owner];
|
||||
return !data.TryGetValue(chunk.Indices, out var datum) || datum.Dirty;
|
||||
}
|
||||
|
||||
public void _setChunkDirty(MapGridComponent grid, Vector2i chunk)
|
||||
{
|
||||
var data = _mapChunkData.GetOrNew(grid.Owner);
|
||||
if (data.TryGetValue(chunk, out var datum))
|
||||
{
|
||||
datum.Dirty = true;
|
||||
}
|
||||
// Don't need to set it if we don't have an entry since lack of an entry is treated as dirty.
|
||||
}
|
||||
|
||||
private void _updateOnGridModified(GridModifiedEvent args)
|
||||
{
|
||||
foreach (var (pos, _) in args.Modified)
|
||||
{
|
||||
var grid = args.Grid;
|
||||
var chunk = grid.GridTileToChunkIndices(pos);
|
||||
_setChunkDirty(grid, chunk);
|
||||
}
|
||||
DeleteVertexArray(data.VAO);
|
||||
CheckGlError();
|
||||
data.VBO.Delete();
|
||||
data.EBO.Delete();
|
||||
}
|
||||
|
||||
private void _updateTileMapOnUpdate(ref TileChangedEvent args)
|
||||
{
|
||||
var grid = _mapManager.GetGrid(args.NewTile.GridUid);
|
||||
var chunk = grid.GridTileToChunkIndices(new Vector2i(args.NewTile.X, args.NewTile.Y));
|
||||
_setChunkDirty(grid, chunk);
|
||||
var gridData = _mapChunkData.GetOrNew(args.Entity);
|
||||
if (gridData.TryGetValue(args.ChunkIndex, out var data))
|
||||
data.Dirty = true;
|
||||
}
|
||||
|
||||
private void _updateOnGridCreated(GridStartupEvent ev)
|
||||
@@ -207,10 +208,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var data = _mapChunkData[gridId];
|
||||
foreach (var chunkDatum in data.Values)
|
||||
{
|
||||
DeleteVertexArray(chunkDatum.VAO);
|
||||
CheckGlError();
|
||||
chunkDatum.VBO.Delete();
|
||||
chunkDatum.EBO.Delete();
|
||||
DeleteChunk(chunkDatum);
|
||||
}
|
||||
|
||||
_mapChunkData.Remove(gridId);
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Profiling;
|
||||
@@ -349,7 +350,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
|
||||
|
||||
if (entry.Sprite.RaiseShaderEvent)
|
||||
_entityManager.EventBus.RaiseLocalEvent(entry.Sprite.Owner,
|
||||
_entityManager.EventBus.RaiseLocalEvent(entry.Uid,
|
||||
new BeforePostShaderRenderEvent(entry.Sprite, viewport), false);
|
||||
}
|
||||
}
|
||||
@@ -511,7 +512,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
RenderOverlays(viewport, OverlaySpace.WorldSpaceBelowFOV, worldAABB, worldBounds);
|
||||
}
|
||||
|
||||
if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawFov)
|
||||
if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawLight && eye.DrawFov)
|
||||
{
|
||||
ApplyFovToBuffer(viewport, eye);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using ES20 = OpenToolkit.Graphics.ES20;
|
||||
|
||||
@@ -16,8 +16,10 @@ using OGLTextureWrapMode = OpenToolkit.Graphics.OpenGL.TextureWrapMode;
|
||||
using TKStencilOp = OpenToolkit.Graphics.OpenGL4.StencilOp;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Shared.Graphics;
|
||||
using static Robust.Shared.GameObjects.OccluderComponent;
|
||||
using Robust.Shared.Utility;
|
||||
using TextureWrapMode = Robust.Shared.Graphics.TextureWrapMode;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
@@ -95,6 +97,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private (PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot)[] _lightsToRenderList = default!;
|
||||
|
||||
private LightCapacityComparer _lightCap = new();
|
||||
private ShadowCapacityComparer _shadowCap = new ShadowCapacityComparer();
|
||||
|
||||
private unsafe void InitLighting()
|
||||
{
|
||||
|
||||
@@ -330,16 +335,18 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private void DrawLightsAndFov(Viewport viewport, Box2Rotated worldBounds, Box2 worldAABB, IEye eye)
|
||||
{
|
||||
if (!_lightManager.Enabled)
|
||||
if (!_lightManager.Enabled || !eye.DrawLight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mapId = eye.Position.MapId;
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
// If this map has lighting disabled, return
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
if (!_entityManager.GetComponent<MapComponent>(mapUid).LightingEnabled)
|
||||
if (!_entityManager.TryGetComponent<MapComponent>(mapUid, out var map) || !map.LightingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -566,6 +573,28 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return true;
|
||||
}
|
||||
|
||||
private sealed class LightCapacityComparer : IComparer<(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot)>
|
||||
{
|
||||
public int Compare(
|
||||
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) x,
|
||||
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) y)
|
||||
{
|
||||
if (x.light.CastShadows && !y.light.CastShadows) return 1;
|
||||
if (!x.light.CastShadows && y.light.CastShadows) return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ShadowCapacityComparer : IComparer<(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot)>
|
||||
{
|
||||
public int Compare(
|
||||
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) x,
|
||||
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) y)
|
||||
{
|
||||
return x.distanceSquared.CompareTo(y.distanceSquared);
|
||||
}
|
||||
}
|
||||
|
||||
private (int count, Box2 expandedBounds) GetLightsToRender(
|
||||
MapId map,
|
||||
in Box2Rotated worldBounds,
|
||||
@@ -591,20 +620,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// First, partition the array based on whether the lights are shadow casting or not
|
||||
// (non shadow casting lights should be the first partition, shadow casting lights the second)
|
||||
Array.Sort(_lightsToRenderList, 0, state.count,
|
||||
Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
|
||||
{
|
||||
if (x.light.CastShadows && !y.light.CastShadows) return 1;
|
||||
else if (!x.light.CastShadows && y.light.CastShadows) return -1;
|
||||
else return 0;
|
||||
}));
|
||||
Array.Sort(_lightsToRenderList, 0, state.count, _lightCap);
|
||||
|
||||
// Next, sort just the shadow casting lights by distance.
|
||||
Array.Sort(_lightsToRenderList, state.count - state.shadowCastingCount, state.shadowCastingCount,
|
||||
Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
|
||||
{
|
||||
return x.distanceSquared.CompareTo(y.distanceSquared);
|
||||
}));
|
||||
Array.Sort(_lightsToRenderList, state.count - state.shadowCastingCount, state.shadowCastingCount, _shadowCap);
|
||||
|
||||
// Then effectively delete the furthest lights, by setting the end of the array to exclude N
|
||||
// number of shadow casting lights (where N is the number above the max number per scene.)
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Graphics;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using TKStencilOp = OpenToolkit.Graphics.OpenGL4.StencilOp;
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Numerics;
|
||||
using System.Text;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,6 +6,15 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde;
|
||||
|
||||
@@ -259,7 +260,7 @@ internal partial class Clyde
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
|
||||
return a.Sprite.Owner.CompareTo(b.Sprite.Owner);
|
||||
return a.Uid.CompareTo(b.Uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user