mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
39 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 |
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,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
110
RELEASE-NOTES.md
110
RELEASE-NOTES.md
@@ -54,6 +54,116 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 182.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Add EntityUid's generation / version to the hashcode.
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public partial 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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -46,7 +46,7 @@ public partial 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.
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -256,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))
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, out MetaDataComponent metadata)
|
||||
{
|
||||
return base.CreateEntity(prototypeName, out metadata);
|
||||
return base.CreateEntity(prototypeName, out metadata, out _);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
@@ -26,17 +25,13 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
|
||||
private void OnEyeAutoState(EntityUid uid, EyeComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateEye(component);
|
||||
UpdateEye((uid, component));
|
||||
}
|
||||
|
||||
private void OnEyeAttached(EntityUid uid, EyeComponent component, LocalPlayerAttachedEvent args)
|
||||
{
|
||||
// TODO: This probably shouldn't be nullable bruv.
|
||||
if (component._eye != null)
|
||||
{
|
||||
_eyeManager.CurrentEye = component._eye;
|
||||
}
|
||||
|
||||
UpdateEye((uid, component));
|
||||
_eyeManager.CurrentEye = component.Eye;
|
||||
var ev = new EyeAttachedEvent(uid, component);
|
||||
RaiseLocalEvent(uid, ref ev, true);
|
||||
}
|
||||
@@ -48,13 +43,7 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
|
||||
private void OnInit(EntityUid uid, EyeComponent component, ComponentInit args)
|
||||
{
|
||||
component._eye = new Eye
|
||||
{
|
||||
Position = Transform(uid).MapPosition,
|
||||
Zoom = component.Zoom,
|
||||
DrawFov = component.DrawFov,
|
||||
Rotation = component.Rotation,
|
||||
};
|
||||
UpdateEye((uid, component));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -64,7 +53,7 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
|
||||
while (query.MoveNext(out var uid, out var eyeComponent))
|
||||
{
|
||||
if (eyeComponent._eye == null)
|
||||
if (eyeComponent.Eye == null)
|
||||
continue;
|
||||
|
||||
if (!TryComp<TransformComponent>(eyeComponent.Target, out var xform))
|
||||
@@ -73,7 +62,7 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
eyeComponent.Target = null;
|
||||
}
|
||||
|
||||
eyeComponent._eye.Position = xform.MapPosition;
|
||||
eyeComponent.Eye.Position = xform.MapPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnTerminate(ref EntityTerminatingEvent ev)
|
||||
{
|
||||
if (!_timing.InPrediction || IsClientSide(ev.Entity))
|
||||
if (!_timing.InPrediction || IsClientSide(ev.Entity, ev.Metadata))
|
||||
return;
|
||||
|
||||
// Client-side entity deletion is not supported and will cause errors.
|
||||
|
||||
@@ -5,6 +5,8 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Arch.Core;
|
||||
using Collections.Pooled;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -29,7 +31,9 @@ using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Toolshed.TypeParsers;
|
||||
using Robust.Shared.Utility;
|
||||
using ComponentType = Arch.Core.Utils.ComponentType;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
{
|
||||
@@ -52,11 +56,12 @@ namespace Robust.Client.GameStates
|
||||
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
|
||||
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
|
||||
private readonly HashSet<NetEntity> _stateEnts = new();
|
||||
private readonly List<EntityUid> _toDelete = new();
|
||||
private readonly List<IComponent> _toRemove = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _outputData = new();
|
||||
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
|
||||
|
||||
private readonly List<NetEntity> _created = new();
|
||||
private readonly List<NetEntity> _detached = new();
|
||||
|
||||
private readonly ObjectPool<Dictionary<ushort, ComponentState>> _compDataPool =
|
||||
new DefaultObjectPool<Dictionary<ushort, ComponentState>>(new DictPolicy<ushort, ComponentState>(), 256);
|
||||
|
||||
@@ -124,6 +129,8 @@ namespace Robust.Client.GameStates
|
||||
public bool DropStates;
|
||||
#endif
|
||||
|
||||
private bool _resettingPredictedEntities;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -161,6 +168,8 @@ namespace Robust.Client.GameStates
|
||||
_conHost.RegisterCommand("localdelete", Loc.GetString("cmd-local-delete-desc"), Loc.GetString("cmd-local-delete-help"), LocalDeleteEntCommand);
|
||||
_conHost.RegisterCommand("fullstatereset", Loc.GetString("cmd-full-state-reset-desc"), Loc.GetString("cmd-full-state-reset-help"), (_,_,_) => RequestFullState());
|
||||
|
||||
_entities.ComponentAdded += OnComponentAdded;
|
||||
|
||||
var metaId = _compFactory.GetRegistration(typeof(MetaDataComponent)).NetID;
|
||||
if (!metaId.HasValue)
|
||||
throw new InvalidOperationException("MetaDataComponent does not have a NetId.");
|
||||
@@ -168,6 +177,23 @@ namespace Robust.Client.GameStates
|
||||
_metaCompNetId = metaId.Value;
|
||||
}
|
||||
|
||||
private void OnComponentAdded(AddedComponentEventArgs args)
|
||||
{
|
||||
if (_resettingPredictedEntities)
|
||||
{
|
||||
var comp = args.ComponentType;
|
||||
|
||||
if (comp.NetID == null)
|
||||
return;
|
||||
|
||||
_sawmill.Error($"""
|
||||
Added component {comp.Name} with net id {comp.NetID} while resetting predicted entities.
|
||||
Stack trace:
|
||||
{Environment.StackTrace}
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Reset()
|
||||
{
|
||||
@@ -519,7 +545,9 @@ namespace Robust.Client.GameStates
|
||||
var countReset = 0;
|
||||
var system = _entitySystemManager.GetEntitySystem<ClientDirtySystem>();
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
RemQueue<IComponent> toRemove = new();
|
||||
using var toRemove = new PooledList<IComponent>();
|
||||
using var toAdd = new PooledList<ushort>();
|
||||
using var toAddStates = new PooledList<ComponentState>();
|
||||
|
||||
foreach (var entity in system.DirtyEntities)
|
||||
{
|
||||
@@ -537,48 +565,63 @@ namespace Robust.Client.GameStates
|
||||
|
||||
countReset += 1;
|
||||
|
||||
foreach (var (netId, comp) in meta.NetComponents)
|
||||
try
|
||||
{
|
||||
if (!comp.NetSyncEnabled)
|
||||
continue;
|
||||
_resettingPredictedEntities = true;
|
||||
|
||||
// Was this component added during prediction?
|
||||
if (comp.CreationTick > _timing.LastRealTick)
|
||||
foreach (var (netId, comp) in meta.NetComponents)
|
||||
{
|
||||
if (last.ContainsKey(netId))
|
||||
if (!comp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
// Was this component added during prediction?
|
||||
if (comp.CreationTick > _timing.LastRealTick)
|
||||
{
|
||||
// Component was probably removed and then re-addedd during a single prediction run
|
||||
// Just reset state as normal.
|
||||
comp.ClearCreationTick();
|
||||
if (last.ContainsKey(netId))
|
||||
{
|
||||
// Component was probably removed and then re-addedd during a single prediction run
|
||||
// Just reset state as normal.
|
||||
comp.ClearCreationTick();
|
||||
}
|
||||
else
|
||||
{
|
||||
toRemove.Add(comp);
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A new component was added: {comp.GetType()}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (comp.LastModifiedTick <= _timing.LastRealTick ||
|
||||
!last.TryGetValue(netId, out var compState))
|
||||
{
|
||||
toRemove.Add(comp);
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A new component was added: {comp.GetType()}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A component was dirtied: {comp.GetType()}");
|
||||
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
|
||||
if (comp.LastModifiedTick <= _timing.LastRealTick || !last.TryGetValue(netId, out var compState))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A component was dirtied: {comp.GetType()}");
|
||||
|
||||
var handleState = new ComponentHandleState(compState, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
|
||||
// Remove predicted component additions
|
||||
foreach (var comp in toRemove)
|
||||
finally
|
||||
{
|
||||
_entities.RemoveComponent(entity, comp);
|
||||
_resettingPredictedEntities = false;
|
||||
}
|
||||
|
||||
if (toRemove.Count > 0)
|
||||
{
|
||||
// Remove predicted component additions
|
||||
// TODO: 1 archetype change.
|
||||
foreach (var comp in toRemove)
|
||||
{
|
||||
_entities.RemoveComponent(entity, comp, meta);
|
||||
}
|
||||
|
||||
toRemove.Clear();
|
||||
}
|
||||
toRemove.Clear();
|
||||
|
||||
// Re-add predicted removals
|
||||
if (system.RemovedComponents.TryGetValue(entity, out var netIds))
|
||||
@@ -591,15 +634,29 @@ namespace Robust.Client.GameStates
|
||||
if (!last.TryGetValue(netId, out var state))
|
||||
continue;
|
||||
|
||||
var comp = _entityManager.AddComponent(entity, netId, meta);
|
||||
toAdd.Add(netId);
|
||||
toAddStates.Add(state);
|
||||
}
|
||||
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A component was removed: {comp.GetType()}");
|
||||
if (toAdd.Count > 0)
|
||||
{
|
||||
for (var i = 0; i < toAdd.Count; i++)
|
||||
{
|
||||
var netId = toAdd[i];
|
||||
var state = toAddStates[i];
|
||||
var comp = _entityManager.AddComponent(entity, netId, meta);
|
||||
|
||||
var stateEv = new ComponentHandleState(state, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
|
||||
comp.ClearCreationTick(); // don't undo the re-adding.
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A component was removed: {comp.GetType()}");
|
||||
|
||||
var stateEv = new ComponentHandleState(state, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
|
||||
comp.ClearCreationTick(); // don't undo the re-adding.
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
|
||||
toAdd.Clear();
|
||||
toAddStates.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,10 +739,9 @@ namespace Robust.Client.GameStates
|
||||
_config.TickProcessMessages();
|
||||
}
|
||||
|
||||
(IEnumerable<NetEntity> Created, List<NetEntity> Detached) output;
|
||||
using (_prof.Group("Entity"))
|
||||
{
|
||||
output = ApplyEntityStates(curState, nextState);
|
||||
ApplyEntityStates(curState, nextState);
|
||||
}
|
||||
|
||||
using (_prof.Group("Player"))
|
||||
@@ -695,13 +751,13 @@ namespace Robust.Client.GameStates
|
||||
|
||||
using (_prof.Group("Callback"))
|
||||
{
|
||||
GameStateApplied?.Invoke(new GameStateAppliedArgs(curState, output.Detached));
|
||||
GameStateApplied?.Invoke(new GameStateAppliedArgs(curState, _detached));
|
||||
}
|
||||
|
||||
return output.Created;
|
||||
return _created;
|
||||
}
|
||||
|
||||
private (IEnumerable<NetEntity> Created, List<NetEntity> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
private void ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
{
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
@@ -710,6 +766,8 @@ namespace Robust.Client.GameStates
|
||||
var enteringPvs = 0;
|
||||
_toApply.Clear();
|
||||
_toCreate.Clear();
|
||||
_detached.Clear();
|
||||
_created.Clear();
|
||||
_pendingReapplyNetStates.Clear();
|
||||
var curSpan = curState.EntityStates.Span;
|
||||
|
||||
@@ -734,6 +792,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
var uid = _entities.CreateEntity(metaState.PrototypeId, out var newMeta);
|
||||
_toCreate.Add(es.NetEntity, es);
|
||||
_created.Add(es.NetEntity);
|
||||
_toApply.Add(uid, (es.NetEntity, newMeta, false, GameTick.Zero, es, null));
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
@@ -787,7 +846,7 @@ namespace Robust.Client.GameStates
|
||||
// Detach entities to null space
|
||||
var containerSys = _entitySystemManager.GetEntitySystem<ContainerSystem>();
|
||||
var lookupSys = _entitySystemManager.GetEntitySystem<EntityLookupSystem>();
|
||||
var detached = ProcessPvsDeparture(curState.ToSequence, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
ProcessPvsDeparture(_detached, curState.ToSequence, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
|
||||
// Check next state (AFTER having created new entities introduced in curstate)
|
||||
if (nextState != null)
|
||||
@@ -889,8 +948,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
_prof.WriteValue("State Size", ProfData.Int32(curSpan.Length));
|
||||
_prof.WriteValue("Entered PVS", ProfData.Int32(enteringPvs));
|
||||
|
||||
return (_toCreate.Keys, detached);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -921,7 +978,7 @@ namespace Robust.Client.GameStates
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
_toDelete.Clear();
|
||||
using var toDelete = new PooledList<EntityUid>();
|
||||
|
||||
// Client side entities won't need the transform, but that should always be a tiny minority of entities
|
||||
var metaQuery = _entityManager.AllEntityQueryEnumerator<MetaDataComponent, TransformComponent>();
|
||||
@@ -932,7 +989,7 @@ namespace Robust.Client.GameStates
|
||||
if (metadata.NetEntity.IsClientSide())
|
||||
{
|
||||
if (deleteClientEntities)
|
||||
_toDelete.Add(ent);
|
||||
toDelete.Add(ent);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -959,14 +1016,14 @@ namespace Robust.Client.GameStates
|
||||
&& !deleteClientEntities // don't add duplicates
|
||||
&& _entities.IsClientSide(child.Value))
|
||||
{
|
||||
_toDelete.Add(child.Value);
|
||||
toDelete.Add(child.Value);
|
||||
}
|
||||
}
|
||||
|
||||
_toDelete.Add(ent);
|
||||
toDelete.Add(ent);
|
||||
}
|
||||
|
||||
foreach (var ent in _toDelete)
|
||||
foreach (var ent in toDelete)
|
||||
{
|
||||
_entities.DeleteEntity(ent);
|
||||
}
|
||||
@@ -1026,7 +1083,8 @@ namespace Robust.Client.GameStates
|
||||
Detach(GameTick.MaxValue, null, entities, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
}
|
||||
|
||||
private List<NetEntity> ProcessPvsDeparture(
|
||||
private void ProcessPvsDeparture(
|
||||
IList<NetEntity> detached,
|
||||
GameTick toTick,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
@@ -1034,18 +1092,17 @@ namespace Robust.Client.GameStates
|
||||
ContainerSystem containerSys,
|
||||
EntityLookupSystem lookupSys)
|
||||
{
|
||||
var toDetach = _processor.GetEntitiesToDetach(toTick, _pvsDetachBudget);
|
||||
var detached = new List<NetEntity>();
|
||||
using var toDetach = new PooledList<(GameTick Tick, List<NetEntity> Entities)>();
|
||||
_processor.GetEntitiesToDetach(toDetach, toTick, _pvsDetachBudget);
|
||||
|
||||
if (toDetach.Count == 0)
|
||||
return detached;
|
||||
return;
|
||||
|
||||
// TODO optimize
|
||||
// If an entity is leaving PVS, so are all of its children. If we can preserve the hierarchy we can avoid
|
||||
// things like container insertion and ejection.
|
||||
|
||||
using var _ = _prof.Group("Leave PVS");
|
||||
detached.EnsureCapacity(toDetach.Count);
|
||||
|
||||
foreach (var (tick, ents) in toDetach)
|
||||
{
|
||||
@@ -1053,7 +1110,6 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(detached.Count));
|
||||
return detached;
|
||||
}
|
||||
|
||||
private void Detach(GameTick maxTick,
|
||||
@@ -1064,7 +1120,7 @@ namespace Robust.Client.GameStates
|
||||
SharedTransformSystem xformSys,
|
||||
ContainerSystem containerSys,
|
||||
EntityLookupSystem lookupSys,
|
||||
List<NetEntity>? detached = null)
|
||||
IList<NetEntity>? detached = null)
|
||||
{
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
@@ -1118,7 +1174,7 @@ namespace Robust.Client.GameStates
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
var brokenEnts = new List<EntityUid>();
|
||||
using var brokenEnts = new PooledList<EntityUid>();
|
||||
#endif
|
||||
using (_prof.Group("Initialize Entity"))
|
||||
{
|
||||
@@ -1182,18 +1238,28 @@ namespace Robust.Client.GameStates
|
||||
// First remove any deleted components
|
||||
if (curState?.NetComponents != null)
|
||||
{
|
||||
_toRemove.Clear();
|
||||
using var toRemove = new PooledList<IComponent>();
|
||||
using var compTypes = new PooledList<ComponentType>();
|
||||
|
||||
foreach (var (id, comp) in meta.NetComponents)
|
||||
{
|
||||
if (comp.NetSyncEnabled && !curState.NetComponents.Contains(id))
|
||||
_toRemove.Add(comp);
|
||||
{
|
||||
toRemove.Add(comp);
|
||||
compTypes.Add(comp.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var comp in _toRemove)
|
||||
if (toRemove.Count > 0)
|
||||
{
|
||||
_entities.RemoveComponent(uid, comp, meta);
|
||||
foreach (var comp in toRemove)
|
||||
{
|
||||
_entityManager.RemoveComponentInternal(uid, comp, terminating: false, archetypeChange: false, meta);
|
||||
}
|
||||
}
|
||||
|
||||
if (compTypes.Count > 0)
|
||||
_entityManager.RemoveComponentRange(uid, compTypes);
|
||||
}
|
||||
|
||||
if (enteringPvs)
|
||||
@@ -1208,7 +1274,7 @@ namespace Robust.Client.GameStates
|
||||
if (!meta.NetComponents.TryGetValue(id, out var comp))
|
||||
{
|
||||
comp = _compFactory.GetComponent(id);
|
||||
_entityManager.AddComponent(uid, comp, true, metadata: meta);
|
||||
_entityManager.AddComponent(uid, comp, metadata: meta);
|
||||
}
|
||||
|
||||
_compStateWork[id] = (comp, state, null);
|
||||
@@ -1216,18 +1282,49 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
else if (curState != null)
|
||||
{
|
||||
using var addedComps = new PooledList<IComponent>();
|
||||
using var addedCompTypes = new PooledList<ComponentType>();
|
||||
using var addedRegistrations = new PooledList<ComponentRegistration>();
|
||||
|
||||
foreach (var compChange in curState.ComponentChanges.Span)
|
||||
{
|
||||
if (!meta.NetComponents.TryGetValue(compChange.NetID, out var comp))
|
||||
{
|
||||
comp = _compFactory.GetComponent(compChange.NetID);
|
||||
_entityManager.AddComponent(uid, comp, true, metadata:meta);
|
||||
var registration = _compFactory.GetRegistration(compChange.NetID);
|
||||
addedRegistrations.Add(registration);
|
||||
comp = _compFactory.GetComponent(registration);
|
||||
comp.Owner = uid;
|
||||
addedComps.Add(comp);
|
||||
addedCompTypes.Add(comp.GetType());
|
||||
}
|
||||
else if (compChange.LastModifiedTick <= lastApplied && lastApplied != GameTick.Zero)
|
||||
continue;
|
||||
|
||||
_compStateWork[compChange.NetID] = (comp, compChange.State, null);
|
||||
}
|
||||
|
||||
// To avoid shuffling the archetype we'll set the component range up-front.
|
||||
if (addedComps.Count > 0)
|
||||
{
|
||||
// TODO: This fucking sucks but
|
||||
// - Frequent archetype changes PER COMPONENT sucks
|
||||
// - the components will be null in event handlers until it's done.
|
||||
_entityManager.AddComponentRange(uid, addedCompTypes);
|
||||
|
||||
for (var i = 0; i < addedComps.Count; i++)
|
||||
{
|
||||
var component = addedComps[i];
|
||||
var reg = addedRegistrations[i];
|
||||
_entityManager.AddComponentInternalOnly(uid, component, reg, meta);
|
||||
}
|
||||
|
||||
for (var i = 0; i < addedComps.Count; i++)
|
||||
{
|
||||
var component = addedComps[i];
|
||||
var reg = addedRegistrations[i];
|
||||
_entityManager.AddComponentEvents(uid, component, reg, false, meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextState != null)
|
||||
@@ -1317,7 +1414,7 @@ namespace Robust.Client.GameStates
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out uid))
|
||||
if (!EntityUid.TryParse(args[0], "-1", out uid))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-parse-failure-uid", ("arg", args[0])));
|
||||
meta = null;
|
||||
@@ -1449,23 +1546,22 @@ namespace Robust.Client.GameStates
|
||||
if (!meta.NetComponents.TryGetValue(id, out var comp))
|
||||
{
|
||||
comp = _compFactory.GetComponent(id);
|
||||
_entityManager.AddComponent(uid, comp, true, meta);
|
||||
_entityManager.AddComponent(uid, comp, meta);
|
||||
}
|
||||
|
||||
var handleState = new ComponentHandleState(state, null);
|
||||
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
}
|
||||
|
||||
// ensure we don't have any extra components
|
||||
_toRemove.Clear();
|
||||
using var toRemove = new PooledList<IComponent>();
|
||||
|
||||
foreach (var (id, comp) in meta.NetComponents)
|
||||
{
|
||||
if (comp.NetSyncEnabled && !lastState.ContainsKey(id))
|
||||
_toRemove.Add(comp);
|
||||
toRemove.Add(comp);
|
||||
}
|
||||
|
||||
foreach (var comp in _toRemove)
|
||||
foreach (var comp in toRemove)
|
||||
{
|
||||
_entities.RemoveComponent(uid, comp);
|
||||
}
|
||||
@@ -1478,10 +1574,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public sealed class GameStateAppliedArgs : EventArgs
|
||||
{
|
||||
public GameState AppliedState { get; }
|
||||
public readonly List<NetEntity> Detached;
|
||||
public readonly GameState AppliedState;
|
||||
public readonly IReadOnlyList<NetEntity> Detached;
|
||||
|
||||
public GameStateAppliedArgs(GameState appliedState, List<NetEntity> detached)
|
||||
public GameStateAppliedArgs(GameState appliedState, IReadOnlyList<NetEntity> detached)
|
||||
{
|
||||
AppliedState = appliedState;
|
||||
Detached = detached;
|
||||
|
||||
@@ -304,9 +304,8 @@ Had full state: {LastFullState != null}"
|
||||
|
||||
public void ClearDetachQueue() => _pvsDetachMessages.Clear();
|
||||
|
||||
public List<(GameTick Tick, List<NetEntity> 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<NetEntity> Entities)>();
|
||||
foreach (var (tick, entities) in _pvsDetachMessages)
|
||||
{
|
||||
if (tick > toTick)
|
||||
@@ -325,7 +324,6 @@ Had full state: {LastFullState != null}"
|
||||
entities.RemoveRange(index, budget);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TryGetDeltaState(out GameState? curState, out GameState? nextState)
|
||||
|
||||
@@ -512,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);
|
||||
}
|
||||
|
||||
@@ -97,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()
|
||||
{
|
||||
|
||||
@@ -332,7 +335,7 @@ 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;
|
||||
}
|
||||
@@ -570,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,
|
||||
@@ -595,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.)
|
||||
|
||||
@@ -42,15 +42,20 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds, ref _grids);
|
||||
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
var mapSystem = _entManager.System<SharedMapSystem>();
|
||||
var xformSystem = _entManager.System<SharedTransformSystem>();
|
||||
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var tileSize = grid.Comp.TileSize;
|
||||
var tileDimensions = new Vector2(tileSize, tileSize);
|
||||
var xform = xformQuery.GetComponent(grid);
|
||||
args.WorldHandle.SetTransform(xform.WorldMatrix);
|
||||
var (_, _, worldMatrix, invMatrix) = xformSystem.GetWorldPositionRotationMatrixWithInv(grid.Owner);
|
||||
args.WorldHandle.SetTransform(worldMatrix);
|
||||
var localAABB = invMatrix.TransformBox(args.WorldBounds);
|
||||
|
||||
foreach (var tileRef in grid.Comp.GetTilesIntersecting(args.WorldBounds, false))
|
||||
var enumerator = mapSystem.GetLocalTilesEnumerator(grid.Owner, grid.Comp, localAABB, false);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
var tileDef = _tileDefManager[tileRef.Tile.TypeId];
|
||||
|
||||
@@ -66,7 +71,7 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
continue;
|
||||
|
||||
var neighborIndices = new Vector2i(tileRef.GridIndices.X + x, tileRef.GridIndices.Y + y);
|
||||
var neighborTile = grid.Comp.GetTileRef(neighborIndices);
|
||||
var neighborTile = mapSystem.GetTileRef(grid.Owner, grid.Comp, neighborIndices);
|
||||
var neighborDef = _tileDefManager[neighborTile.Tile.TypeId];
|
||||
|
||||
// If it's the same tile then no edge to be drawn.
|
||||
@@ -118,9 +123,9 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
}
|
||||
|
||||
if (angle == Angle.Zero)
|
||||
args.WorldHandle.DrawTextureRect(texture, box);
|
||||
args.WorldHandle.DrawTextureRect(texture.Texture, box);
|
||||
else
|
||||
args.WorldHandle.DrawTextureRect(texture, new Box2Rotated(box, angle, box.Center));
|
||||
args.WorldHandle.DrawTextureRect(texture.Texture, new Box2Rotated(box, angle, box.Center));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -8,6 +7,7 @@ using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Dynamics.Contacts;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Physics;
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.Placement.Modes
|
||||
|
||||
var closestEntity = snapToEntities[0];
|
||||
var closestTransform = pManager.EntityManager.GetComponent<TransformComponent>(closestEntity);
|
||||
if (!pManager.EntityManager.TryGetComponent<SpriteComponent?>(closestEntity, out var component) || component.BaseRSI == null)
|
||||
if (!pManager.EntityManager.TryGetComponent(closestEntity, out SpriteComponent? component) || component.BaseRSI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,15 +15,22 @@ public interface IPlayerManager : ISharedPlayerManager
|
||||
event Action? PlayerListUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets attached to a new entity. See also <see cref="LocalPlayerAttachedEvent"/>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets attached to a new entity, or when the local
|
||||
/// session gets updated. See also <see cref="LocalPlayerAttachedEvent"/>
|
||||
/// </summary>
|
||||
event Action<EntityUid>? LocalPlayerAttached;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets detached from new entity. See also <see cref="LocalPlayerDetachedEvent"/>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets detached from an entity, or when the local
|
||||
/// session gets updated. See also <see cref="LocalPlayerDetachedEvent"/>
|
||||
/// </summary>
|
||||
event Action<EntityUid>? LocalPlayerDetached;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever <see cref="ISharedPlayerManager.LocalSession"/> changes.
|
||||
/// </summary>
|
||||
event Action<(ICommonSession? Old, ICommonSession? New)>? LocalSessionChanged;
|
||||
|
||||
void ApplyPlayerStates(IReadOnlyCollection<SessionState> list);
|
||||
|
||||
/// <summary>
|
||||
@@ -38,34 +45,8 @@ public interface IPlayerManager : ISharedPlayerManager
|
||||
/// </summary>
|
||||
void SetupMultiplayer(INetChannel channel);
|
||||
|
||||
void SetLocalSession(ICommonSession session);
|
||||
|
||||
[Obsolete("Use LocalSession instead")]
|
||||
LocalPlayer? LocalPlayer { get;}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ECS event that gets raised when the local player gets attached to a new entity. The event is both broadcast and
|
||||
/// raised directed at the new entity.
|
||||
/// </summary>
|
||||
public sealed class LocalPlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public LocalPlayerAttachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public EntityUid Entity { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ECS event that gets raised when the local player gets detached from an entity. The event is both broadcast and
|
||||
/// raised directed at the new entity.
|
||||
/// </summary>
|
||||
public sealed class LocalPlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public LocalPlayerDetachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public EntityUid Entity { get; }
|
||||
}
|
||||
@@ -42,12 +42,13 @@ namespace Robust.Client.Player
|
||||
/// <inheritdoc />
|
||||
public override int MaxPlayers => _client.GameInfo?.ServerMaxPlayers ?? -1;
|
||||
|
||||
public LocalPlayer? LocalPlayer { get; set; }
|
||||
public LocalPlayer? LocalPlayer { get; private set; }
|
||||
|
||||
public event Action<SessionStatusEventArgs>? LocalStatusChanged;
|
||||
public event Action? PlayerListUpdated;
|
||||
public event Action<EntityUid>? LocalPlayerDetached;
|
||||
public event Action<EntityUid>? LocalPlayerAttached;
|
||||
public event Action<(ICommonSession? Old, ICommonSession? New)>? LocalSessionChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(int maxPlayers)
|
||||
@@ -64,22 +65,14 @@ namespace Robust.Client.Player
|
||||
LocalStatusChanged?.Invoke(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Startup()
|
||||
{
|
||||
if (LocalSession == null)
|
||||
throw new InvalidOperationException("LocalSession cannot be null");
|
||||
|
||||
LocalPlayer = new LocalPlayer(LocalSession);
|
||||
base.Startup();
|
||||
}
|
||||
|
||||
public void SetupSinglePlayer(string name)
|
||||
{
|
||||
if (LocalSession != null)
|
||||
throw new InvalidOperationException($"Player manager already running?");
|
||||
|
||||
LocalSession = CreateAndAddSession(default, name);
|
||||
var session = CreateAndAddSession(default, name);
|
||||
session.ClientSide = true;
|
||||
SetLocalSession(session);
|
||||
Startup();
|
||||
PlayerListUpdated?.Invoke();
|
||||
}
|
||||
@@ -89,18 +82,44 @@ namespace Robust.Client.Player
|
||||
if (LocalSession != null)
|
||||
throw new InvalidOperationException($"Player manager already running?");
|
||||
|
||||
var session = CreateAndAddSession(channel.UserId, channel.UserName);
|
||||
session.Channel = channel;
|
||||
LocalSession = session;
|
||||
SetLocalSession(CreateAndAddSession(channel));
|
||||
Startup();
|
||||
_network.ClientSendMessage(new MsgPlayerListReq());
|
||||
}
|
||||
|
||||
public void SetLocalSession(ICommonSession? session)
|
||||
{
|
||||
if (session == LocalSession)
|
||||
return;
|
||||
|
||||
var old = LocalSession;
|
||||
|
||||
if (old?.AttachedEntity is {} oldUid)
|
||||
{
|
||||
LocalSession = null;
|
||||
LocalPlayer = null;
|
||||
Sawmill.Info($"Detaching local player from {EntManager.ToPrettyString(oldUid)}.");
|
||||
EntManager.EventBus.RaiseLocalEvent(oldUid, new LocalPlayerDetachedEvent(oldUid), true);
|
||||
LocalPlayerDetached?.Invoke(oldUid);
|
||||
}
|
||||
|
||||
LocalSession = session;
|
||||
LocalPlayer = session == null ? null : new LocalPlayer(session);
|
||||
Sawmill.Info($"Changing local session from {old?.ToString() ?? "null"} to {session?.ToString() ?? "null"}.");
|
||||
LocalSessionChanged?.Invoke((old, LocalSession));
|
||||
|
||||
if (session?.AttachedEntity is {} newUid)
|
||||
{
|
||||
Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(newUid)}.");
|
||||
EntManager.EventBus.RaiseLocalEvent(newUid, new LocalPlayerAttachedEvent(newUid), true);
|
||||
LocalPlayerAttached?.Invoke(newUid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (LocalSession != null)
|
||||
SetAttachedEntity(LocalSession, null);
|
||||
SetAttachedEntity(LocalSession, null, out _);
|
||||
LocalPlayer = null;
|
||||
LocalSession = null;
|
||||
_pendingStates.Clear();
|
||||
@@ -108,16 +127,21 @@ namespace Robust.Client.Player
|
||||
PlayerListUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public override void SetAttachedEntity(ICommonSession session, EntityUid? uid)
|
||||
public override bool SetAttachedEntity(ICommonSession? session, EntityUid? uid, out ICommonSession? kicked, bool force = false)
|
||||
{
|
||||
kicked = null;
|
||||
if (session == null)
|
||||
return false;
|
||||
|
||||
if (session.AttachedEntity == uid)
|
||||
return;
|
||||
return true;
|
||||
|
||||
var old = session.AttachedEntity;
|
||||
base.SetAttachedEntity(session, uid);
|
||||
if (!base.SetAttachedEntity(session, uid, out kicked, force))
|
||||
return false;
|
||||
|
||||
if (session != LocalSession)
|
||||
return;
|
||||
return true;
|
||||
|
||||
if (old.HasValue)
|
||||
{
|
||||
@@ -129,13 +153,13 @@ namespace Robust.Client.Player
|
||||
if (uid == null)
|
||||
{
|
||||
Sawmill.Info($"Local player is no longer attached to any entity.");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!EntManager.EntityExists(uid))
|
||||
{
|
||||
Sawmill.Error($"Attempted to attach player to non-existent entity {uid}!");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!EntManager.EnsureComponent(uid.Value, out EyeComponent eye))
|
||||
@@ -148,6 +172,7 @@ namespace Robust.Client.Player
|
||||
Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(uid)}.");
|
||||
EntManager.EventBus.RaiseLocalEvent(uid.Value, new LocalPlayerAttachedEvent(uid.Value), true);
|
||||
LocalPlayerAttached?.Invoke(uid.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ApplyPlayerStates(IReadOnlyCollection<SessionState> list)
|
||||
@@ -193,7 +218,7 @@ namespace Robust.Client.Player
|
||||
_pendingStates.Remove(state.UserId);
|
||||
}
|
||||
|
||||
SetAttachedEntity(LocalSession, uid);
|
||||
SetAttachedEntity(LocalSession, uid, out _, true);
|
||||
SetStatus(LocalSession, state.Status);
|
||||
}
|
||||
|
||||
@@ -233,11 +258,10 @@ namespace Robust.Client.Player
|
||||
{
|
||||
// This is a new userid, so we create a new session.
|
||||
DebugTools.Assert(state.UserId != LocalPlayer?.UserId);
|
||||
var newSession = CreateAndAddSession(state.UserId, state.Name);
|
||||
var newSession = (CommonSession) CreateAndAddSession(state.UserId, state.Name);
|
||||
newSession.Ping = state.Ping;
|
||||
newSession.Name = state.Name;
|
||||
SetStatus(newSession, state.Status);
|
||||
SetAttachedEntity(newSession, controlled);
|
||||
SetAttachedEntity(newSession, controlled, out _, true);
|
||||
dirty = true;
|
||||
continue;
|
||||
}
|
||||
@@ -256,7 +280,7 @@ namespace Robust.Client.Player
|
||||
local.Name = state.Name;
|
||||
local.Ping = state.Ping;
|
||||
SetStatus(local, state.Status);
|
||||
SetAttachedEntity(local, controlled);
|
||||
SetAttachedEntity(local, controlled, out _, true);
|
||||
}
|
||||
|
||||
// Remove old users. This only works if the provided state is a list of all players
|
||||
@@ -264,10 +288,12 @@ namespace Robust.Client.Player
|
||||
{
|
||||
foreach (var oldUser in InternalSessions.Keys.ToArray())
|
||||
{
|
||||
// clear slot, player left
|
||||
if (users.Contains(oldUser))
|
||||
continue;
|
||||
|
||||
if (InternalSessions[oldUser].ClientSide)
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(oldUser != LocalUser
|
||||
|| LocalUser == null
|
||||
|| LocalUser == default(NetUserId),
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(EnableClientScripting)' == 'True'">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.1.0" />
|
||||
|
||||
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
|
||||
return;
|
||||
|
||||
var mapSystem = _entityManager.System<SharedMapSystem>();
|
||||
var xformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
|
||||
if (_mapManager.TryFindGridAt(mouseWorldMap, out var mouseGridUid, out var mouseGrid))
|
||||
{
|
||||
@@ -80,7 +81,7 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
|
||||
{
|
||||
mouseGridPos = new EntityCoordinates(_mapManager.GetMapEntityId(mouseWorldMap.MapId),
|
||||
mouseWorldMap.Position);
|
||||
tile = new TileRef(EntityUid.Invalid, mouseGridPos.ToVector2i(_entityManager, _mapManager), Tile.Empty);
|
||||
tile = new TileRef(EntityUid.Invalid, mouseGridPos.ToVector2i(_entityManager, _mapManager, xformSystem), Tile.Empty);
|
||||
}
|
||||
|
||||
var controlHovered = UserInterfaceManager.CurrentlyHovered;
|
||||
@@ -90,35 +91,35 @@ Screen Size: {screenSize} (scale: {screenScale})
|
||||
Mouse Pos:
|
||||
Screen: {mouseScreenPos}
|
||||
{mouseWorldMap}
|
||||
{mouseGridPos}
|
||||
{_entityManager.GetNetCoordinates(mouseGridPos)}
|
||||
{tile}
|
||||
GUI: {controlHovered}");
|
||||
|
||||
_textBuilder.AppendLine("\nAttached Entity:");
|
||||
var controlledEntity = _playerManager?.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
_textBuilder.AppendLine("\nAttached NetEntity:");
|
||||
var controlledEntity = _playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid;
|
||||
|
||||
if (controlledEntity == EntityUid.Invalid)
|
||||
{
|
||||
_textBuilder.AppendLine("No attached entity.");
|
||||
_textBuilder.AppendLine("No attached netentity.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var entityTransform = _entityManager.GetComponent<TransformComponent>(controlledEntity);
|
||||
var playerWorldOffset = entityTransform.MapPosition;
|
||||
var playerWorldOffset = xformSystem.GetMapCoordinates(entityTransform);
|
||||
var playerScreen = _eyeManager.WorldToScreen(playerWorldOffset.Position);
|
||||
|
||||
var playerCoordinates = entityTransform.Coordinates;
|
||||
var playerRotation = entityTransform.WorldRotation;
|
||||
var playerRotation = xformSystem.GetWorldRotation(entityTransform);
|
||||
var gridRotation = entityTransform.GridUid != null
|
||||
? _entityManager.GetComponent<TransformComponent>(entityTransform.GridUid.Value)
|
||||
.WorldRotation
|
||||
? xformSystem.GetWorldRotation(entityTransform.GridUid.Value)
|
||||
: Angle.Zero;
|
||||
|
||||
_textBuilder.Append($@" Screen: {playerScreen}
|
||||
{playerWorldOffset}
|
||||
{playerCoordinates}
|
||||
{_entityManager.GetNetCoordinates(playerCoordinates)}
|
||||
Rotation: {playerRotation.Degrees:F2}°
|
||||
EntId: {controlledEntity}
|
||||
GridUid: {entityTransform.GridUid}
|
||||
NEntId: {_entityManager.GetNetEntity(controlledEntity)}
|
||||
Grid NEntId: {_entityManager.GetNetEntity(entityTransform.GridUid)}
|
||||
Grid Rotation: {gridRotation.Degrees:F2}°");
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
|
||||
void OnEntered(LineEdit.LineEditEventArgs e)
|
||||
{
|
||||
var gridVal = EntityUid.Parse(gridId.Text);
|
||||
var gridVal = EntityUid.Parse(gridId.Text, "-1");
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
var xVal = float.Parse(x.Text, CultureInfo.InvariantCulture);
|
||||
var yVal = float.Parse(y.Text, CultureInfo.InvariantCulture);
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
if (!ReadOnly)
|
||||
{
|
||||
lineEdit.OnTextEntered += e =>
|
||||
ValueChanged(EntityUid.Parse(e.Text));
|
||||
ValueChanged(EntityUid.Parse(e.Text, ", -1"));
|
||||
}
|
||||
|
||||
var vvButton = new Button()
|
||||
|
||||
@@ -660,10 +660,14 @@ namespace Robust.Server
|
||||
{
|
||||
// Write down exception log
|
||||
var logPath = _config.GetCVar(CVars.LogPath);
|
||||
var relPath = PathHelpers.ExecutableRelativeFile(logPath);
|
||||
Directory.CreateDirectory(relPath);
|
||||
var pathToWrite = Path.Combine(relPath,
|
||||
if (!Path.IsPathRooted(logPath))
|
||||
{
|
||||
logPath = PathHelpers.ExecutableRelativeFile(logPath);
|
||||
}
|
||||
|
||||
var pathToWrite = Path.Combine(logPath,
|
||||
"Runtime-" + DateTime.Now.ToString("yyyy-MM-dd-THH-mm-ss") + ".txt");
|
||||
Directory.CreateDirectory(logPath);
|
||||
File.WriteAllText(pathToWrite, _runtimeLog.Display(), EncodingHelpers.UTF8);
|
||||
}
|
||||
|
||||
|
||||
@@ -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($"Server: Profiled spawning {amount} entities in {stopwatch.Elapsed.TotalMilliseconds:N3} ms");
|
||||
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
_entities.DeleteEntity(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// System that handles players being attached/detached from entities.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class ActorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActorComponent, ComponentShutdown>(OnActorShutdown);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to attach the player to</param>
|
||||
/// <param name="player">The player to attach to the entity</param>
|
||||
/// <param name="force">Whether to kick any existing players from the entity</param>
|
||||
/// <returns>Whether the attach succeeded, or not.</returns>
|
||||
public bool Attach(EntityUid? uid, ICommonSession player, bool force = false)
|
||||
{
|
||||
return Attach(uid, player, false, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to attach the player to</param>
|
||||
/// <param name="player">The player to attach to the entity</param>
|
||||
/// <param name="force">Whether to kick any existing players from the entity</param>
|
||||
/// <param name="forceKicked">The player that was forcefully kicked, or null.</param>
|
||||
/// <returns>Whether the attach succeeded, or not.</returns>
|
||||
public bool Attach(EntityUid? entity, ICommonSession player, bool force, out ICommonSession? forceKicked)
|
||||
{
|
||||
// Null by default.
|
||||
forceKicked = null;
|
||||
|
||||
if (player.AttachedEntity == entity)
|
||||
{
|
||||
DebugTools.Assert(entity == null || HasComp<ActorComponent>(entity));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entity is not { } uid)
|
||||
return Detach(player);
|
||||
|
||||
// Cannot attach to a deleted, nonexisting or terminating entity.
|
||||
if (TerminatingOrDeleted(uid))
|
||||
return false;
|
||||
|
||||
// Check if there was a player attached to the entity already...
|
||||
if (TryComp(uid, out ActorComponent? actor))
|
||||
{
|
||||
// If we're not forcing the attach, this fails.
|
||||
if (!force)
|
||||
return false;
|
||||
|
||||
// Set the event's force-kicked session before detaching it.
|
||||
forceKicked = actor.PlayerSession;
|
||||
RemComp(uid, actor);
|
||||
DebugTools.AssertNull(forceKicked.AttachedEntity);
|
||||
}
|
||||
|
||||
// Detach from the currently attached entity.
|
||||
Detach(player);
|
||||
|
||||
// We add the actor component.
|
||||
actor = EntityManager.AddComponent<ActorComponent>(uid);
|
||||
EntityManager.EnsureComponent<EyeComponent>(uid);
|
||||
actor.PlayerSession = player;
|
||||
_playerManager.SetAttachedEntity(player, uid);
|
||||
DebugTools.Assert(player.AttachedEntity == entity);
|
||||
|
||||
// The player is fully attached now, raise an event!
|
||||
RaiseLocalEvent(uid, new PlayerAttachedEvent(uid, player, forceKicked), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches an attached session from the entity, if any.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity player sessions will be detached from.</param>
|
||||
/// <returns>Whether any player session was detached.</returns>
|
||||
public bool Detach(EntityUid uid, ActorComponent? actor = null)
|
||||
{
|
||||
if (!Resolve(uid, ref actor, false))
|
||||
return false;
|
||||
|
||||
RemComp(uid, actor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches this player from its attached entity, if any.
|
||||
/// </summary>
|
||||
/// <param name="player">The player session that will be detached from any attached entities.</param>
|
||||
/// <returns>Whether the player is now detached from any entities.
|
||||
/// This returns true if the player wasn't attached to any entity.</returns>
|
||||
public bool Detach(ICommonSession player, ActorComponent? actor = null)
|
||||
{
|
||||
var uid = player.AttachedEntity;
|
||||
if (uid == null)
|
||||
return true;
|
||||
|
||||
if (!Resolve(uid.Value, ref actor, false))
|
||||
{
|
||||
Log.Error($"Player {player} was attached to a deleted entity?");
|
||||
((CommonSession) player).AttachedEntity = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
RemComp(uid.Value, actor);
|
||||
DebugTools.AssertNull(player.AttachedEntity);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnActorShutdown(EntityUid entity, ActorComponent component, ComponentShutdown args)
|
||||
{
|
||||
_playerManager.SetAttachedEntity(component.PlayerSession, null);
|
||||
|
||||
// The player is fully detached now that the component has shut down.
|
||||
RaiseLocalEvent(entity, new PlayerDetachedEvent(entity, component.PlayerSession), true);
|
||||
}
|
||||
|
||||
public bool TryGetActorFromUserId(NetUserId? userId, [NotNullWhen(true)] out ICommonSession? actor, out EntityUid? actorEntity)
|
||||
{
|
||||
actor = null;
|
||||
actorEntity = null;
|
||||
if (userId != null)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(userId.Value, out actor))
|
||||
return false;
|
||||
actorEntity = actor.AttachedEntity;
|
||||
}
|
||||
|
||||
return actor != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for when a player has been attached to an entity.
|
||||
/// </summary>
|
||||
public sealed class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Entity { get; }
|
||||
public ICommonSession Player { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The player session that was forcefully kicked from the entity, if any.
|
||||
/// </summary>
|
||||
public ICommonSession? Kicked { get; }
|
||||
|
||||
public PlayerAttachedEvent(EntityUid entity, ICommonSession player, ICommonSession? kicked = null)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
Kicked = kicked;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for when a player has been detached from an entity.
|
||||
/// </summary>
|
||||
public sealed class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Entity { get; }
|
||||
public ICommonSession Player { get; }
|
||||
|
||||
public PlayerDetachedEvent(EntityUid entity, ICommonSession player)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Utils;
|
||||
using Collections.Pooled;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -154,10 +157,11 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
result = Deserialize(data);
|
||||
_logLoader.Debug($"Loaded map in {sw.Elapsed}");
|
||||
|
||||
var mapEnt = _mapManager.GetMapEntityId(mapId);
|
||||
_logLoader.Info($"Loaded map {resPath} in {sw.Elapsed}");
|
||||
var xformQuery = _serverEntityManager.GetEntityQuery<TransformComponent>();
|
||||
var mapEnt = _mapManager.GetMapEntityId(mapId);
|
||||
|
||||
var rootEnts = new List<EntityUid>();
|
||||
// aeoeoeieioe content
|
||||
|
||||
@@ -174,6 +178,8 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
// EntityManager.CleanupArch();
|
||||
|
||||
rootUids = rootEnts;
|
||||
}
|
||||
|
||||
@@ -290,6 +296,9 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
ReadGrids(data);
|
||||
|
||||
// grids prior to engine v175 might've been serialized with empty chunks which now throw debug asserts.
|
||||
RemoveEmptyChunks(data);
|
||||
|
||||
// Then, go hierarchically in order and do the entity things.
|
||||
StartupEntities(data);
|
||||
|
||||
@@ -305,6 +314,25 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RemoveEmptyChunks(MapData data)
|
||||
{
|
||||
var gridQuery = _serverEntityManager.GetEntityQuery<MapGridComponent>();
|
||||
foreach (var uid in data.EntitiesToDeserialize.Keys)
|
||||
{
|
||||
if (!gridQuery.TryGetComponent(uid, out var gridComp))
|
||||
continue;
|
||||
|
||||
foreach (var (index, chunk) in gridComp.Chunks)
|
||||
{
|
||||
if (chunk.FilledTiles > 0)
|
||||
continue;
|
||||
|
||||
Log.Warning($"Encountered empty chunk while deserializing map. Grid: {ToPrettyString(uid)}. Chunk index: {index}");
|
||||
gridComp.Chunks.Remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool VerifyEntitiesExist(MapData data, BeforeEntityReadEvent ev)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
@@ -406,6 +434,10 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
if (data.Version >= 4)
|
||||
{
|
||||
var metaEntities = data.RootMappingNode.Get<SequenceDataNode>("entities");
|
||||
using var mapSaveCompType = new PooledSet<Type>()
|
||||
{
|
||||
typeof(MapSaveIdComponent)
|
||||
};
|
||||
|
||||
foreach (var metaDef in metaEntities.Cast<MappingDataNode>())
|
||||
{
|
||||
@@ -425,9 +457,30 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
var entities = (SequenceDataNode) metaDef["entities"];
|
||||
EntityPrototype? proto = null;
|
||||
var count = entities.Count;
|
||||
var entTotal = data.Entities.Count + count;
|
||||
data.Entities.EnsureCapacity(entTotal);
|
||||
data.UidEntityMap.EnsureCapacity(entTotal);
|
||||
data.EntitiesToDeserialize.EnsureCapacity(entTotal);
|
||||
|
||||
if (type != null)
|
||||
_prototypeManager.TryIndex(type, out proto);
|
||||
{
|
||||
if (_prototypeManager.TryIndex(type, out proto) && count > 1)
|
||||
{
|
||||
ComponentType[] compTypes;
|
||||
|
||||
if (data.Options.StoreMapUids)
|
||||
{
|
||||
compTypes = EntityManager.GetComponentType(proto, mapSaveCompType);
|
||||
}
|
||||
else
|
||||
{
|
||||
compTypes = EntityManager.GetComponentType(proto);
|
||||
}
|
||||
|
||||
EntityManager.Reserve(compTypes, count);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entityDef in entities.Cast<MappingDataNode>())
|
||||
{
|
||||
@@ -442,6 +495,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
{
|
||||
deletedPrototypeUids.Add(entity);
|
||||
}
|
||||
// TODO: Move this elsewhere?
|
||||
else if (data.Options.StoreMapUids)
|
||||
{
|
||||
var comp = _serverEntityManager.AddComponent<MapSaveIdComponent>(entity);
|
||||
@@ -550,6 +604,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
_context.CurrentlyIgnoredComponents = missingComponentList.Cast<ValueDataNode>().Select(x => x.Value).ToHashSet();
|
||||
|
||||
_serverEntityManager.FinishEntityLoad(uid, meta.EntityPrototype, _context);
|
||||
|
||||
if (_context.CurrentlyIgnoredComponents.Count > 0)
|
||||
meta.LastComponentRemoved = _timing.CurTick;
|
||||
}
|
||||
|
||||
@@ -24,11 +24,6 @@ namespace Robust.Server.GameObjects
|
||||
_cfg.OnValueChanged(CVars.GameDeleteEmptyGrids, SetGridDeletion, true);
|
||||
}
|
||||
|
||||
protected override void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args)
|
||||
{
|
||||
EnsureComp<PhysicsMapComponent>(uid);
|
||||
}
|
||||
|
||||
private void SetGridDeletion(bool value)
|
||||
{
|
||||
_deleteEmptyGrids = value;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Robust.Server.GameObjects
|
||||
/// Manager for entities -- controls things like template loading and instantiation
|
||||
/// </summary>
|
||||
[UsedImplicitly] // DI Container
|
||||
public sealed class ServerEntityManager : EntityManager, IServerEntityManagerInternal
|
||||
public sealed partial class ServerEntityManager : EntityManager, IServerEntityManagerInternal
|
||||
{
|
||||
private static readonly Gauge EntitiesCount = Metrics.CreateGauge(
|
||||
"robust_entities_count",
|
||||
@@ -62,7 +62,7 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype)
|
||||
{
|
||||
return AllocEntity(prototype, out _);
|
||||
return AllocEntity(prototype, out _, out _);
|
||||
}
|
||||
|
||||
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, IEntityLoadContext? context)
|
||||
@@ -85,15 +85,15 @@ namespace Robust.Server.GameObjects
|
||||
StartEntity(entity);
|
||||
}
|
||||
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, out TransformComponent xform, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, out metadata, context);
|
||||
return base.CreateEntity(prototypeName, out metadata, out xform, context);
|
||||
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
var entity = base.CreateEntity(prototype, out metadata, context);
|
||||
var entity = base.CreateEntity(prototype, out metadata, out xform, context);
|
||||
|
||||
// At this point in time, all data configure on the entity *should* be purely from the prototype.
|
||||
// As such, we can reset the modified ticks to Zero,
|
||||
@@ -159,7 +159,7 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
base.TickUpdate(frameTime, noPredictions, histogram);
|
||||
|
||||
EntitiesCount.Set(Entities.Count);
|
||||
EntitiesCount.Set(EntityCount);
|
||||
}
|
||||
|
||||
public uint GetLastMessageSequence(ICommonSession session)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -271,6 +272,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
{
|
||||
sessionData.LastSeenAt.Remove(metadata.NetEntity);
|
||||
sessionData.LastLeftView.Remove(metadata.NetEntity);
|
||||
|
||||
if (sessionData.SentEntities.TryGetValue(previousTick, out var ents))
|
||||
ents.Remove(metadata.NetEntity);
|
||||
}
|
||||
@@ -433,10 +435,15 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
#endregion
|
||||
|
||||
public (List<(int, IChunkIndexLocation)> , HashSet<int>[], EntityUid[][] viewers) GetChunks(ICommonSession[] sessions)
|
||||
public List<(int, IChunkIndexLocation)> GetChunks(
|
||||
ICommonSession[] sessions,
|
||||
ref HashSet<int>[] playerChunks,
|
||||
ref EntityUid[][] viewerEntities)
|
||||
{
|
||||
var playerChunks = new HashSet<int>[sessions.Length];
|
||||
var viewerEntities = new EntityUid[sessions.Length][];
|
||||
// Pass these in to avoid allocating new ones every tick, 99% of the time sessions length is going to be the same size.
|
||||
// These values will get overridden here and the old values have already been returned to the pool by this point.
|
||||
Array.Resize(ref playerChunks, sessions.Length);
|
||||
Array.Resize(ref viewerEntities, sessions.Length);
|
||||
|
||||
_chunkList.Clear();
|
||||
// Keep track of the index of each chunk we use for a faster index lookup.
|
||||
@@ -459,8 +466,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var session = sessions[i];
|
||||
playerChunks[i] = _playerChunkPool.Get();
|
||||
|
||||
var viewers = GetSessionViewers(session);
|
||||
viewerEntities[i] = viewers;
|
||||
ref var viewers = ref viewerEntities[i];
|
||||
GetSessionViewers(session, ref viewers);
|
||||
|
||||
for (var j = 0; j < viewers.Length; j++)
|
||||
{
|
||||
@@ -552,7 +559,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
return (_chunkList, playerChunks, viewerEntities);
|
||||
return _chunkList;
|
||||
}
|
||||
|
||||
public void RegisterNewPreviousChunkTrees(
|
||||
@@ -569,23 +576,20 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
_reusedTrees.Add(chunks[i]);
|
||||
}
|
||||
|
||||
var previousIndices = _previousTrees.Keys.ToArray();
|
||||
for (var i = 0; i < previousIndices.Length; i++)
|
||||
foreach (var (index, chunk) in _previousTrees)
|
||||
{
|
||||
var index = previousIndices[i];
|
||||
// ReSharper disable once InconsistentlySynchronizedField
|
||||
if (_reusedTrees.Contains(index)) continue;
|
||||
var chunk = _previousTrees[index];
|
||||
if (chunk.HasValue)
|
||||
if (_reusedTrees.Contains(index))
|
||||
continue;
|
||||
|
||||
if (chunk != null)
|
||||
{
|
||||
_chunkCachePool.Return(chunk.Value.metadata);
|
||||
_treePool.Return(chunk.Value.tree);
|
||||
}
|
||||
|
||||
if (!chunks.Contains(index))
|
||||
{
|
||||
_previousTrees.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
_previousTrees.EnsureCapacity(chunks.Count);
|
||||
@@ -1313,26 +1317,44 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
return entState;
|
||||
}
|
||||
|
||||
private EntityUid[] GetSessionViewers(ICommonSession session)
|
||||
private void GetSessionViewers(ICommonSession session, [NotNull] ref EntityUid[]? viewers)
|
||||
{
|
||||
if (session.Status != SessionStatus.InGame)
|
||||
return Array.Empty<EntityUid>();
|
||||
{
|
||||
viewers = Array.Empty<EntityUid>();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path
|
||||
if (session.ViewSubscriptions.Count == 0)
|
||||
{
|
||||
if (session.AttachedEntity == null)
|
||||
return Array.Empty<EntityUid>();
|
||||
{
|
||||
viewers = Array.Empty<EntityUid>();
|
||||
return;
|
||||
}
|
||||
|
||||
return new[] { session.AttachedEntity.Value };
|
||||
Array.Resize(ref viewers, 1);
|
||||
viewers[0] = session.AttachedEntity.Value;
|
||||
return;
|
||||
}
|
||||
|
||||
var viewers = new HashSet<EntityUid>();
|
||||
if (session.AttachedEntity != null)
|
||||
viewers.Add(session.AttachedEntity.Value);
|
||||
int i = 0;
|
||||
if (session.AttachedEntity is { } local)
|
||||
{
|
||||
DebugTools.Assert(!session.ViewSubscriptions.Contains(local));
|
||||
Array.Resize(ref viewers, session.ViewSubscriptions.Count + 1);
|
||||
viewers[i++] = local;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Resize(ref viewers, session.ViewSubscriptions.Count);
|
||||
}
|
||||
|
||||
viewers.UnionWith(session.ViewSubscriptions);
|
||||
return viewers.ToArray();
|
||||
foreach (var ent in session.ViewSubscriptions)
|
||||
{
|
||||
viewers[i++] = ent;
|
||||
}
|
||||
}
|
||||
|
||||
// Read Safe
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace Robust.Server.GameStates
|
||||
// Mapping of net UID of clients -> last known acked state.
|
||||
private GameTick _lastOldestAck = GameTick.Zero;
|
||||
|
||||
private HashSet<int>[] _playerChunks = Array.Empty<HashSet<int>>();
|
||||
private EntityUid[][] _viewerEntities = Array.Empty<EntityUid[]>();
|
||||
|
||||
private PvsSystem _pvs = default!;
|
||||
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
@@ -267,7 +270,7 @@ Oldest acked clients: {string.Join(", ", players)}
|
||||
|
||||
private PvsData? GetPVSData(ICommonSession[] players)
|
||||
{
|
||||
var (chunks, playerChunks, viewerEntities) = _pvs.GetChunks(players);
|
||||
var chunks= _pvs.GetChunks(players, ref _playerChunks, ref _viewerEntities);
|
||||
const int ChunkBatchSize = 2;
|
||||
var chunksCount = chunks.Count;
|
||||
var chunkBatches = (int)MathF.Ceiling((float)chunksCount / ChunkBatchSize);
|
||||
@@ -310,8 +313,8 @@ Oldest acked clients: {string.Join(", ", players)}
|
||||
ArrayPool<bool>.Shared.Return(reuse);
|
||||
return new PvsData()
|
||||
{
|
||||
PlayerChunks = playerChunks,
|
||||
ViewerEntities = viewerEntities,
|
||||
PlayerChunks = _playerChunks,
|
||||
ViewerEntities = _viewerEntities,
|
||||
ChunkCache = chunkCache,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server.Maps;
|
||||
|
||||
@@ -93,6 +94,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
IDependencyCollection dependencies, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
DebugTools.Assert(value.FilledTiles > 0, "Attempting to write an empty chunk");
|
||||
var root = new MappingDataNode();
|
||||
var ind = new ValueDataNode($"{value.X},{value.Y}");
|
||||
root.Add("ind", ind);
|
||||
|
||||
@@ -191,14 +191,11 @@ namespace Robust.Server.Placement
|
||||
|
||||
MapGridComponent? grid;
|
||||
|
||||
_entityManager.TryGetComponent(coordinates.EntityId, out grid);
|
||||
|
||||
if (grid == null)
|
||||
_mapManager.TryFindGridAt(coordinates.ToMap(_entityManager, _xformSystem), out _, out grid);
|
||||
|
||||
if (grid != null) // stick to existing grid
|
||||
EntityUid gridId = coordinates.EntityId;
|
||||
if (_entityManager.TryGetComponent(coordinates.EntityId, out grid)
|
||||
|| _mapManager.TryFindGridAt(coordinates.ToMap(_entityManager, _xformSystem), out gridId, out grid))
|
||||
{
|
||||
_maps.SetTile(coordinates.EntityId, grid, coordinates, new Tile(tileType));
|
||||
_maps.SetTile(gridId, grid, coordinates, new Tile(tileType));
|
||||
|
||||
var placementEraseEvent = new PlacementTileEvent(tileType, coordinates, placingUserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
|
||||
@@ -254,7 +251,7 @@ namespace Robust.Server.Placement
|
||||
/// </summary>
|
||||
public void SendPlacementBegin(EntityUid mob, int range, string objectType, string alignOption)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
|
||||
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
var playerConnection = actor.PlayerSession.Channel;
|
||||
@@ -275,7 +272,7 @@ namespace Robust.Server.Placement
|
||||
/// </summary>
|
||||
public void SendPlacementBeginTile(EntityUid mob, int range, string tileType, string alignOption)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
|
||||
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
var playerConnection = actor.PlayerSession.Channel;
|
||||
@@ -296,7 +293,7 @@ namespace Robust.Server.Placement
|
||||
/// </summary>
|
||||
public void SendPlacementCancel(EntityUid mob)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
|
||||
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
var playerConnection = actor.PlayerSession.Channel;
|
||||
|
||||
@@ -80,9 +80,7 @@ namespace Robust.Server.Player
|
||||
/// <param name="args"></param>
|
||||
private void NewSession(object? sender, NetChannelArgs args)
|
||||
{
|
||||
var session = CreateAndAddSession(args.Channel.UserId, args.Channel.UserName);
|
||||
session.Channel = args.Channel;
|
||||
|
||||
CreateAndAddSession(args.Channel);
|
||||
PlayerCountMetric.Set(PlayerCount);
|
||||
// Synchronize base time.
|
||||
var msgTimeBase = new MsgSyncTimeBase();
|
||||
@@ -106,8 +104,7 @@ namespace Robust.Server.Player
|
||||
DebugTools.Assert(session.Channel == args.Channel);
|
||||
|
||||
SetStatus(session, SessionStatus.Disconnected);
|
||||
if (session.AttachedEntity != null)
|
||||
EntManager.System<ActorSystem>().Detach(session.AttachedEntity.Value);
|
||||
SetAttachedEntity(session, null, out _, true);
|
||||
|
||||
var viewSys = EntManager.System<ViewSubscriberSystem>();
|
||||
foreach (var eye in session.ViewSubscriptions.ToArray())
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Primitives" Version="6.0.0" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.2" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.0" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SharpZstd.Interop" Version="1.5.2-beta2" PrivateAssets="compile" />
|
||||
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" />
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Completion;
|
||||
@@ -294,9 +295,9 @@ namespace Robust.Server.Scripting
|
||||
loader: TextLoader.From(TextAndVersion.Create(SourceText.From(message.Code), VersionStamp.Create()))
|
||||
));
|
||||
|
||||
var results = await CompletionService
|
||||
.GetService(document)
|
||||
.GetCompletionsAsync(document, message.Cursor);
|
||||
var results = await (CompletionService
|
||||
.GetService(document)?
|
||||
.GetCompletionsAsync(document, message.Cursor) ?? Task.FromResult<CompletionList?>(null));
|
||||
|
||||
if (results is not null)
|
||||
{
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
getStateInit.Append($@"
|
||||
{name} = GetNetEntitySet(component.{name}),");
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = EnsureEntitySet<{componentName}>(state.{name}, uid);");
|
||||
EnsureEntitySet<{componentName}>(state.{name}, uid, component.{name});");
|
||||
|
||||
break;
|
||||
case GlobalEntityUidListName:
|
||||
@@ -177,7 +177,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
getStateInit.Append($@"
|
||||
{name} = GetNetEntityList(component.{name}),");
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = EnsureEntityList<{componentName}>(state.{name}, uid);");
|
||||
EnsureEntityList<{componentName}>(state.{name}, uid, component.{name});");
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ILReader.Core" Version="1.0.0.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.1.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Robust.Shared.Scripting
|
||||
|
||||
public EntityCoordinates gpos(double x, double y, int gridId)
|
||||
{
|
||||
return gpos(x, y, new EntityUid(gridId));
|
||||
return gpos(x, y, new EntityUid(gridId, -1));
|
||||
}
|
||||
|
||||
public EntityCoordinates gpos(double x, double y, EntityUid gridId)
|
||||
@@ -61,12 +61,12 @@ namespace Robust.Shared.Scripting
|
||||
|
||||
public EntityUid eid(int i)
|
||||
{
|
||||
return new(i);
|
||||
return new(i, -1);
|
||||
}
|
||||
|
||||
public MapGridComponent getgrid(int i)
|
||||
{
|
||||
return map.GetGrid(new EntityUid(i));
|
||||
return map.GetGrid(new EntityUid(i, -1));
|
||||
}
|
||||
|
||||
public MapGridComponent getgrid(EntityUid mapId)
|
||||
@@ -193,7 +193,7 @@ namespace Robust.Shared.Scripting
|
||||
public bool TryComp<T>(EntityUid uid, out T? comp) where T : IComponent
|
||||
=> ent.TryGetComponent(uid, out comp);
|
||||
|
||||
public bool HasComp<T>(EntityUid uid)
|
||||
public bool HasComp<T>(EntityUid uid) where T : IComponent
|
||||
=> ent.HasComponent<T>(uid);
|
||||
|
||||
public EntityUid Spawn(string? prototype, EntityCoordinates position)
|
||||
|
||||
@@ -53,6 +53,16 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<int> NetReceiveBufferSize =
|
||||
CVarDef.Create("net.receivebuffersize", 131071, CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// Size of the pool for Lidgren's array buffers to send messages.
|
||||
/// Set to 0 to disable pooling; max is 8192.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Higher just means more potentially wasted space and slower pool retrieval.
|
||||
/// </remarks>
|
||||
public static readonly CVarDef<int> NetPoolSize =
|
||||
CVarDef.Create("net.pool_size", 512, CVar.CLIENT | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum UDP payload size to send.
|
||||
/// </summary>
|
||||
|
||||
17
Robust.Shared/Console/Commands/ArchTrimCommand.cs
Normal file
17
Robust.Shared/Console/Commands/ArchTrimCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Shared.Console.Commands;
|
||||
|
||||
public sealed class ArchTrimCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
public string Command => "arch_trim";
|
||||
public string Description => "Runs TrimExcess on arch";
|
||||
public string Help => Command;
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_entManager.CleanupArch();
|
||||
}
|
||||
}
|
||||
@@ -131,6 +131,7 @@ internal sealed class RunMapInitCommand : LocalizedCommands
|
||||
|
||||
internal sealed class ListMapsCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public override string Command => "lsmap";
|
||||
@@ -144,10 +145,13 @@ internal sealed class ListMapsCommand : LocalizedCommands
|
||||
|
||||
foreach (var mapId in _map.GetAllMapIds().OrderBy(id => id.Value))
|
||||
{
|
||||
msg.AppendFormat("{0}: init: {1}, paused: {2}, ent: {3}, grids: {4}\n",
|
||||
mapId, _map.IsMapInitialized(mapId),
|
||||
var mapUid = _map.GetMapEntityId(mapId);
|
||||
|
||||
msg.AppendFormat("{0}: {1}, init: {2}, paused: {3}, nent: {4}, grids: {5}\n",
|
||||
mapId, _entManager.GetComponent<MetaDataComponent>(mapUid).EntityName,
|
||||
_map.IsMapInitialized(mapId),
|
||||
_map.IsMapPaused(mapId),
|
||||
_map.GetMapEntityId(mapId),
|
||||
_entManager.GetNetEntity(_map.GetMapEntityId(mapId)),
|
||||
string.Join(",", _map.GetAllGrids(mapId).Select(grid => grid.Owner)));
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,12 @@ WhitelistedNamespaces:
|
||||
# * The API is not *relevant* to content. e.g. System.Type.IsAnsiClass.
|
||||
# * I am lazy these API lists are huge dude.
|
||||
Types:
|
||||
NetSerializer:
|
||||
NetListAsArray`1:
|
||||
Fields:
|
||||
- "System.Collections.Generic.IReadOnlyCollection`1<!0> Value"
|
||||
Methods:
|
||||
- "bool get_HasContents()"
|
||||
Lidgren.Network:
|
||||
NetBuffer:
|
||||
All: True
|
||||
|
||||
47
Robust.Shared/GameObjects/ArchetypeIterator.cs
Normal file
47
Robust.Shared/GameObjects/ArchetypeIterator.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Arch.Core;
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
internal struct ArchetypeIterator
|
||||
{
|
||||
private readonly PooledList<Archetype> _archetypes;
|
||||
|
||||
internal ArchetypeIterator(PooledList<Archetype> archetypes)
|
||||
{
|
||||
_archetypes = archetypes;
|
||||
}
|
||||
|
||||
public ArchetypeEnumerator GetEnumerator()
|
||||
{
|
||||
return new ArchetypeEnumerator(_archetypes);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ArchetypeEnumerator
|
||||
{
|
||||
private readonly PooledList<Archetype> _archetypes;
|
||||
private int _index;
|
||||
|
||||
public ArchetypeEnumerator(PooledList<Archetype> archetypes)
|
||||
{
|
||||
_archetypes = archetypes;
|
||||
_index = _archetypes.Count;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (--_index >= 0)
|
||||
{
|
||||
var archetype = Current;
|
||||
if (archetype.EntityCount > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Archetype Current => _archetypes[_index];
|
||||
}
|
||||
60
Robust.Shared/GameObjects/ChunkIterator.cs
Normal file
60
Robust.Shared/GameObjects/ChunkIterator.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Arch.Core;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
internal struct ArchChunkIterator
|
||||
{
|
||||
private readonly ArchetypeEnumerator _archetypes;
|
||||
|
||||
internal ArchChunkIterator(in ArchetypeEnumerator archetypes)
|
||||
{
|
||||
_archetypes = archetypes;
|
||||
}
|
||||
|
||||
public ArchChunkEnumerator GetEnumerator()
|
||||
{
|
||||
return new ArchChunkEnumerator(_archetypes);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ArchChunkEnumerator
|
||||
{
|
||||
private ArchetypeEnumerator _archetypes;
|
||||
private int _chunkIndex;
|
||||
public Chunk Current => _archetypes.Current.GetChunk(_chunkIndex);
|
||||
|
||||
internal ArchChunkEnumerator(in ArchetypeEnumerator archetypes)
|
||||
{
|
||||
_archetypes = archetypes;
|
||||
|
||||
if (_archetypes.MoveNext())
|
||||
{
|
||||
_chunkIndex = _archetypes.Current.ChunkCount;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (--_chunkIndex >= 0 && Current.Size > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_archetypes.MoveNext())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_chunkIndex = _archetypes.Current.ChunkCount - 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class QueryExtensions
|
||||
{
|
||||
internal static ArchChunkIterator ChunkIterator(this in Query query, World world)
|
||||
{
|
||||
var archetypeEnumerator = new ArchetypeEnumerator(query.Matches);
|
||||
return new ArchChunkIterator(in archetypeEnumerator);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Arch.Core.Utils;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -13,6 +14,7 @@ public readonly struct CompIdx : IEquatable<CompIdx>
|
||||
private static readonly Dictionary<Type, CompIdx> SlowStore = new();
|
||||
|
||||
internal readonly int Value;
|
||||
internal readonly ComponentType Type;
|
||||
|
||||
internal static CompIdx Index<T>() => Store<T>.Index;
|
||||
|
||||
@@ -62,12 +64,13 @@ public readonly struct CompIdx : IEquatable<CompIdx>
|
||||
private static class Store<T>
|
||||
{
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
public static readonly CompIdx Index = new(Interlocked.Increment(ref _CompIdxMaster));
|
||||
public static readonly CompIdx Index = new(Interlocked.Increment(ref _CompIdxMaster), typeof(T));
|
||||
}
|
||||
|
||||
internal CompIdx(int value)
|
||||
internal CompIdx(int value, in ComponentType type)
|
||||
{
|
||||
Value = value;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public bool Equals(CompIdx other)
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace Robust.Shared.GameObjects
|
||||
public readonly struct AddedComponentEventArgs
|
||||
{
|
||||
public readonly ComponentEventArgs BaseArgs;
|
||||
public readonly CompIdx ComponentType;
|
||||
public readonly ComponentRegistration ComponentType;
|
||||
|
||||
public AddedComponentEventArgs(ComponentEventArgs baseArgs, CompIdx componentType)
|
||||
public AddedComponentEventArgs(ComponentEventArgs baseArgs, ComponentRegistration componentType)
|
||||
{
|
||||
BaseArgs = baseArgs;
|
||||
ComponentType = componentType;
|
||||
@@ -55,17 +55,4 @@ namespace Robust.Shared.GameObjects
|
||||
Meta = meta;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct DeletedComponentEventArgs
|
||||
{
|
||||
public readonly ComponentEventArgs BaseArgs;
|
||||
|
||||
public readonly bool Terminating;
|
||||
|
||||
public DeletedComponentEventArgs(ComponentEventArgs baseArgs, bool terminating)
|
||||
{
|
||||
BaseArgs = baseArgs;
|
||||
Terminating = terminating;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,16 +12,10 @@ namespace Robust.Shared.GameObjects
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedEyeSystem)), AutoGenerateComponentState(true)]
|
||||
public sealed partial class EyeComponent : Component
|
||||
{
|
||||
#region Client
|
||||
|
||||
[ViewVariables] internal Eye? _eye = default!;
|
||||
|
||||
public IEye? Eye => _eye;
|
||||
public const int DefaultVisibilityMask = 1;
|
||||
|
||||
[ViewVariables]
|
||||
public MapCoordinates? Position => _eye?.Position;
|
||||
|
||||
#endregion
|
||||
public readonly Eye Eye = new();
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this entity is used to update the eye's position instead of just using the component's owner.
|
||||
@@ -37,6 +31,9 @@ namespace Robust.Shared.GameObjects
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("drawFov"), AutoNetworkedField]
|
||||
public bool DrawFov = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public bool DrawLight = true;
|
||||
|
||||
// yes it's not networked, don't ask.
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("rotation")]
|
||||
public Angle Rotation;
|
||||
@@ -47,8 +44,6 @@ namespace Robust.Shared.GameObjects
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("offset"), AutoNetworkedField]
|
||||
public Vector2 Offset;
|
||||
|
||||
public const int DefaultVisibilityMask = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The visibility mask for this eye.
|
||||
/// The player will be able to get updates for entities whose layers match the mask.
|
||||
@@ -58,7 +53,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Single layer used for Eye visiblity. Controls what entities they are allowed to see.
|
||||
/// Single layer used for Eye visibility. Controls what entities they are allowed to see.
|
||||
/// </summary>
|
||||
public sealed class VisibilityMaskLayer {}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Shared.GameObjects
|
||||
[ViewVariables]
|
||||
private NetEntity NetParent => _entMan.GetNetEntity(_parent);
|
||||
|
||||
[DataField("parent")] internal EntityUid _parent;
|
||||
[DataField("parent")] internal EntityUid _parent = EntityUid.Invalid;
|
||||
[DataField("pos")] internal Vector2 _localPosition = Vector2.Zero; // holds offset from grid, or offset from parent
|
||||
[DataField("rot")] internal Angle _localRotation; // local rotation
|
||||
[DataField("noRot")] internal bool _noLocalRotation;
|
||||
@@ -733,7 +733,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public bool IsValid() => Uid.IsValid();
|
||||
public bool Valid => IsValid();
|
||||
public static readonly BroadphaseData Invalid = default;
|
||||
public static readonly BroadphaseData Invalid = new(EntityUid.Invalid, EntityUid.Invalid, false, false);
|
||||
|
||||
// TODO include MapId if ever grids are allowed to enter null-space (leave PVS).
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
_subscriptionLock = true;
|
||||
|
||||
EntAddComponent(e.BaseArgs.Owner, e.ComponentType);
|
||||
EntAddComponent(e.BaseArgs.Owner, e.ComponentType.Idx);
|
||||
}
|
||||
|
||||
public void OnComponentRemoved(in RemovedComponentEventArgs e)
|
||||
|
||||
48
Robust.Shared/GameObjects/EntityIterator.cs
Normal file
48
Robust.Shared/GameObjects/EntityIterator.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Arch.Core;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
internal struct EntityIterator
|
||||
{
|
||||
private readonly Chunk _chunk;
|
||||
|
||||
internal EntityIterator(in Chunk chunk)
|
||||
{
|
||||
_chunk = chunk;
|
||||
}
|
||||
|
||||
public EntityEnumerator GetEnumerator()
|
||||
{
|
||||
return new EntityEnumerator(_chunk);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct EntityEnumerator
|
||||
{
|
||||
private readonly Chunk _chunk;
|
||||
private int _entityIndex;
|
||||
public Entity Current { get; private set; }
|
||||
|
||||
public EntityEnumerator(in Chunk chunk)
|
||||
{
|
||||
_chunk = chunk;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_entityIndex >= _chunk.Entities.Length)
|
||||
return false;
|
||||
|
||||
Current = _chunk.Entities[_entityIndex];
|
||||
_entityIndex++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class QueryExtensions
|
||||
{
|
||||
internal static EntityIterator ChunkIterator(this in Chunk chunk)
|
||||
{
|
||||
return new EntityIterator(chunk);
|
||||
}
|
||||
}
|
||||
115
Robust.Shared/GameObjects/EntityManager.Arch.cs
Normal file
115
Robust.Shared/GameObjects/EntityManager.Arch.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions;
|
||||
using Arch.Core.Utils;
|
||||
using Collections.Pooled;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial class EntityManager
|
||||
{
|
||||
private World _world = default!;
|
||||
|
||||
private static readonly ComponentType[] DefaultArchetype = new ComponentType[]
|
||||
{
|
||||
typeof(MetaDataComponent),
|
||||
typeof(TransformComponent),
|
||||
};
|
||||
|
||||
protected void InitializeArch()
|
||||
{
|
||||
_world = World.Create();
|
||||
}
|
||||
|
||||
protected void ShutdownArch()
|
||||
{
|
||||
World.Destroy(_world);
|
||||
}
|
||||
|
||||
protected void DestroyArch(EntityUid uid)
|
||||
{
|
||||
var archEnt = (Entity) uid;
|
||||
var reference = _world.Reference(archEnt);
|
||||
|
||||
if (reference.Version != (uid.Version - EntityUid.ArchVersionOffset))
|
||||
{
|
||||
throw new InvalidOperationException($"Tried to delete a different matching entity for Arch.");
|
||||
}
|
||||
|
||||
_world.Destroy(archEnt);
|
||||
}
|
||||
|
||||
private void SpawnEntityArch(out EntityUid entity)
|
||||
{
|
||||
var archEnt = _world.Create(DefaultArchetype);
|
||||
var reference = _world.Reference(archEnt);
|
||||
entity = new EntityUid(reference);
|
||||
}
|
||||
|
||||
public void CleanupArch()
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var arc = _world.Archetypes.Count;
|
||||
_world.TrimExcess();
|
||||
arc -= _world.Archetypes.Count;
|
||||
sw.Stop();
|
||||
_sawmill.Debug($"Trimming {arc} archetypes took {sw.Elapsed.TotalMilliseconds} milliseconds");
|
||||
}
|
||||
|
||||
internal ComponentType[] GetComponentType(EntityPrototype prototype, ICollection<Type>? added = null, ICollection<Type>? missing = null)
|
||||
{
|
||||
var compTypes = new ComponentType[prototype.Components.Count + (added?.Count ?? 0) - (missing?.Count ?? 0)];
|
||||
var idx = 0;
|
||||
|
||||
foreach (var comp in prototype.Components.Values)
|
||||
{
|
||||
var componentType = comp.Component.GetType();
|
||||
if (missing?.Contains(componentType) == true || added?.Contains(componentType) == true)
|
||||
continue;
|
||||
|
||||
compTypes[idx++] = componentType;
|
||||
}
|
||||
|
||||
if (added != null)
|
||||
{
|
||||
foreach (var componentType in added)
|
||||
{
|
||||
if (missing?.Contains(componentType) == true)
|
||||
continue;
|
||||
|
||||
compTypes[idx++] = componentType;
|
||||
}
|
||||
}
|
||||
|
||||
return compTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reserves additional slots for the specified ComponentTypes.
|
||||
/// </summary>
|
||||
internal void Reserve(ComponentType[] compTypes, int count)
|
||||
{
|
||||
_world.Reserve(compTypes, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WARNING: DO NOT CALL THIS UNLESS YOU KNOW WHAT YOU ARE DOING.
|
||||
/// Adds the component types to the entity, shuffling its archetype.
|
||||
/// </summary>
|
||||
internal void AddComponentRange(EntityUid uid, PooledList<ComponentType> compTypes)
|
||||
{
|
||||
DebugTools.Assert(compTypes.Count > 0);
|
||||
_world.AddRange(uid, compTypes);
|
||||
}
|
||||
|
||||
internal void RemoveComponentRange(EntityUid uid, PooledList<ComponentType> compTypes)
|
||||
{
|
||||
DebugTools.Assert(compTypes.Count > 0);
|
||||
_world.RemoveRange(uid, compTypes.Span);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -256,8 +256,7 @@ public partial class EntityManager
|
||||
/// <inheritdoc />
|
||||
public HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities)
|
||||
{
|
||||
var entities = new HashSet<EntityUid>();
|
||||
entities.EnsureCapacity(netEntities.Count);
|
||||
var entities = new HashSet<EntityUid>(netEntities.Count);
|
||||
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
@@ -292,6 +291,16 @@ public partial class EntityManager
|
||||
return entities;
|
||||
}
|
||||
|
||||
public void EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity, HashSet<EntityUid> entities)
|
||||
{
|
||||
entities.Clear();
|
||||
entities.EnsureCapacity(netEntities.Count);
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(EnsureEntity<T>(netEntity, callerEntity));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity)
|
||||
{
|
||||
@@ -305,6 +314,16 @@ public partial class EntityManager
|
||||
return entities;
|
||||
}
|
||||
|
||||
public void EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity, List<EntityUid> entities)
|
||||
{
|
||||
entities.Clear();
|
||||
entities.EnsureCapacity(netEntities.Count);
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(EnsureEntity<T>(netEntity, callerEntity));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityUid> GetEntityList(ICollection<NetEntity> netEntities)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Arch.Core.Extensions.Dangerous;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
@@ -40,6 +41,16 @@ public partial class EntityManager
|
||||
return ents;
|
||||
}
|
||||
|
||||
public EntityUid[] SpawnEntities(MapCoordinates coordinates, string? prototype, int count)
|
||||
{
|
||||
var ents = new EntityUid[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
ents[i] = Spawn(prototype, coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions;
|
||||
using Arch.Core.Utils;
|
||||
using Collections.Pooled;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -13,6 +16,8 @@ using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ComponentRegistry = Robust.Shared.Prototypes.ComponentRegistry;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
@@ -39,6 +44,8 @@ namespace Robust.Shared.GameObjects
|
||||
// positions on spawn....
|
||||
private SharedTransformSystem _xforms = default!;
|
||||
|
||||
private QueryDescription _archMetaQuery = new QueryDescription().WithAll<MetaDataComponent>();
|
||||
|
||||
public EntityQuery<MetaDataComponent> MetaQuery;
|
||||
public EntityQuery<TransformComponent> TransformQuery;
|
||||
|
||||
@@ -62,15 +69,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private EntityDiffContext _context = new();
|
||||
|
||||
/// <summary>
|
||||
/// All entities currently stored in the manager.
|
||||
/// </summary>
|
||||
protected readonly HashSet<EntityUid> Entities = new();
|
||||
|
||||
private EntityEventBus _eventBus = null!;
|
||||
|
||||
protected int NextEntityUid = (int) EntityUid.FirstUid;
|
||||
|
||||
protected int NextNetworkId = (int) NetEntity.First;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -116,7 +116,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
_eventBus = new EntityEventBus(this);
|
||||
|
||||
InitializeComponents();
|
||||
InitializeArch();
|
||||
_metaReg = _componentFactory.GetRegistration(typeof(MetaDataComponent));
|
||||
_xformReg = _componentFactory.GetRegistration(typeof(TransformComponent));
|
||||
_xformName = _xformReg.Name;
|
||||
@@ -144,15 +144,17 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
var protoData = PrototypeManager.GetPrototypeData(prototype);
|
||||
var comps = _entCompIndex[uid];
|
||||
var comps = _world.GetAllComponents(uid);
|
||||
|
||||
// Fast check if the component counts match.
|
||||
// Note that transform and metadata are not included in the prototype data.
|
||||
if (protoData.Count + 2 != comps.Count)
|
||||
if (protoData.Count + 2 != comps.Length)
|
||||
return false;
|
||||
|
||||
foreach (var component in comps)
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
var component = (IComponent)comp!;
|
||||
|
||||
if (component.Deleted)
|
||||
return false;
|
||||
|
||||
@@ -208,6 +210,7 @@ namespace Robust.Shared.GameObjects
|
||||
FlushEntities();
|
||||
_eventBus.ClearEventTables();
|
||||
_entitySystemManager.Shutdown();
|
||||
ShutdownArch();
|
||||
ClearComponents();
|
||||
ShuttingDown = false;
|
||||
Started = false;
|
||||
@@ -215,12 +218,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public virtual void Cleanup()
|
||||
{
|
||||
_componentFactory.ComponentAdded -= OnComponentAdded;
|
||||
ShuttingDown = true;
|
||||
FlushEntities();
|
||||
_entitySystemManager.Clear();
|
||||
_eventBus.Dispose();
|
||||
_eventBus = null!;
|
||||
ShutdownArch();
|
||||
ClearComponents();
|
||||
|
||||
ShuttingDown = false;
|
||||
@@ -269,28 +272,27 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
return CreateEntity(prototypeName, out _, out _, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
return CreateEntity(prototypeName, out _, out _, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
_xforms.SetCoordinates(newEntity, TransformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
var newEntity = CreateEntity(prototypeName, out _, out var xform, overrides);
|
||||
_xforms.SetCoordinates(newEntity, xform, coordinates, unanchor: false);
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
var transform = TransformQuery.GetComponent(newEntity);
|
||||
var newEntity = CreateEntity(prototypeName, out _, out var transform, overrides);
|
||||
|
||||
if (coordinates.MapId == MapId.Nullspace)
|
||||
{
|
||||
@@ -320,10 +322,19 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int EntityCount => Entities.Count;
|
||||
public int EntityCount => _world.Size;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetEntities() => Entities;
|
||||
public IEnumerable<EntityUid> GetEntities()
|
||||
{
|
||||
var ents = new List<Entity>();
|
||||
_world.GetEntities(_archMetaQuery, ents);
|
||||
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
yield return EntityUid.FromArch(_world, entity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void DirtyEntity(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
@@ -375,7 +386,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="e">Entity to remove</param>
|
||||
public virtual void DeleteEntity(EntityUid? uid)
|
||||
{
|
||||
if (uid == null)
|
||||
if (uid == null || uid == EntityUid.Invalid)
|
||||
return;
|
||||
var e = uid.Value;
|
||||
|
||||
@@ -400,9 +411,9 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
// Notify all entities they are being terminated prior to detaching & deleting
|
||||
RecursiveFlagEntityTermination(e, meta);
|
||||
|
||||
var xform = TransformQuery.GetComponent(e);
|
||||
RecursiveFlagEntityTermination(e, meta, xform);
|
||||
|
||||
TransformComponent? parentXform = null;
|
||||
if (xform.ParentUid.IsValid())
|
||||
TransformQuery.Resolve(xform.ParentUid, ref parentXform);
|
||||
@@ -413,14 +424,14 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private void RecursiveFlagEntityTermination(
|
||||
EntityUid uid,
|
||||
MetaDataComponent metadata)
|
||||
MetaDataComponent metadata,
|
||||
TransformComponent transform)
|
||||
{
|
||||
var transform = TransformQuery.GetComponent(uid);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Terminating;
|
||||
|
||||
try
|
||||
{
|
||||
var ev = new EntityTerminatingEvent(uid);
|
||||
var ev = new EntityTerminatingEvent(uid, metadata);
|
||||
EventBus.RaiseLocalEvent(uid, ref ev, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -437,7 +448,7 @@ namespace Robust.Shared.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
RecursiveFlagEntityTermination(child, childMeta);
|
||||
RecursiveFlagEntityTermination(child, childMeta, TransformQuery.GetComponent(child));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,8 +496,12 @@ namespace Robust.Shared.GameObjects
|
||||
_sawmill.Error($"Failed to delete all children of entity: {ToPrettyString(uid)}");
|
||||
|
||||
// Shut down all components.
|
||||
foreach (var component in InSafeOrder(_entCompIndex[uid]))
|
||||
var objComps = _world.GetAllComponents(uid);
|
||||
|
||||
foreach (var comp in objComps)
|
||||
{
|
||||
var component = (IComponent)comp!;
|
||||
|
||||
if (component.Running)
|
||||
{
|
||||
try
|
||||
@@ -501,7 +516,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
// Dispose all my components, in a safe order so transform is available
|
||||
DisposeComponents(uid);
|
||||
DisposeComponents(uid, metadata);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Deleted;
|
||||
|
||||
try
|
||||
@@ -514,14 +529,14 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
_eventBus.OnEntityDeleted(uid);
|
||||
Entities.Remove(uid);
|
||||
DestroyArch(uid);
|
||||
// Need to get the ID above before MetadataComponent shutdown but only remove it after everything else is done.
|
||||
NetEntityLookup.Remove(metadata.NetEntity);
|
||||
}
|
||||
|
||||
public virtual void QueueDeleteEntity(EntityUid? uid)
|
||||
{
|
||||
if (uid == null)
|
||||
if (uid == null || uid.Value == EntityUid.Invalid)
|
||||
return;
|
||||
|
||||
if (!QueuedDeletionsSet.Add(uid.Value))
|
||||
@@ -535,7 +550,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool EntityExists(EntityUid uid)
|
||||
{
|
||||
return MetaQuery.HasComponentInternal(uid);
|
||||
return IsAlive(uid);
|
||||
}
|
||||
|
||||
public bool EntityExists(EntityUid? uid)
|
||||
@@ -554,12 +569,26 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool Deleted(EntityUid uid)
|
||||
{
|
||||
return !MetaQuery.TryGetComponentInternal(uid, out var comp) || comp.EntityDeleted;
|
||||
return !IsAlive(uid) || !_world.TryGet(uid, out MetaDataComponent? comp) || comp!.EntityLifeStage > EntityLifeStage.Terminating;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the entity is alive inside of the ECS world.
|
||||
/// </summary>
|
||||
internal bool IsAlive(EntityUid uid)
|
||||
{
|
||||
return ((EntityReference) uid).IsAlive(_world);
|
||||
}
|
||||
|
||||
internal bool TryAlive(EntityUid uid, out EntityReference entity)
|
||||
{
|
||||
entity = uid;
|
||||
return entity.IsAlive(_world);
|
||||
}
|
||||
|
||||
public bool Deleted([NotNullWhen(false)] EntityUid? uid)
|
||||
{
|
||||
return !uid.HasValue || !MetaQuery.TryGetComponentInternal(uid.Value, out var comp) || comp.EntityDeleted;
|
||||
return !uid.HasValue || Deleted(uid.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -574,7 +603,10 @@ namespace Robust.Shared.GameObjects
|
||||
DeleteEntity(e);
|
||||
}
|
||||
|
||||
if (Entities.Count != 0)
|
||||
// Arch bug atm
|
||||
// CleanupArch();
|
||||
|
||||
if (_world.Size > 0)
|
||||
_sawmill.Error("Entities were spawned while flushing entities.");
|
||||
}
|
||||
|
||||
@@ -583,9 +615,10 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
private protected EntityUid AllocEntity(
|
||||
EntityPrototype? prototype,
|
||||
out MetaDataComponent metadata)
|
||||
out MetaDataComponent metadata,
|
||||
out TransformComponent xform)
|
||||
{
|
||||
var entity = AllocEntity(out metadata);
|
||||
var entity = AllocEntity(out metadata, out xform);
|
||||
metadata._entityPrototype = prototype;
|
||||
Dirty(entity, metadata, metadata);
|
||||
return entity;
|
||||
@@ -594,16 +627,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Allocates an entity and stores it but does not load components or do initialization.
|
||||
/// </summary>
|
||||
private EntityUid AllocEntity(out MetaDataComponent metadata)
|
||||
private EntityUid AllocEntity(out MetaDataComponent metadata, out TransformComponent xform)
|
||||
{
|
||||
var uid = GenerateEntityUid();
|
||||
|
||||
#if DEBUG
|
||||
if (EntityExists(uid))
|
||||
{
|
||||
throw new InvalidOperationException($"UID already taken: {uid}");
|
||||
}
|
||||
#endif
|
||||
SpawnEntityArch(out var uid);
|
||||
|
||||
// we want this called before adding components
|
||||
EntityAdded?.Invoke(uid);
|
||||
@@ -620,16 +646,15 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
SetNetEntity(uid, netEntity, metadata);
|
||||
|
||||
Entities.Add(uid);
|
||||
// add the required MetaDataComponent directly.
|
||||
AddComponentInternal(uid, metadata, _metaReg, false, true, metadata);
|
||||
AddComponentInternal(uid, metadata, _metaReg, true, metadata);
|
||||
|
||||
// allocate the required TransformComponent
|
||||
var xformComp = Unsafe.As<TransformComponent>(_componentFactory.GetComponent(_xformReg));
|
||||
xform = Unsafe.As<TransformComponent>(_componentFactory.GetComponent(_xformReg));
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
xformComp.Owner = uid;
|
||||
xform.Owner = uid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
AddComponentInternal(uid, xformComp, false, true, metadata);
|
||||
AddComponentInternal(uid, xform, true, metadata);
|
||||
|
||||
return uid;
|
||||
}
|
||||
@@ -637,26 +662,26 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, out TransformComponent xform, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(out metadata);
|
||||
return AllocEntity(out metadata, out xform);
|
||||
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
return CreateEntity(prototype, out metadata, context);
|
||||
return CreateEntity(prototype, out metadata, out xform, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, out MetaDataComponent metadata, out TransformComponent xform, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = AllocEntity(prototype, out metadata);
|
||||
var entity = AllocEntity(prototype, out metadata, out xform);
|
||||
try
|
||||
{
|
||||
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
LoadEntity(metadata.EntityPrototype, entity, context);
|
||||
return entity;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -670,18 +695,105 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context)
|
||||
{
|
||||
EntityPrototype.LoadEntity(MetaQuery.GetComponent(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
LoadEntity(MetaQuery.GetComponent(entity).EntityPrototype, entity, context);
|
||||
}
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context, EntityPrototype? prototype)
|
||||
{
|
||||
EntityPrototype.LoadEntity(prototype, entity, ComponentFactory, this, _serManager, context);
|
||||
LoadEntity(prototype, entity, context);
|
||||
}
|
||||
|
||||
internal void LoadEntity(EntityPrototype? prototype, EntityUid entity, IEntityLoadContext? context)
|
||||
{
|
||||
var count = prototype?.Components.Count ?? 2;
|
||||
// Lort forgiv
|
||||
using var types = new PooledList<ComponentType>(count);
|
||||
using var comps = new PooledList<IComponent>(count);
|
||||
using var compRegs = new PooledList<ComponentRegistration>(count);
|
||||
Archetype arc;
|
||||
var metadata = MetaQuery.GetComponent(entity);
|
||||
|
||||
#if DEBUG
|
||||
arc = _world.GetArchetype(entity);
|
||||
#endif
|
||||
|
||||
if (prototype != null)
|
||||
{
|
||||
foreach (var (name, entry) in prototype.Components)
|
||||
{
|
||||
if (context != null && context.ShouldSkipComponent(name))
|
||||
continue;
|
||||
|
||||
var fullData = context != null && context.TryGetComponent(name, out var data) ? data : entry.Component;
|
||||
|
||||
var comp = EntityPrototype.EnsureCompExistsAndDeserialize(entity, _componentFactory, this, _serManager, name, fullData, context as ISerializationContext, metadata);
|
||||
var compType = comp.CompReg.Idx.Type;
|
||||
|
||||
// Don't double add an existing component, just set data above.
|
||||
if (!comp.Add)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
types.Add(compType);
|
||||
comps.Add(comp.Comp);
|
||||
compRegs.Add(comp.CompReg);
|
||||
}
|
||||
}
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
foreach (var name in context.GetExtraComponentTypes())
|
||||
{
|
||||
if (prototype != null && prototype.Components.ContainsKey(name))
|
||||
{
|
||||
// This component also exists in the prototype.
|
||||
// This means that the previous step already caught both the prototype data AND map data.
|
||||
// Meaning that re-running EnsureCompExistsAndDeserialize would wipe prototype data.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!context.TryGetComponent(name, out var data))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{nameof(IEntityLoadContext)} provided component name {name} but refused to provide data");
|
||||
}
|
||||
|
||||
var comp = EntityPrototype.EnsureCompExistsAndDeserialize(entity, _componentFactory, this, _serManager, name, data, context as ISerializationContext, metadata);
|
||||
var compType = comp.CompReg.Idx.Type;
|
||||
|
||||
// Don't double add an existing component, just set data above.
|
||||
if (!comp.Add)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
types.Add(compType);
|
||||
comps.Add(comp.Comp);
|
||||
compRegs.Add(comp.CompReg);
|
||||
}
|
||||
}
|
||||
|
||||
// Shouldn't be changing archetype above or we're having a bad time.
|
||||
DebugTools.Assert(_world.GetArchetype(entity).Equals(arc));
|
||||
|
||||
// Yeah it can happen.
|
||||
if (types.Count == 0)
|
||||
return;
|
||||
|
||||
_world.AddRange(entity, types);
|
||||
|
||||
for (var i = 0; i < comps.Count; i++)
|
||||
{
|
||||
AddComponentInternal(entity, comps[i], compRegs[i], true, metadata: metadata);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeAndStartEntity(EntityUid entity, MapId? mapId = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Pass this + transformcomp around
|
||||
var meta = MetaQuery.GetComponent(entity);
|
||||
InitializeEntity(entity, meta);
|
||||
StartEntity(entity);
|
||||
@@ -760,14 +872,6 @@ namespace Robust.Shared.GameObjects
|
||||
DebugTools.Assert("Why are you raising predictive events on the server?");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory for generating a new EntityUid for an entity currently being created.
|
||||
/// </summary>
|
||||
internal EntityUid GenerateEntityUid()
|
||||
{
|
||||
return new EntityUid(NextEntityUid++);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a unique network id and increments <see cref="NextNetworkId"/>
|
||||
/// </summary>
|
||||
|
||||
@@ -154,18 +154,26 @@ public partial class EntitySystem
|
||||
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void Dirty<T>(Entity<T> ent, MetaDataComponent? meta = null) where T : IComponent
|
||||
protected void Dirty<T>(Entity<T> ent, MetaDataComponent? meta = null) where T : IComponent?
|
||||
{
|
||||
EntityManager.Dirty(ent.Owner, ent.Comp, meta);
|
||||
var comp = ent.Comp;
|
||||
if (comp == null && !EntityManager.TryGetComponent(ent.Owner, out comp))
|
||||
return;
|
||||
|
||||
EntityManager.Dirty(ent.Owner, comp, meta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void Dirty<T>(Entity<T, MetaDataComponent> ent) where T : IComponent
|
||||
protected void Dirty<T>(Entity<T, MetaDataComponent> ent) where T : IComponent?
|
||||
{
|
||||
EntityManager.Dirty(ent.Owner, ent.Comp1, ent.Comp2);
|
||||
var comp = ent.Comp1;
|
||||
if (comp == null && !EntityManager.TryGetComponent(ent.Owner, out comp))
|
||||
return;
|
||||
|
||||
EntityManager.Dirty(ent.Owner, comp, ent.Comp2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -517,7 +525,7 @@ public partial class EntitySystem
|
||||
/// Retrieves whether the entity has the specified component or not.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool HasComp<T>(EntityUid uid)
|
||||
protected bool HasComp<T>(EntityUid uid) where T : IComponent
|
||||
{
|
||||
return EntityManager.HasComponent<T>(uid);
|
||||
}
|
||||
@@ -535,7 +543,7 @@ public partial class EntitySystem
|
||||
/// Retrieves whether the entity has the specified component or not.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool HasComp<T>([NotNullWhen(true)] EntityUid? uid)
|
||||
protected bool HasComp<T>([NotNullWhen(true)] EntityUid? uid) where T : IComponent
|
||||
{
|
||||
return EntityManager.HasComponent<T>(uid);
|
||||
}
|
||||
@@ -564,7 +572,7 @@ public partial class EntitySystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void AddComp<T>(EntityUid uid, T component, bool overwrite = false) where T : IComponent
|
||||
{
|
||||
EntityManager.AddComponent(uid, component, overwrite);
|
||||
EntityManager.AddComponent(uid, component);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.EnsureComponent<T>(EntityUid)"/>
|
||||
@@ -606,13 +614,6 @@ public partial class EntitySystem
|
||||
return EntityManager.RemoveComponentDeferred(uid, type);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.RemoveComponentDeferred(EntityUid, Component)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void RemCompDeferred(EntityUid uid, Component component)
|
||||
{
|
||||
EntityManager.RemoveComponentDeferred(uid, component);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.RemoveComponentDeferred(EntityUid, IComponent)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void RemCompDeferred(EntityUid uid, IComponent component)
|
||||
@@ -655,13 +656,6 @@ public partial class EntitySystem
|
||||
return EntityManager.RemoveComponent(uid, type);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.RemoveComponent(EntityUid, Component)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void RemComp(EntityUid uid, Component component)
|
||||
{
|
||||
EntityManager.RemoveComponent(uid, component);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.RemoveComponent(EntityUid, IComponent)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void RemComp(EntityUid uid, IComponent component)
|
||||
@@ -1016,12 +1010,24 @@ public partial class EntitySystem
|
||||
return EntityManager.EnsureEntitySet<T>(netEntities, callerEntity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity, HashSet<EntityUid> entities)
|
||||
{
|
||||
EntityManager.EnsureEntitySet<T>(netEntities, callerEntity, entities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity)
|
||||
{
|
||||
return EntityManager.EnsureEntityList<T>(netEntities, callerEntity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity, List<EntityUid> entities)
|
||||
{
|
||||
EntityManager.EnsureEntityList<T>(netEntities, callerEntity, entities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,10 +7,12 @@ namespace Robust.Shared.GameObjects
|
||||
public readonly struct EntityTerminatingEvent
|
||||
{
|
||||
public readonly EntityUid Entity;
|
||||
public readonly MetaDataComponent Metadata;
|
||||
|
||||
public EntityTerminatingEvent(EntityUid entity)
|
||||
public EntityTerminatingEvent(EntityUid entity, MetaDataComponent metadata)
|
||||
{
|
||||
Entity = entity;
|
||||
Metadata = metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions.Dangerous;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -9,7 +12,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// This type contains a network identification number of an entity.
|
||||
/// This type contains a local identification number of an entity.
|
||||
/// This can be used by the EntityManager to access an entity
|
||||
/// </summary>
|
||||
[CopyByRef]
|
||||
@@ -17,22 +20,40 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public readonly int Id;
|
||||
|
||||
public readonly int Version;
|
||||
|
||||
/// <summary>
|
||||
/// An Invalid entity UID you can compare against.
|
||||
/// </summary>
|
||||
public static readonly EntityUid Invalid = new(0);
|
||||
public static readonly EntityUid Invalid = new(-1 + ArchUidOffset, -1 + ArchVersionOffset);
|
||||
|
||||
/// <summary>
|
||||
/// The first entity UID the entityManager should use when the manager is initialized.
|
||||
/// </summary>
|
||||
public static readonly EntityUid FirstUid = new(1);
|
||||
public static readonly EntityUid FirstUid = new(0 + ArchUidOffset, 1 + ArchVersionOffset);
|
||||
|
||||
internal const int ArchUidOffset = 1;
|
||||
internal const int ArchVersionOffset = 1;
|
||||
|
||||
public EntityUid()
|
||||
{
|
||||
Id = Invalid.Id;
|
||||
Version = Invalid.Version;
|
||||
}
|
||||
|
||||
internal EntityUid(EntityReference reference)
|
||||
{
|
||||
Id = reference.Entity.Id + ArchUidOffset;
|
||||
Version = reference.Version + ArchVersionOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of this structure, with the given network ID.
|
||||
/// </summary>
|
||||
public EntityUid(int id)
|
||||
public EntityUid(int id, int version)
|
||||
{
|
||||
Id = id;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public bool Valid => IsValid();
|
||||
@@ -40,16 +61,16 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Creates an entity UID by parsing a string number.
|
||||
/// </summary>
|
||||
public static EntityUid Parse(ReadOnlySpan<char> uid)
|
||||
public static EntityUid Parse(ReadOnlySpan<char> uid, ReadOnlySpan<char> version)
|
||||
{
|
||||
return new EntityUid(int.Parse(uid));
|
||||
return new EntityUid(int.Parse(uid), int.Parse(version));
|
||||
}
|
||||
|
||||
public static bool TryParse(ReadOnlySpan<char> uid, out EntityUid entityUid)
|
||||
public static bool TryParse(ReadOnlySpan<char> uid, ReadOnlySpan<char> version, out EntityUid entityUid)
|
||||
{
|
||||
try
|
||||
{
|
||||
entityUid = Parse(uid);
|
||||
entityUid = Parse(uid, version);
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
@@ -59,6 +80,15 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EntityUid FromArch(in World world, in Entity entity)
|
||||
{
|
||||
return new EntityUid(entity.Id + ArchUidOffset, world.Reference(entity).Version + ArchVersionOffset);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int GetArchId() => Id - ArchUidOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the ID value is valid. Does not check if it identifies
|
||||
/// a valid Entity.
|
||||
@@ -66,13 +96,13 @@ namespace Robust.Shared.GameObjects
|
||||
[Pure]
|
||||
public bool IsValid()
|
||||
{
|
||||
return Id > 0;
|
||||
return Id > Invalid.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(EntityUid other)
|
||||
{
|
||||
return Id == other.Id;
|
||||
return Id == other.Id && Version == other.Version;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -85,7 +115,10 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id;
|
||||
unchecked
|
||||
{
|
||||
return Id.GetHashCode() * 397 ^ Version.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,7 +126,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public static bool operator ==(EntityUid a, EntityUid b)
|
||||
{
|
||||
return a.Id == b.Id;
|
||||
return a.Id == b.Id && a.Version == b.Version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,6 +146,21 @@ namespace Robust.Shared.GameObjects
|
||||
return self.Id;
|
||||
}
|
||||
|
||||
public static implicit operator Entity(EntityUid self)
|
||||
{
|
||||
return DangerousEntityExtensions.CreateEntityStruct(self.Id - ArchUidOffset, 0);
|
||||
}
|
||||
|
||||
public static implicit operator EntityReference(EntityUid other)
|
||||
{
|
||||
return DangerousEntityExtensions.CreateEntityReferenceStruct(other.Id - ArchUidOffset, other.Version - ArchVersionOffset, 0);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(EntityReference other)
|
||||
{
|
||||
return new EntityUid(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="uid">Entity being modified.</param>
|
||||
/// <param name="component">Component to add.</param>
|
||||
/// <param name="overwrite">Should it overwrite existing components?</param>
|
||||
void AddComponent<T>(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : IComponent;
|
||||
void AddComponent<T>(EntityUid uid, T component, MetaDataComponent? metadata = null) where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the component with the specified reference type,
|
||||
@@ -136,14 +136,6 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="component">Component to remove.</param>
|
||||
void RemoveComponentDeferred(EntityUid uid, IComponent component);
|
||||
|
||||
/// <summary>
|
||||
/// Immediately shuts down a component, but defers the removal and deletion until the end of the tick.
|
||||
/// Throws if the given component does not belong to the entity.
|
||||
/// </summary>
|
||||
/// <param name="uid">Entity UID to modify.</param>
|
||||
/// <param name="component">Component to remove.</param>
|
||||
void RemoveComponentDeferred(EntityUid uid, Component component);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all components from an entity, except the required components.
|
||||
/// </summary>
|
||||
@@ -164,7 +156,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <typeparam name="T">Component reference type to check for.</typeparam>
|
||||
/// <param name="uid">Entity UID to check.</param>
|
||||
/// <returns>True if the entity has the component type, otherwise false.</returns>
|
||||
bool HasComponent<T>(EntityUid uid);
|
||||
bool HasComponent<T>(EntityUid uid) where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity has a component type.
|
||||
@@ -172,7 +164,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <typeparam name="T">Component reference type to check for.</typeparam>
|
||||
/// <param name="uid">Entity UID to check.</param>
|
||||
/// <returns>True if the entity has the component type, otherwise false.</returns>
|
||||
bool HasComponent<T>(EntityUid? uid);
|
||||
bool HasComponent<T>(EntityUid? uid) where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity has a component type.
|
||||
@@ -281,7 +273,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="uid">Entity UID to check.</param>
|
||||
/// <param name="component">Component of the specified type (if exists).</param>
|
||||
/// <returns>If the component existed in the entity.</returns>
|
||||
bool TryGetComponent<T>(EntityUid uid, [NotNullWhen(true)] out T? component);
|
||||
bool TryGetComponent<T>(EntityUid uid, [NotNullWhen(true)] out T? component) where T : IComponent?;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
@@ -290,7 +282,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="uid">Entity UID to check.</param>
|
||||
/// <param name="component">Component of the specified type (if exists).</param>
|
||||
/// <returns>If the component existed in the entity.</returns>
|
||||
bool TryGetComponent<T>([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out T? component);
|
||||
bool TryGetComponent<T>([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out T? component) where T : IComponent?;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
@@ -505,5 +497,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Culls all components from the collection that are marked as deleted. This needs to be called often.
|
||||
/// </summary>
|
||||
void CullRemovedComponents();
|
||||
|
||||
void CleanupArch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public partial interface IEntityManager
|
||||
EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames);
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, string? prototype, int count);
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames);
|
||||
@@ -52,7 +53,7 @@ public partial interface IEntityManager
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
@@ -64,7 +65,7 @@ public partial interface IEntityManager
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
@@ -84,8 +85,8 @@ public partial interface IEntityManager
|
||||
/// instead attempt to spawn the entity next to the target's parent.
|
||||
/// </summary>
|
||||
EntityUid SpawnNextToOrDrop(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
TransformComponent? xform = null,
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,11 @@ using Robust.Shared.Collections;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
@@ -15,18 +19,6 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
#region Private
|
||||
|
||||
private void AddComponentsIntersecting<T>(
|
||||
EntityUid lookupUid,
|
||||
HashSet<T> intersecting,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query) where T : IComponent
|
||||
{
|
||||
var intersectingEntities = new HashSet<Entity<T>>();
|
||||
AddEntitiesIntersecting(lookupUid, intersectingEntities, worldAABB, flags, query);
|
||||
intersecting.UnionWith(intersectingEntities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
private void AddEntitiesIntersecting<T>(
|
||||
EntityUid lookupUid,
|
||||
HashSet<Entity<T>> intersecting,
|
||||
@@ -88,6 +80,155 @@ public sealed partial class EntityLookupSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void AddEntitiesIntersecting<T>(
|
||||
EntityUid lookupUid,
|
||||
HashSet<Entity<T>> intersecting,
|
||||
IPhysShape shape,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query) where T : IComponent
|
||||
{
|
||||
var lookup = _broadQuery.GetComponent(lookupUid);
|
||||
var invMatrix = _transform.GetInvWorldMatrix(lookupUid);
|
||||
var localAABB = invMatrix.TransformBox(worldAABB);
|
||||
var transform = new Transform(0);
|
||||
var state = new QueryState<T>(
|
||||
intersecting,
|
||||
shape,
|
||||
transform,
|
||||
_fixtures,
|
||||
_physics,
|
||||
_transform,
|
||||
_manifoldManager,
|
||||
query,
|
||||
_fixturesQuery,
|
||||
(flags & LookupFlags.Sensors) != 0
|
||||
);
|
||||
|
||||
if ((flags & LookupFlags.Dynamic) != 0x0)
|
||||
{
|
||||
lookup.DynamicTree.QueryAabb(ref state, static (ref QueryState<T> state, in FixtureProxy value) =>
|
||||
{
|
||||
if (!state.Sensors && !value.Fixture.Hard)
|
||||
return true;
|
||||
|
||||
if (!state.Query.TryGetComponent(value.Entity, out var comp))
|
||||
return true;
|
||||
|
||||
var intersectingTransform = state.Physics.GetPhysicsTransform(value.Entity);
|
||||
if (!state.Manifolds.TestOverlap(state.Shape, 0, value.Fixture.Shape, value.ChildIndex, state.Transform, intersectingTransform))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
state.Intersecting.Add((value.Entity, comp));
|
||||
return true;
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
|
||||
if ((flags & (LookupFlags.Static)) != 0x0)
|
||||
{
|
||||
lookup.StaticTree.QueryAabb(ref state, static (ref QueryState<T> state, in FixtureProxy value) =>
|
||||
{
|
||||
if (!state.Sensors && !value.Fixture.Hard)
|
||||
return true;
|
||||
|
||||
if (!state.Query.TryGetComponent(value.Entity, out var comp))
|
||||
return true;
|
||||
|
||||
var intersectingTransform = state.Physics.GetPhysicsTransform(value.Entity);
|
||||
if (!state.Manifolds.TestOverlap(state.Shape, 0, value.Fixture.Shape, value.ChildIndex, state.Transform, intersectingTransform))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
state.Intersecting.Add((value.Entity, comp));
|
||||
return true;
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
|
||||
if ((flags & LookupFlags.StaticSundries) == LookupFlags.StaticSundries)
|
||||
{
|
||||
lookup.StaticSundriesTree.QueryAabb(ref state, static (ref QueryState<T> state, in EntityUid value) =>
|
||||
{
|
||||
if (!state.Query.TryGetComponent(value, out var comp))
|
||||
return true;
|
||||
|
||||
var intersectingTransform = state.Physics.GetPhysicsTransform(value);
|
||||
|
||||
if (state.FixturesQuery.TryGetComponent(value, out var fixtures))
|
||||
{
|
||||
bool anyFixture = false;
|
||||
foreach (var fixture in fixtures.Fixtures.Values)
|
||||
{
|
||||
if (!state.Sensors && !fixture.Hard)
|
||||
continue;
|
||||
|
||||
anyFixture = true;
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
{
|
||||
if (state.Manifolds.TestOverlap(state.Shape, 0, fixture.Shape, i, state.Transform,
|
||||
intersectingTransform))
|
||||
{
|
||||
state.Intersecting.Add((value, comp));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyFixture)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position))
|
||||
state.Intersecting.Add((value, comp));
|
||||
|
||||
return true;
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
|
||||
if ((flags & LookupFlags.Sundries) != 0x0)
|
||||
{
|
||||
lookup.SundriesTree.QueryAabb(ref state, static (ref QueryState<T> state,
|
||||
in EntityUid value) =>
|
||||
{
|
||||
if (!state.Query.TryGetComponent(value, out var comp))
|
||||
return true;
|
||||
|
||||
var intersectingTransform = state.Physics.GetPhysicsTransform(value);
|
||||
|
||||
if (state.FixturesQuery.TryGetComponent(value, out var fixtures))
|
||||
{
|
||||
bool anyFixture = false;
|
||||
foreach (var fixture in fixtures.Fixtures.Values)
|
||||
{
|
||||
if (!state.Sensors && !fixture.Hard)
|
||||
continue;
|
||||
|
||||
anyFixture = true;
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
{
|
||||
if (state.Manifolds.TestOverlap(state.Shape, 0, fixture.Shape, i, state.Transform,
|
||||
intersectingTransform))
|
||||
{
|
||||
state.Intersecting.Add((value, comp));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyFixture)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position))
|
||||
state.Intersecting.Add((value, comp));
|
||||
|
||||
return true;
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
private bool AnyComponentsIntersecting<T>(
|
||||
EntityUid lookupUid,
|
||||
Box2 worldAABB,
|
||||
@@ -308,15 +449,6 @@ public sealed partial class EntityLookupSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public HashSet<IComponent> GetComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var intersectingEntities = new HashSet<Entity<IComponent>>();
|
||||
GetEntitiesIntersecting(type, mapId, worldAABB, intersectingEntities, flags);
|
||||
var intersecting = new HashSet<IComponent>(intersectingEntities.Select(e => e.Comp));
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
public void GetEntitiesIntersecting(Type type, MapId mapId, Box2 worldAABB, HashSet<Entity<IComponent>> intersecting, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
@@ -366,14 +498,6 @@ public sealed partial class EntityLookupSystem
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public HashSet<T> GetComponentsIntersecting<T>(MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
var intersectingEntities = new HashSet<Entity<T>>();
|
||||
GetEntitiesIntersecting(mapId, worldAABB, intersectingEntities, flags);
|
||||
return new HashSet<T>(intersectingEntities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
public void GetEntitiesIntersecting<T>(MapId mapId, Box2 worldAABB, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
@@ -416,14 +540,163 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
#endregion
|
||||
|
||||
#region EntityCoordinates
|
||||
#region IPhysShape
|
||||
|
||||
public HashSet<T> GetComponentsInRange<T>(EntityCoordinates coordinates, float range) where T : IComponent
|
||||
public void GetEntitiesIntersecting(Type type, MapId mapId, IPhysShape shape, HashSet<Entity<IComponent>> intersecting, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
return GetComponentsInRange<T>(mapPos, range);
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var shapeTransform = new Transform(0);
|
||||
var worldAABB = shape.ComputeAABB(shapeTransform, 0);
|
||||
var sensors = (flags & LookupFlags.Sensors) != 0;
|
||||
if (!UseBoundsQuery(type, worldAABB.Height * worldAABB.Width))
|
||||
{
|
||||
foreach (var (uid, comp) in EntityManager.GetAllComponents(type, true))
|
||||
{
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
var (pos, rot) = _transform.GetWorldPositionRotation(xform);
|
||||
|
||||
if (xform.MapID != mapId ||
|
||||
!worldAABB.Contains(pos) ||
|
||||
((flags & LookupFlags.Contained) == 0x0 &&
|
||||
_container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_fixturesQuery.TryGetComponent(uid, out var fixtures))
|
||||
{
|
||||
var transform = new Transform(pos, rot);
|
||||
|
||||
bool anyFixture = false;
|
||||
foreach (var fixture in fixtures.Fixtures.Values)
|
||||
{
|
||||
if (!sensors && !fixture.Hard)
|
||||
continue;
|
||||
|
||||
anyFixture = true;
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
{
|
||||
if (_manifoldManager.TestOverlap(shape, 0, fixture.Shape, i, shapeTransform, transform))
|
||||
{
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyFixture)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_fixtures.TestPoint(shape, shapeTransform, pos))
|
||||
continue;
|
||||
|
||||
found:
|
||||
intersecting.Add((uid, comp));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var query = EntityManager.GetEntityQuery(type);
|
||||
|
||||
// Get grid entities
|
||||
var state = new GridQueryState<IComponent>(intersecting, shape, worldAABB, this, flags, query);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref state,
|
||||
static (EntityUid uid, MapGridComponent grid, ref GridQueryState<IComponent> state) =>
|
||||
{
|
||||
state.Lookup.AddEntitiesIntersecting(uid, state.Intersecting, state.Shape, state.WorldAABB, state.Flags, state.Query);
|
||||
return true;
|
||||
}, (flags & LookupFlags.Approximate) != 0x0);
|
||||
|
||||
// Get map entities
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
AddEntitiesIntersecting(mapUid, intersecting, shape, worldAABB, flags, query);
|
||||
AddContained(intersecting, flags, query);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetEntitiesIntersecting<T>(MapId mapId, IPhysShape shape, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
var shapeTransform = new Transform(0);
|
||||
var worldAABB = shape.ComputeAABB(shapeTransform, 0);
|
||||
var sensors = (flags & LookupFlags.Sensors) != 0;
|
||||
if (!UseBoundsQuery<T>(worldAABB.Height * worldAABB.Width))
|
||||
{
|
||||
var query = AllEntityQuery<T, TransformComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp, out var xform))
|
||||
{
|
||||
var (pos, rot) = _transform.GetWorldPositionRotation(xform);
|
||||
|
||||
if (xform.MapID != mapId || !worldAABB.Contains(pos))
|
||||
continue;
|
||||
|
||||
if (_fixturesQuery.TryGetComponent(uid, out var fixtures))
|
||||
{
|
||||
var transform = new Transform(pos, rot);
|
||||
bool anyFixture = false;
|
||||
foreach (var fixture in fixtures.Fixtures.Values)
|
||||
{
|
||||
if (!sensors && !fixture.Hard)
|
||||
continue;
|
||||
|
||||
anyFixture = true;
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
{
|
||||
if (_manifoldManager.TestOverlap(shape, 0, fixture.Shape, i, shapeTransform, transform))
|
||||
{
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyFixture)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_fixtures.TestPoint(shape, shapeTransform, pos))
|
||||
continue;
|
||||
|
||||
found:
|
||||
entities.Add((uid, comp));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var query = GetEntityQuery<T>();
|
||||
|
||||
// Get grid entities
|
||||
var state = (this, shape, worldAABB, flags, query, entities);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref state,
|
||||
static (EntityUid uid, MapGridComponent grid,
|
||||
ref (EntityLookupSystem system,
|
||||
IPhysShape shape,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query,
|
||||
HashSet<Entity<T>> intersecting) tuple) =>
|
||||
{
|
||||
tuple.system.AddEntitiesIntersecting(uid, tuple.intersecting, tuple.shape, tuple.worldAABB, tuple.flags, tuple.query);
|
||||
return true;
|
||||
}, (flags & LookupFlags.Approximate) != 0x0);
|
||||
|
||||
// Get map entities
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
AddEntitiesIntersecting(mapUid, entities, shape, worldAABB, flags, query);
|
||||
AddContained(entities, flags, query);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EntityCoordinates
|
||||
|
||||
public void GetEntitiesInRange<T>(EntityCoordinates coordinates, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
{
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
@@ -441,13 +714,6 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
#region MapCoordinates
|
||||
|
||||
[Obsolete]
|
||||
public HashSet<IComponent> GetComponentsInRange(Type type, MapCoordinates coordinates, float range)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
return GetComponentsInRange(type, coordinates.MapId, coordinates.Position, range);
|
||||
}
|
||||
|
||||
public HashSet<Entity<IComponent>> GetEntitiesInRange(Type type, MapCoordinates coordinates, float range)
|
||||
{
|
||||
var entities = new HashSet<Entity<IComponent>>();
|
||||
@@ -461,20 +727,21 @@ public sealed partial class EntityLookupSystem
|
||||
GetEntitiesInRange(type, coordinates.MapId, coordinates.Position, range, entities);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public HashSet<T> GetComponentsInRange<T>(MapCoordinates coordinates, float range) where T : IComponent
|
||||
{
|
||||
return GetComponentsInRange<T>(coordinates.MapId, coordinates.Position, range);
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange<T>(MapCoordinates coordinates, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
public void GetEntitiesInRange<T>(MapCoordinates coordinates, float range, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities);
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities, flags);
|
||||
}
|
||||
|
||||
public HashSet<Entity<T>> GetEntitiesInRange<T>(MapCoordinates coordinates, float range) where T : IComponent
|
||||
public HashSet<Entity<T>> GetEntitiesInRange<T>(MapCoordinates coordinates, float range, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
var entities = new HashSet<Entity<T>>();
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities);
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities, flags);
|
||||
return entities;
|
||||
}
|
||||
|
||||
@@ -482,40 +749,15 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
#region MapId
|
||||
|
||||
public bool AnyComponentsInRange(Type type, MapId mapId, Vector2 worldPos, float range)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return false;
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
return AnyComponentsIntersecting(type, mapId, worldAABB);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public HashSet<IComponent> GetComponentsInRange(Type type, MapId mapId, Vector2 worldPos, float range)
|
||||
{
|
||||
var entities = new HashSet<Entity<IComponent>>();
|
||||
GetEntitiesInRange(type, mapId, worldPos, range, entities);
|
||||
return new HashSet<IComponent>(entities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange(Type type, MapId mapId, Vector2 worldPos, float range, HashSet<Entity<IComponent>> entities)
|
||||
public void GetEntitiesInRange(Type type, MapId mapId, Vector2 worldPos, float range, HashSet<Entity<IComponent>> entities, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
GetEntitiesIntersecting(type, mapId, worldAABB, entities);
|
||||
var circle = new PhysShapeCircle(range, worldPos);
|
||||
GetEntitiesIntersecting(type, mapId, circle, entities, flags);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
@@ -526,18 +768,41 @@ public sealed partial class EntityLookupSystem
|
||||
return new HashSet<T>(entities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange<T>(MapId mapId, Vector2 worldPos, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
public void GetEntitiesInRange<T>(MapId mapId, Vector2 worldPos, float range, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
GetEntitiesInRange(mapId, new PhysShapeCircle(range, worldPos), entities, flags);
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange<T>(MapId mapId, IPhysShape shape, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(shape.Radius > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
GetEntitiesIntersecting(mapId, worldAABB, entities);
|
||||
GetEntitiesIntersecting(mapId, shape, entities, flags);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly record struct GridQueryState<T>(
|
||||
HashSet<Entity<T>> Intersecting,
|
||||
IPhysShape Shape,
|
||||
Box2 WorldAABB,
|
||||
EntityLookupSystem Lookup,
|
||||
LookupFlags Flags,
|
||||
EntityQuery<T> Query
|
||||
) where T : IComponent;
|
||||
|
||||
private readonly record struct QueryState<T>(
|
||||
HashSet<Entity<T>> Intersecting,
|
||||
IPhysShape Shape,
|
||||
Transform Transform,
|
||||
FixtureSystem Fixtures,
|
||||
SharedPhysicsSystem Physics,
|
||||
SharedTransformSystem TransformSystem,
|
||||
IManifoldManager Manifolds,
|
||||
EntityQuery<T> Query,
|
||||
EntityQuery<FixturesComponent> FixturesQuery,
|
||||
bool Sensors
|
||||
) where T : IComponent;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.BroadPhase;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -72,11 +74,14 @@ public record struct WorldAABBEvent
|
||||
|
||||
public sealed partial class EntityLookupSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IManifoldManager _manifoldManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private EntityQuery<BroadphaseComponent> _broadQuery;
|
||||
@@ -108,7 +113,6 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<BroadphaseComponent, EntityTerminatingEvent>(OnBroadphaseTerminating);
|
||||
SubscribeLocalEvent<BroadphaseComponent, ComponentAdd>(OnBroadphaseAdd);
|
||||
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
||||
SubscribeLocalEvent<MapChangedEvent>(OnMapChange);
|
||||
|
||||
SubscribeLocalEvent<MoveEvent>(OnMove);
|
||||
@@ -187,12 +191,6 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGridAdd(GridAddEvent ev)
|
||||
{
|
||||
// Must be done before initialization as that's when broadphase data starts getting set.
|
||||
EnsureComp<BroadphaseComponent>(ev.EntityUid);
|
||||
}
|
||||
|
||||
private void OnBroadphaseAdd(EntityUid uid, BroadphaseComponent component, ComponentAdd args)
|
||||
{
|
||||
component.StaticSundriesTree = new DynamicTree<EntityUid>(
|
||||
@@ -434,7 +432,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
{
|
||||
DebugTools.Assert(!_container.IsEntityOrParentInContainer(uid));
|
||||
DebugTools.Assert(xform.Broadphase == null || xform.Broadphase == new BroadphaseData(broadUid, default, false, staticBody));
|
||||
xform.Broadphase ??= new(broadUid, default, false, staticBody);
|
||||
xform.Broadphase ??= new BroadphaseData(broadUid, EntityUid.Invalid, false, staticBody);
|
||||
(staticBody ? broadphase.StaticSundriesTree : broadphase.SundriesTree).AddOrUpdate(uid, aabb);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,20 +6,20 @@ namespace Robust.Shared.GameObjects;
|
||||
|
||||
public abstract class SharedEyeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes all values for IEye with the component.
|
||||
/// </summary>
|
||||
public void UpdateEye(EyeComponent component)
|
||||
public void UpdateEye(Entity<EyeComponent?> entity)
|
||||
{
|
||||
if (component._eye == null)
|
||||
var component = entity.Comp;
|
||||
if (!Resolve(entity, ref component))
|
||||
return;
|
||||
|
||||
component._eye.Offset = component.Offset;
|
||||
component._eye.DrawFov = component.DrawFov;
|
||||
component._eye.Rotation = component.Rotation;
|
||||
component._eye.Zoom = component.Zoom;
|
||||
component.Eye.Offset = component.Offset;
|
||||
component.Eye.DrawFov = component.DrawFov;
|
||||
component.Eye.DrawLight = component.DrawLight;
|
||||
component.Eye.Rotation = component.Rotation;
|
||||
component.Eye.Zoom = component.Zoom;
|
||||
}
|
||||
|
||||
public void SetOffset(EntityUid uid, Vector2 value, EyeComponent? eyeComponent = null)
|
||||
@@ -31,10 +31,7 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.Offset = value;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
eyeComponent._eye.Offset = value;
|
||||
}
|
||||
eyeComponent.Eye.Offset = value;
|
||||
Dirty(uid, eyeComponent);
|
||||
}
|
||||
|
||||
@@ -47,13 +44,23 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.DrawFov = value;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
eyeComponent._eye.DrawFov = value;
|
||||
}
|
||||
eyeComponent.Eye.DrawFov = value;
|
||||
Dirty(uid, eyeComponent);
|
||||
}
|
||||
|
||||
public void SetDrawLight(Entity<EyeComponent?> entity, bool value)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return;
|
||||
|
||||
if (entity.Comp.DrawLight == value)
|
||||
return;
|
||||
|
||||
entity.Comp.DrawLight = value;
|
||||
entity.Comp.Eye.DrawLight = value;
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
public void SetRotation(EntityUid uid, Angle rotation, EyeComponent? eyeComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref eyeComponent))
|
||||
@@ -63,10 +70,7 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.Rotation = rotation;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
eyeComponent._eye.Rotation = rotation;
|
||||
}
|
||||
eyeComponent.Eye.Rotation = rotation;
|
||||
}
|
||||
|
||||
public void SetTarget(EntityUid uid, EntityUid? value, EyeComponent? eyeComponent = null)
|
||||
@@ -90,10 +94,7 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.Zoom = value;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
eyeComponent._eye.Zoom = value;
|
||||
}
|
||||
eyeComponent.Eye.Zoom = value;
|
||||
}
|
||||
|
||||
public void SetVisibilityMask(EntityUid uid, int value, EyeComponent? eyeComponent = null)
|
||||
|
||||
@@ -103,7 +103,7 @@ internal sealed class SharedGridTraversalSystem : EntitySystem
|
||||
// Attach them to map / they are on an invalid grid
|
||||
if (oldGridId != null)
|
||||
{
|
||||
_transform.SetParent(entity, xform, _mapManager.GetMapEntityIdOrThrow(xform.MapID));
|
||||
_transform.SetParent(entity, xform, xform.MapUid!.Value);
|
||||
var ev = new ChangedGridEvent(entity, oldGridId, null);
|
||||
RaiseLocalEvent(entity, ref ev);
|
||||
}
|
||||
|
||||
@@ -429,8 +429,7 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
private void OnGridInit(EntityUid uid, MapGridComponent component, ComponentInit args)
|
||||
{
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xform = xformQuery.GetComponent(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
|
||||
// Force networkedmapmanager to send it due to non-ECS legacy code.
|
||||
var curTick = _timing.CurTick;
|
||||
@@ -443,7 +442,7 @@ public abstract partial class SharedMapSystem
|
||||
component.LastTileModifiedTick = curTick;
|
||||
|
||||
if (xform.MapUid != null && xform.MapUid != uid)
|
||||
_transform.SetParent(uid, xform, xform.MapUid.Value, xformQuery);
|
||||
_transform.SetParent(uid, xform, xform.MapUid.Value);
|
||||
|
||||
if (!HasComp<MapComponent>(uid))
|
||||
{
|
||||
@@ -689,9 +688,8 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
public IEnumerable<TileRef> GetAllTiles(EntityUid uid, MapGridComponent grid, bool ignoreEmpty = true)
|
||||
{
|
||||
foreach (var kvChunk in grid.Chunks)
|
||||
foreach (var chunk in grid.Chunks.Values)
|
||||
{
|
||||
var chunk = kvChunk.Value;
|
||||
for (ushort x = 0; x < grid.ChunkSize; x++)
|
||||
{
|
||||
for (ushort y = 0; y < grid.ChunkSize; y++)
|
||||
@@ -772,11 +770,54 @@ public abstract partial class SharedMapSystem
|
||||
RegenerateCollision(uid, grid, modified);
|
||||
}
|
||||
|
||||
public TilesEnumerator GetLocalTilesEnumerator(EntityUid uid, MapGridComponent grid, Box2 aabb,
|
||||
bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, aabb);
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
public TilesEnumerator GetTilesEnumerator(EntityUid uid, MapGridComponent grid, Box2 aabb, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var invMatrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localAABB = invMatrix.TransformBox(aabb);
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
public TilesEnumerator GetTilesEnumerator(EntityUid uid, MapGridComponent grid, Box2Rotated bounds, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var invMatrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localAABB = invMatrix.TransformBox(bounds);
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2 localAABB, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
yield return tileRef;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated localArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var localAABB = localArea.CalcBoundingBox();
|
||||
return GetLocalTilesIntersecting(uid, grid, localAABB, ignoreEmpty, predicate);
|
||||
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
yield return tileRef;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated worldArea, bool ignoreEmpty = true,
|
||||
@@ -785,9 +826,11 @@ public abstract partial class SharedMapSystem
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localArea);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
yield return tile;
|
||||
yield return tileRef;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,46 +840,11 @@ public abstract partial class SharedMapSystem
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localArea);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2 localArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
// TODO: Should move the intersecting calls onto mapmanager system and then allow people to pass in xform / xformquery
|
||||
// that way we can avoid the GetComp here.
|
||||
var gridTileLb = new Vector2i((int)Math.Floor(localArea.Left), (int)Math.Floor(localArea.Bottom));
|
||||
// If we have 20.1 we want to include that tile but if we have 20 then we don't.
|
||||
var gridTileRt = new Vector2i((int)Math.Ceiling(localArea.Right), (int)Math.Ceiling(localArea.Top));
|
||||
|
||||
for (var x = gridTileLb.X; x < gridTileRt.X; x++)
|
||||
{
|
||||
for (var y = gridTileLb.Y; y < gridTileRt.Y; y++)
|
||||
{
|
||||
var gridChunk = GridTileToChunkIndices(uid, grid, new Vector2i(x, y));
|
||||
|
||||
if (grid.Chunks.TryGetValue(gridChunk, out var chunk))
|
||||
{
|
||||
var chunkTile = chunk.GridTileToChunkTile(new Vector2i(x, y));
|
||||
var tile = GetTileRef(uid, grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
if (ignoreEmpty && tile.Tile.IsEmpty)
|
||||
continue;
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
yield return tile;
|
||||
}
|
||||
else if (!ignoreEmpty)
|
||||
{
|
||||
var tile = new TileRef(uid, x, y, Tile.Empty);
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
yield return tileRef;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,12 +994,25 @@ public abstract partial class SharedMapSystem
|
||||
// create an entire chunk for it.
|
||||
var gridChunkPos = GridTileToChunkIndices(uid, grid, pos);
|
||||
|
||||
if (!grid.Chunks.TryGetValue(gridChunkPos, out var chunk)) return Enumerable.Empty<EntityUid>();
|
||||
if (!grid.Chunks.TryGetValue(gridChunkPos, out var chunk))
|
||||
return Enumerable.Empty<EntityUid>();
|
||||
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
return chunk.GetSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
}
|
||||
|
||||
public void GetAnchoredEntities(Entity<MapGridComponent> grid, Vector2i pos, List<EntityUid> list)
|
||||
{
|
||||
var gridChunkPos = GridTileToChunkIndices(grid.Owner, grid.Comp, pos);
|
||||
if (!grid.Comp.Chunks.TryGetValue(gridChunkPos, out var chunk))
|
||||
return;
|
||||
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
var anchored = chunk.GetSnapGrid((ushort) chunkTile.X, (ushort) chunkTile.Y);
|
||||
if (anchored != null)
|
||||
list.AddRange(anchored);
|
||||
}
|
||||
|
||||
public AnchoredEntitiesEnumerator GetAnchoredEntitiesEnumerator(EntityUid uid, MapGridComponent grid, Vector2i pos)
|
||||
{
|
||||
var gridChunkPos = GridTileToChunkIndices(uid, grid, pos);
|
||||
@@ -1008,22 +1029,32 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
public IEnumerable<EntityUid> GetLocalAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 localAABB)
|
||||
{
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localAABB, true, null))
|
||||
var enumerator = new TilesEnumerator(this, true, null, uid, grid, localAABB);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
|
||||
var anchoredEnumerator = GetAnchoredEntitiesEnumerator(uid, grid, tileRef.GridIndices);
|
||||
|
||||
while (anchoredEnumerator.MoveNext(out var ent))
|
||||
{
|
||||
yield return ent;
|
||||
yield return ent.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 worldAABB)
|
||||
{
|
||||
foreach (var tile in GetTilesIntersecting(uid, grid, worldAABB))
|
||||
var invWorldMatrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localAABB = invWorldMatrix.TransformBox(worldAABB);
|
||||
var enumerator = new TilesEnumerator(this, true, null, uid, grid, localAABB);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
|
||||
var anchoredEnumerator = GetAnchoredEntitiesEnumerator(uid, grid, tileRef.GridIndices);
|
||||
|
||||
while (anchoredEnumerator.MoveNext(out var ent))
|
||||
{
|
||||
yield return ent;
|
||||
yield return ent.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1279,6 +1310,9 @@ public abstract partial class SharedMapSystem
|
||||
}
|
||||
|
||||
public Vector2i GridTileToChunkIndices(EntityUid uid, MapGridComponent grid, Vector2i gridTile)
|
||||
=> GridTileToChunkIndices(grid, gridTile);
|
||||
|
||||
public Vector2i GridTileToChunkIndices(MapGridComponent grid, Vector2i gridTile)
|
||||
{
|
||||
var x = (int)Math.Floor(gridTile.X / (float) grid.ChunkSize);
|
||||
var y = (int)Math.Floor(gridTile.Y / (float) grid.ChunkSize);
|
||||
@@ -1321,6 +1355,36 @@ public abstract partial class SharedMapSystem
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetTile(MapGridComponent grid, Vector2i indices, out Tile tile)
|
||||
{
|
||||
var chunkIndices = GridTileToChunkIndices(grid, indices);
|
||||
if (!grid.Chunks.TryGetValue(chunkIndices, out var chunk))
|
||||
{
|
||||
tile = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var cTileIndices = chunk.GridTileToChunkTile(indices);
|
||||
tile = chunk.Tiles[cTileIndices.X, cTileIndices.Y];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the <see cref="ITileDefinition"/> for the tile at the given grid indices. This will throw an
|
||||
/// exception if the tile at this location has no registered tile definition.
|
||||
/// </summary>
|
||||
public bool TryGetTileDef(MapGridComponent grid, Vector2i indices, [NotNullWhen(true)] out ITileDefinition? tileDef)
|
||||
{
|
||||
if (!TryGetTile(grid, indices, out var tile))
|
||||
{
|
||||
tileDef = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
tileDef = _tileMan[tile.TypeId];
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetTileRef(EntityUid uid, MapGridComponent grid, EntityCoordinates coords, out TileRef tile)
|
||||
{
|
||||
return TryGetTileRef(uid, grid, CoordinatesToTile(uid, grid, coords), out tile);
|
||||
@@ -1377,4 +1441,98 @@ public abstract partial class SharedMapSystem
|
||||
RegenerateCollision(uid, grid, mapChunk);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates the local tiles of the specified data.
|
||||
/// </summary>
|
||||
public struct TilesEnumerator
|
||||
{
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
|
||||
private readonly EntityUid _uid;
|
||||
private readonly MapGridComponent _grid;
|
||||
private readonly bool _ignoreEmpty;
|
||||
private readonly Predicate<TileRef>? _predicate;
|
||||
|
||||
private readonly int _lowerY;
|
||||
private readonly int _upperX;
|
||||
private readonly int _upperY;
|
||||
|
||||
private int _x;
|
||||
private int _y;
|
||||
|
||||
public TilesEnumerator(
|
||||
SharedMapSystem mapSystem,
|
||||
bool ignoreEmpty,
|
||||
Predicate<TileRef>? predicate,
|
||||
EntityUid uid,
|
||||
MapGridComponent grid,
|
||||
Box2 aabb)
|
||||
{
|
||||
_mapSystem = mapSystem;
|
||||
|
||||
_uid = uid;
|
||||
_grid = grid;
|
||||
_ignoreEmpty = ignoreEmpty;
|
||||
_predicate = predicate;
|
||||
|
||||
// TODO: Should move the intersecting calls onto mapmanager system and then allow people to pass in xform / xformquery
|
||||
// that way we can avoid the GetComp here.
|
||||
var gridTileLb = new Vector2i((int)Math.Floor(aabb.Left), (int)Math.Floor(aabb.Bottom));
|
||||
// If we have 20.1 we want to include that tile but if we have 20 then we don't.
|
||||
var gridTileRt = new Vector2i((int)Math.Ceiling(aabb.Right), (int)Math.Ceiling(aabb.Top));
|
||||
|
||||
_x = gridTileLb.X;
|
||||
_y = gridTileLb.Y;
|
||||
_lowerY = gridTileLb.Y;
|
||||
_upperX = gridTileRt.X;
|
||||
_upperY = gridTileRt.Y;
|
||||
}
|
||||
|
||||
public bool MoveNext(out TileRef tile)
|
||||
{
|
||||
if (_x >= _upperX)
|
||||
{
|
||||
tile = TileRef.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
var gridTile = new Vector2i(_x, _y);
|
||||
|
||||
_y++;
|
||||
|
||||
if (_y >= _upperY)
|
||||
{
|
||||
_x++;
|
||||
_y = _lowerY;
|
||||
}
|
||||
|
||||
var gridChunk = _mapSystem.GridTileToChunkIndices(_uid, _grid, gridTile);
|
||||
|
||||
if (_grid.Chunks.TryGetValue(gridChunk, out var chunk))
|
||||
{
|
||||
var chunkTile = chunk.GridTileToChunkTile(gridTile);
|
||||
tile = _mapSystem.GetTileRef(_uid, _grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
if (_ignoreEmpty && tile.Tile.IsEmpty)
|
||||
return MoveNext(out tile);
|
||||
|
||||
if (_predicate == null || _predicate(tile))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (!_ignoreEmpty)
|
||||
{
|
||||
tile = new TileRef(_uid, gridTile.X, gridTile.Y, Tile.Empty);
|
||||
|
||||
if (_predicate == null || _predicate(tile))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return MoveNext(out tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ public abstract partial class SharedMapSystem
|
||||
{
|
||||
private void InitializeMap()
|
||||
{
|
||||
SubscribeLocalEvent<MapComponent, ComponentAdd>(OnMapAdd);
|
||||
SubscribeLocalEvent<MapComponent, ComponentInit>(OnMapInit);
|
||||
SubscribeLocalEvent<MapComponent, ComponentShutdown>(OnMapRemoved);
|
||||
SubscribeLocalEvent<MapComponent, ComponentHandleState>(OnMapHandleState);
|
||||
@@ -43,8 +42,6 @@ public abstract partial class SharedMapSystem
|
||||
args.State = new MapComponentState(component.MapId, component.LightingEnabled, component.MapPaused);
|
||||
}
|
||||
|
||||
protected abstract void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args);
|
||||
|
||||
private void OnMapInit(EntityUid uid, MapComponent component, ComponentInit args)
|
||||
{
|
||||
EnsureComp<GridTreeComponent>(uid);
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public abstract partial class SharedMapSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ITileDefinitionManager _tileMan = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] protected readonly IMapManager MapManager = default!;
|
||||
[Dependency] private readonly IMapManagerInternal _mapInternal = default!;
|
||||
|
||||
@@ -246,6 +246,39 @@ namespace Robust.Shared.GameObjects
|
||||
// We're on a grid, need to convert the coordinates to grid tiles.
|
||||
return _map.CoordinatesToTile(xform.GridUid.Value, Comp<MapGridComponent>(xform.GridUid.Value), xform.Coordinates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that returns the grid tile an entity is on.
|
||||
/// </summary>
|
||||
public Vector2i GetGridTilePositionOrDefault(Entity<TransformComponent?> entity, MapGridComponent? grid = null)
|
||||
{
|
||||
var xform = entity.Comp;
|
||||
if(!Resolve(entity.Owner, ref xform) || xform.GridUid == null)
|
||||
return Vector2i.Zero;
|
||||
|
||||
if (!Resolve(xform.GridUid.Value, ref grid))
|
||||
return Vector2i.Zero;
|
||||
|
||||
return _map.CoordinatesToTile(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that returns the grid tile an entity is on.
|
||||
/// </summary>
|
||||
public bool TryGetGridTilePosition(Entity<TransformComponent?> entity, out Vector2i indices, MapGridComponent? grid = null)
|
||||
{
|
||||
indices = default;
|
||||
var xform = entity.Comp;
|
||||
if(!Resolve(entity.Owner, ref xform) || xform.GridUid == null)
|
||||
return false;
|
||||
|
||||
if (!Resolve(xform.GridUid.Value, ref grid))
|
||||
return false;
|
||||
|
||||
indices = _map.CoordinatesToTile(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
|
||||
@@ -19,6 +19,10 @@ namespace Robust.Shared.Graphics
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool DrawFov { get; set; } = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public bool DrawLight { get; set; } = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual MapCoordinates Position
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace Robust.Shared.Graphics
|
||||
/// </summary>
|
||||
bool DrawFov { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to draw lights for this eye.
|
||||
/// </summary>
|
||||
bool DrawLight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current position of the center of the eye in the game world.
|
||||
/// </summary>
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Robust.Shared.Localization
|
||||
|
||||
private bool TryGetEntityLocAttrib(EntityUid entity, string attribute, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
if (_entMan.TryGetComponent<GrammarComponent?>(entity, out var grammar) &&
|
||||
if (_entMan.TryGetComponent(entity, out GrammarComponent? grammar) &&
|
||||
grammar.Attributes.TryGetValue(attribute, out value))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace Robust.Shared.Localization
|
||||
{
|
||||
EntityUid entity = (EntityUid)entity0.Value;
|
||||
|
||||
if (_entMan.TryGetComponent<GrammarComponent?>(entity, out var grammar) && grammar.Gender.HasValue)
|
||||
if (_entMan.TryGetComponent(entity, out GrammarComponent? grammar) && grammar.Gender.HasValue)
|
||||
{
|
||||
return new LocValueString(grammar.Gender.Value.ToString().ToLowerInvariant());
|
||||
}
|
||||
@@ -307,7 +307,7 @@ namespace Robust.Shared.Localization
|
||||
{
|
||||
EntityUid entity = (EntityUid)entity0.Value;
|
||||
|
||||
if (_entMan.TryGetComponent<GrammarComponent?>(entity, out var grammar) && grammar.ProperNoun.HasValue)
|
||||
if (_entMan.TryGetComponent(entity, out GrammarComponent? grammar) && grammar.ProperNoun.HasValue)
|
||||
{
|
||||
return new LocValueString(grammar.ProperNoun.Value.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
public float Y => Position.Y;
|
||||
|
||||
public EntityCoordinates()
|
||||
{
|
||||
EntityId = EntityUid.Invalid;
|
||||
Position = Vector2.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="EntityCoordinates"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Map.Enumerators;
|
||||
|
||||
@@ -27,32 +28,33 @@ public struct GridTileEnumerator
|
||||
|
||||
public bool MoveNext([NotNullWhen(true)] out TileRef? tileRef)
|
||||
{
|
||||
if (_index == _chunkSize * _chunkSize)
|
||||
while (true)
|
||||
{
|
||||
if (!_chunkEnumerator.MoveNext())
|
||||
if (_index == _chunkSize * _chunkSize)
|
||||
{
|
||||
tileRef = null;
|
||||
return false;
|
||||
if (!_chunkEnumerator.MoveNext())
|
||||
{
|
||||
tileRef = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
_index = 0;
|
||||
var (chunkOrigin, chunk) = _chunkEnumerator.Current;
|
||||
DebugTools.Assert(chunk.FilledTiles > 0, $"Encountered empty chunk while enumerating tiles");
|
||||
var x = (ushort) (_index / _chunkSize);
|
||||
var y = (ushort) (_index % _chunkSize);
|
||||
var tile = chunk.GetTile(x, y);
|
||||
_index++;
|
||||
|
||||
if (_ignoreEmpty && tile.IsEmpty)
|
||||
continue;
|
||||
|
||||
var gridX = x + chunkOrigin.X * _chunkSize;
|
||||
var gridY = y + chunkOrigin.Y * _chunkSize;
|
||||
tileRef = new TileRef(_gridUid, gridX, gridY, tile);
|
||||
return true;
|
||||
}
|
||||
|
||||
var (chunkOrigin, chunk) = _chunkEnumerator.Current;
|
||||
|
||||
var x = (ushort) (_index / _chunkSize);
|
||||
var y = (ushort) (_index % _chunkSize);
|
||||
var tile = chunk.GetTile(x, y);
|
||||
_index++;
|
||||
|
||||
if (_ignoreEmpty && tile.IsEmpty)
|
||||
{
|
||||
return MoveNext(out tileRef);
|
||||
}
|
||||
|
||||
var gridX = x + chunkOrigin.X * _chunkSize;
|
||||
var gridY = y + chunkOrigin.Y * _chunkSize;
|
||||
tileRef = new TileRef(_gridUid, gridX, gridY, tile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Robust.Shared.Map
|
||||
/// <param name="id">The ID of the tile definition.</param>
|
||||
/// <returns>The tile definition.</returns>
|
||||
ITileDefinition this[int id] { get; }
|
||||
// TODO add a try get and get-or-null variant.
|
||||
|
||||
/// <summary>
|
||||
/// The number of tile definitions contained inside of this manager.
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
// All the obsolete warnings about GridId are probably useless here.
|
||||
@@ -159,6 +161,9 @@ internal partial class MapManager
|
||||
|
||||
var grid = EntityManager.AddComponent<MapGridComponent>(gridEnt);
|
||||
grid.ChunkSize = chunkSize;
|
||||
EntityManager.AddComponent<PhysicsComponent>(gridEnt);
|
||||
EntityManager.AddComponent<FixturesComponent>(gridEnt);
|
||||
EntityManager.AddComponent<BroadphaseComponent>(gridEnt);
|
||||
|
||||
_sawmill.Debug($"Binding new grid {gridEnt}");
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Map;
|
||||
@@ -233,6 +234,7 @@ internal partial class MapManager
|
||||
|
||||
var mapComp = EntityManager.AddComponent<MapComponent>(newEnt);
|
||||
mapComp.MapId = actualId;
|
||||
EntityManager.AddComponent<PhysicsMapComponent>(newEnt);
|
||||
var meta = EntityManager.GetComponent<MetaDataComponent>(newEnt);
|
||||
EntityManager.System<MetaDataSystem>().SetEntityName(newEnt, $"map {actualId}", meta);
|
||||
EntityManager.Dirty(newEnt, mapComp, meta);
|
||||
|
||||
@@ -165,6 +165,6 @@ internal sealed class MapSerializationContext : ISerializationContext, IEntityLo
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new((int)source);
|
||||
return new EntityUid(source.Id, source.Version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,6 +576,17 @@ namespace Robust.Shared.Network
|
||||
// ping the client once per second.
|
||||
netConfig.PingInterval = 1f;
|
||||
|
||||
var poolSize = _config.GetCVar(CVars.NetPoolSize);
|
||||
|
||||
if (poolSize <= 0)
|
||||
{
|
||||
netConfig.UseMessageRecycling = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
netConfig.RecycledCacheMaxCount = Math.Min(poolSize, 8192);
|
||||
}
|
||||
|
||||
netConfig.SendBufferSize = _config.GetCVar(CVars.NetSendBufferSize);
|
||||
netConfig.ReceiveBufferSize = _config.GetCVar(CVars.NetReceiveBufferSize);
|
||||
netConfig.MaximumHandshakeAttempts = 5;
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Physics.BroadPhase;
|
||||
|
||||
namespace Robust.Shared.Physics
|
||||
namespace Robust.Shared.Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the broadphase structure for the relevant grid / map.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class BroadphaseComponent : Component
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Stores the broadphase structure for the relevant grid / map.
|
||||
/// Stores all non-static bodies.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class BroadphaseComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores all non-static bodies.
|
||||
/// </summary>
|
||||
public IBroadPhase DynamicTree = new DynamicTreeBroadPhase();
|
||||
public IBroadPhase DynamicTree = new DynamicTreeBroadPhase();
|
||||
|
||||
/// <summary>
|
||||
/// Stores all static bodies.
|
||||
/// </summary>
|
||||
public IBroadPhase StaticTree = new DynamicTreeBroadPhase();
|
||||
/// <summary>
|
||||
/// Stores all static bodies.
|
||||
/// </summary>
|
||||
public IBroadPhase StaticTree = new DynamicTreeBroadPhase();
|
||||
|
||||
/// <summary>
|
||||
/// Stores all other non-static entities not in another tree.
|
||||
/// </summary>
|
||||
public DynamicTree<EntityUid> SundriesTree = default!;
|
||||
/// <summary>
|
||||
/// Stores all other non-static entities not in another tree.
|
||||
/// </summary>
|
||||
public DynamicTree<EntityUid> SundriesTree = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Stores all other static entities not in another tree.
|
||||
/// </summary>
|
||||
public DynamicTree<EntityUid> StaticSundriesTree = default!;
|
||||
}
|
||||
/// <summary>
|
||||
/// Stores all other static entities not in another tree.
|
||||
/// </summary>
|
||||
public DynamicTree<EntityUid> StaticSundriesTree = default!;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
|
||||
namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
{
|
||||
|
||||
@@ -43,8 +43,7 @@ public abstract partial class SharedJointSystem
|
||||
if (args.Current is not JointRelayComponentState state)
|
||||
return;
|
||||
|
||||
component.Relayed.Clear();
|
||||
component.Relayed.UnionWith(EnsureEntitySet<JointRelayTargetComponent>(state.Entities, uid));
|
||||
EnsureEntitySet<JointRelayTargetComponent>(state.Entities, uid, component.Relayed);
|
||||
}
|
||||
|
||||
private void OnRelayShutdown(EntityUid uid, JointRelayTargetComponent component, ComponentShutdown args)
|
||||
|
||||
@@ -322,7 +322,7 @@ public partial class SharedPhysicsSystem
|
||||
body.AngularVelocity = value;
|
||||
|
||||
if (dirty)
|
||||
Dirty(body);
|
||||
Dirty(uid, body);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -347,7 +347,7 @@ public partial class SharedPhysicsSystem
|
||||
body.LinearVelocity = velocity;
|
||||
|
||||
if (dirty)
|
||||
Dirty(body);
|
||||
Dirty(uid, body);
|
||||
}
|
||||
|
||||
public void SetAngularDamping(PhysicsComponent body, float value, bool dirty = true)
|
||||
@@ -617,7 +617,7 @@ public partial class SharedPhysicsSystem
|
||||
if (!Resolve(uid, ref xform))
|
||||
return new Transform();
|
||||
|
||||
xformQuery ??= GetEntityQuery<TransformComponent>();
|
||||
xformQuery ??= _xformQuery;
|
||||
var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform, xformQuery.Value);
|
||||
|
||||
return new Transform(worldPos, worldRot);
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -488,24 +489,28 @@ namespace Robust.Shared.Physics.Systems
|
||||
if (bodyA.Hard && !fixtureA.Hard)
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(fixtureA.ProxyCount <= 1);
|
||||
|
||||
foreach (var fixtureB in managerB.Fixtures.Values)
|
||||
for (var i = 0; i < fixtureA.Shape.ChildCount; i++)
|
||||
{
|
||||
if (bodyB.Hard && !fixtureB.Hard)
|
||||
continue;
|
||||
input.ProxyA.Set(fixtureA.Shape, i);
|
||||
|
||||
DebugTools.Assert(fixtureB.ProxyCount <= 1);
|
||||
input.ProxyA.Set(fixtureA.Shape, 0);
|
||||
input.ProxyB.Set(fixtureB.Shape, 0);
|
||||
DistanceManager.ComputeDistance(out var output, out _, input);
|
||||
foreach (var fixtureB in managerB.Fixtures.Values)
|
||||
{
|
||||
if (bodyB.Hard && !fixtureB.Hard)
|
||||
continue;
|
||||
|
||||
if (distance < output.Distance)
|
||||
continue;
|
||||
for (var j = 0; j < fixtureB.Shape.ChildCount; j++)
|
||||
{
|
||||
input.ProxyB.Set(fixtureB.Shape, j);
|
||||
DistanceManager.ComputeDistance(out var output, out _, input);
|
||||
|
||||
pointA = output.PointA;
|
||||
pointB = output.PointB;
|
||||
distance = output.Distance;
|
||||
if (distance < output.Distance)
|
||||
continue;
|
||||
|
||||
pointA = output.PointA;
|
||||
pointB = output.PointB;
|
||||
distance = output.Distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,9 +538,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
return false;
|
||||
}
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xfA = GetPhysicsTransform(uidA, xformA, xformQuery);
|
||||
var xfB = GetPhysicsTransform(uidB, xformB, xformQuery);
|
||||
var xfA = GetPhysicsTransform(uidA, xformA);
|
||||
var xfB = GetPhysicsTransform(uidB, xformB);
|
||||
|
||||
return TryGetNearest(uidA, uidB, out pointA, out pointB, out distance, xfA, xfB, managerA, managerB, bodyA, bodyB);
|
||||
}
|
||||
|
||||
@@ -67,12 +67,8 @@ public abstract partial class SharedPhysicsSystem
|
||||
velocityConstraint.TangentSpeed = contact.TangentSpeed;
|
||||
velocityConstraint.IndexA = bodyA.IslandIndex[island.Index];
|
||||
velocityConstraint.IndexB = bodyB.IslandIndex[island.Index];
|
||||
velocityConstraint.Points = new VelocityConstraintPoint[2];
|
||||
|
||||
for (var j = 0; j < 2; j++)
|
||||
{
|
||||
velocityConstraint.Points[j] = new VelocityConstraintPoint();
|
||||
}
|
||||
Array.Resize(ref velocityConstraint.Points, 2);
|
||||
// Don't need to reset point data as it all gets set below.
|
||||
|
||||
var (invMassA, invMassB) = GetInvMass(bodyA, bodyB);
|
||||
|
||||
@@ -91,7 +87,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
(positionConstraint.InvMassA, positionConstraint.InvMassB) = (invMassA, invMassB);
|
||||
positionConstraint.LocalCenterA = bodyA.LocalCenter;
|
||||
positionConstraint.LocalCenterB = bodyB.LocalCenter;
|
||||
positionConstraint.LocalPoints = new Vector2[2];
|
||||
Array.Resize(ref positionConstraint.LocalPoints, 2);
|
||||
|
||||
positionConstraint.InvIA = bodyA.InvI;
|
||||
positionConstraint.InvIB = bodyB.InvI;
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
public bool MetricsEnabled { get; protected set; }
|
||||
|
||||
private EntityQuery<CollideOnAnchorComponent> _collideAnchorQuery;
|
||||
private EntityQuery<FixturesComponent> _fixturesQuery;
|
||||
protected EntityQuery<PhysicsComponent> PhysicsQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
@@ -70,11 +71,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_collideAnchorQuery = GetEntityQuery<CollideOnAnchorComponent>();
|
||||
_fixturesQuery = GetEntityQuery<FixturesComponent>();
|
||||
PhysicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridAdd);
|
||||
SubscribeLocalEvent<PhysicsWakeEvent>(OnWake);
|
||||
SubscribeLocalEvent<PhysicsSleepEvent>(OnSleep);
|
||||
SubscribeLocalEvent<CollisionChangeEvent>(OnCollisionChange);
|
||||
@@ -178,14 +180,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
/// </summary>
|
||||
private void HandleMapChange(EntityUid uid, TransformComponent xform, PhysicsComponent? body, MapId oldMapId, MapId newMapId)
|
||||
{
|
||||
var bodyQuery = GetEntityQuery<PhysicsComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var jointQuery = GetEntityQuery<JointComponent>();
|
||||
|
||||
TryComp(_mapManager.GetMapEntityId(oldMapId), out PhysicsMapComponent? oldMap);
|
||||
TryComp(_mapManager.GetMapEntityId(newMapId), out PhysicsMapComponent? newMap);
|
||||
|
||||
RecursiveMapUpdate(uid, xform, body, newMap, oldMap, bodyQuery, xformQuery, jointQuery);
|
||||
RecursiveMapUpdate(uid, xform, body, newMap, oldMap, jointQuery);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -197,8 +197,6 @@ namespace Robust.Shared.Physics.Systems
|
||||
PhysicsComponent? body,
|
||||
PhysicsMapComponent? newMap,
|
||||
PhysicsMapComponent? oldMap,
|
||||
EntityQuery<PhysicsComponent> bodyQuery,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<JointComponent> jointQuery)
|
||||
{
|
||||
DebugTools.Assert(!Deleted(uid));
|
||||
@@ -222,15 +220,15 @@ namespace Robust.Shared.Physics.Systems
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
if (xformQuery.TryGetComponent(child, out var childXform))
|
||||
if (_xformQuery.TryGetComponent(child, out var childXform))
|
||||
{
|
||||
bodyQuery.TryGetComponent(child, out var childBody);
|
||||
RecursiveMapUpdate(child.Value, childXform, childBody, newMap, oldMap, bodyQuery, xformQuery, jointQuery);
|
||||
PhysicsQuery.TryGetComponent(child, out var childBody);
|
||||
RecursiveMapUpdate(child.Value, childXform, childBody, newMap, oldMap, jointQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGridAdd(GridAddEvent ev)
|
||||
private void OnGridAdd(GridInitializeEvent ev)
|
||||
{
|
||||
var guid = ev.EntityUid;
|
||||
|
||||
@@ -256,7 +254,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
private void OnWake(ref PhysicsWakeEvent @event)
|
||||
{
|
||||
var mapId = EntityManager.GetComponent<TransformComponent>(@event.Entity).MapID;
|
||||
var mapId = _xformQuery.GetComponent(@event.Entity).MapID;
|
||||
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
@@ -267,7 +265,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
private void OnSleep(ref PhysicsSleepEvent @event)
|
||||
{
|
||||
var mapId = EntityManager.GetComponent<TransformComponent>(@event.Entity).MapID;
|
||||
var mapId = _xformQuery.GetComponent(@event.Entity).MapID;
|
||||
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
@@ -282,7 +280,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating) return;
|
||||
|
||||
// If this entity is only meant to collide when anchored, return early.
|
||||
if (TryComp(uid, out CollideOnAnchorComponent? collideComp) && collideComp.Enable)
|
||||
if (_collideAnchorQuery.TryGetComponent(uid, out var collideComp) && collideComp.Enable)
|
||||
return;
|
||||
|
||||
WakeBody(uid, body: physics);
|
||||
|
||||
23
Robust.Shared/Player/ActorSystem.cs
Normal file
23
Robust.Shared/Player/ActorSystem.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Shared.Player;
|
||||
|
||||
/// <summary>
|
||||
/// System that handles <see cref="ActorComponent"/>.
|
||||
/// </summary>
|
||||
public sealed class ActorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ActorComponent, ComponentShutdown>(OnActorShutdown);
|
||||
}
|
||||
|
||||
private void OnActorShutdown(EntityUid entity, ActorComponent component, ComponentShutdown args)
|
||||
{
|
||||
_playerManager.SetAttachedEntity(component.PlayerSession, null);
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,10 @@ internal sealed class CommonSession : ICommonSession
|
||||
public NetUserId UserId { get; }
|
||||
|
||||
[ViewVariables]
|
||||
public string Name { get; set; } = "<Unknown>";
|
||||
public string Name { get; internal set; } = "<Unknown>";
|
||||
|
||||
[ViewVariables]
|
||||
public short Ping { get; set; }
|
||||
public short Ping { get; internal set; }
|
||||
|
||||
[ViewVariables]
|
||||
public DateTime ConnectedTime { get; set; }
|
||||
@@ -34,6 +34,8 @@ internal sealed class CommonSession : ICommonSession
|
||||
[ViewVariables]
|
||||
public SessionData Data { get; }
|
||||
|
||||
public bool ClientSide { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public INetChannel Channel { get; set; } = default!;
|
||||
|
||||
@@ -54,4 +56,4 @@ internal sealed class CommonSession : ICommonSession
|
||||
Name = name;
|
||||
Data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
Robust.Shared/Player/Events.cs
Normal file
63
Robust.Shared/Player/Events.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Player;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets raised when a player has been attached to an entity. This event is both raised directed at the
|
||||
/// entity and broadcast.
|
||||
/// </summary>
|
||||
public sealed class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Entity;
|
||||
public readonly ICommonSession Player;
|
||||
|
||||
public PlayerAttachedEvent(EntityUid entity, ICommonSession player)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets raised when a player has been detached from an entity. This event is both raised directed at the
|
||||
/// entity and broadcast.
|
||||
/// </summary>
|
||||
public sealed class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Entity;
|
||||
public readonly ICommonSession Player;
|
||||
|
||||
public PlayerDetachedEvent(EntityUid entity, ICommonSession player)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="PlayerAttachedEvent"/> that gets raised by the client when the local session gets attached to
|
||||
/// a new entity. This event will also get raised if the local session changes.
|
||||
/// </summary>
|
||||
public sealed class LocalPlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Entity;
|
||||
|
||||
public LocalPlayerAttachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="PlayerDetachedEvent"/> that gets raised by the client when the local session gets attached to
|
||||
/// a new entity. This event will also get raised if the local session changes.
|
||||
/// </summary>
|
||||
public sealed class LocalPlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Entity;
|
||||
|
||||
public LocalPlayerDetachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user