mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d49f27a3bb | ||
|
|
7ebd4e39be | ||
|
|
ef1fd698a0 | ||
|
|
a253c18e01 | ||
|
|
4641634011 | ||
|
|
5d93618442 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,6 +13,3 @@
|
||||
[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
|
||||
|
||||
Submodule Arch/Arch deleted from c76d18feb7
@@ -1,94 +0,0 @@
|
||||
<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 -->
|
||||
|
||||
|
||||
185
RELEASE-NOTES.md
185
RELEASE-NOTES.md
@@ -54,192 +54,13 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 182.0.0
|
||||
## 174.0.3
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Add EntityUid's generation / version to the hashcode.
|
||||
## 174.0.2
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
* Removed toInsertXform and added containerXform in SharedContainerSystem.CanInsert.
|
||||
* Removed EntityQuery parameters from SharedContainerSystem.IsEntityOrParentInContainer.
|
||||
* Changed the signature of ContainsEntity in SharedTransformSystem to use Entity<T>.
|
||||
* Removed one obsoleted SharedTransformSystem.AnchorEntity method.
|
||||
* Changed signature of SharedTransformSystem.SetCoordinates to use Entity<T>.
|
||||
|
||||
### New features
|
||||
|
||||
* Added more Entity<T> query methods.
|
||||
* Added BeforeApplyState event to replay playback.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed inverted GetAllMapGrids map id check.
|
||||
* Fixed transform test warnings.
|
||||
* Fixed PlacementManager warnings.
|
||||
* Fixed reparenting bug for entities that are being deleted.
|
||||
|
||||
### Other
|
||||
|
||||
* Changed VerticalAlignment of RichTextLabel to Center to be consistent with Label.
|
||||
* Changed PVS error log to be a warning instead.
|
||||
* Marked insert and remove container methods as obsolete, added container system methods to replace them.
|
||||
* Marked TransformComponent.MapPosition as obsolete, added GetMapCoordinates system method to replace it.
|
||||
|
||||
### Internal
|
||||
|
||||
* Moved TryGetUi/TryToggleUi/ToggleUi/TryOpen/OpenUi/TryClose/CloseUi methods from UserInterfaceSystem to SharedUserInterfaceSystem.
|
||||
|
||||
|
||||
## 176.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Reverted audio rework temporarily until packaging is fixed.
|
||||
* Changes to Robust.Packaging to facilitate Content.Packaging ports from the python packaging scripts.
|
||||
|
||||
### New features
|
||||
|
||||
* Add a cvar for max game state buffer size.
|
||||
* Add an overload for GetEntitiesInRange that takes in a set.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PVS initial list capacity always being 0.
|
||||
* Fix replay lerp error spam.
|
||||
|
||||
|
||||
## 175.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed static SoundSystem.Play methods.
|
||||
* Moved IPlayingAudioStream onto AudioComponent and entities instead of an abstract stream.
|
||||
* IResourceCache is in shared and IClientResourceCache is the client version to use for textures.
|
||||
* Default audio attenuation changed from InverseDistanceClamped to LinearDistanceClamped.
|
||||
* Removed per-source audio attenuation.
|
||||
|
||||
### New features
|
||||
|
||||
* Add preliminary support for EFX Reverb presets + auxiliary slots; these are also entities.
|
||||
* Audio on grid entities is now attached to the grid.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* If an audio entity comes into PVS range its track will start at the relevant offset and not the beginning.
|
||||
* Z-Axis offset is considered for ReferenceDistance / MaxDistance for audio.
|
||||
* Audio will now pause if the attached entity is paused.
|
||||
|
||||
### Other
|
||||
|
||||
* Changed audio Z-Axis offset from -5m to -1m.
|
||||
## 174.0.1
|
||||
|
||||
|
||||
## 174.0.0
|
||||
|
||||
@@ -2203,207 +2203,3 @@
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: Arch
|
||||
license: |
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Lars Matthäus/genaray
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
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, -1);
|
||||
var uid = new EntityUid(i);
|
||||
_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), typeof(T));
|
||||
public static readonly CompIdx Idx = new(Interlocked.Increment(ref _compIndexMaster));
|
||||
}
|
||||
|
||||
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, -1));
|
||||
Comps[i] = _entityManager.GetComponent<A>(new EntityUid(i));
|
||||
}
|
||||
|
||||
// Return something so the JIT doesn't optimize out all the GetComponent calls.
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<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], "-1");
|
||||
var uid = EntityUid.Parse(args[0]);
|
||||
var entmgr = _entityManager;
|
||||
if (!entmgr.EntityExists(uid))
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = Type.GetType(args[0]);
|
||||
var type = GetType(args[0]);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
@@ -25,6 +25,17 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteLine(sig);
|
||||
}
|
||||
}
|
||||
|
||||
private Type? GetType(string name)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.GetType(name) is { } type)
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
GC.Collect();
|
||||
|
||||
Span<EntityUid> ents = stackalloc EntityUid[amount];
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
@@ -51,17 +50,12 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
ents[i] = _entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
|
||||
_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, out _);
|
||||
return base.CreateEntity(prototypeName, out metadata);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
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;
|
||||
|
||||
@@ -25,13 +26,17 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
|
||||
private void OnEyeAutoState(EntityUid uid, EyeComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateEye((uid, component));
|
||||
UpdateEye(component);
|
||||
}
|
||||
|
||||
private void OnEyeAttached(EntityUid uid, EyeComponent component, LocalPlayerAttachedEvent args)
|
||||
{
|
||||
UpdateEye((uid, component));
|
||||
_eyeManager.CurrentEye = component.Eye;
|
||||
// TODO: This probably shouldn't be nullable bruv.
|
||||
if (component._eye != null)
|
||||
{
|
||||
_eyeManager.CurrentEye = component._eye;
|
||||
}
|
||||
|
||||
var ev = new EyeAttachedEvent(uid, component);
|
||||
RaiseLocalEvent(uid, ref ev, true);
|
||||
}
|
||||
@@ -43,7 +48,13 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
|
||||
private void OnInit(EntityUid uid, EyeComponent component, ComponentInit args)
|
||||
{
|
||||
UpdateEye((uid, component));
|
||||
component._eye = new Eye
|
||||
{
|
||||
Position = Transform(uid).MapPosition,
|
||||
Zoom = component.Zoom,
|
||||
DrawFov = component.DrawFov,
|
||||
Rotation = component.Rotation,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -53,7 +64,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))
|
||||
@@ -62,7 +73,7 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
eyeComponent.Target = null;
|
||||
}
|
||||
|
||||
eyeComponent.Eye.Position = xform.MapPosition;
|
||||
eyeComponent._eye.Position = xform.MapPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
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
|
||||
{
|
||||
@@ -25,5 +28,10 @@ namespace Robust.Client.GameObjects
|
||||
base.Shutdown();
|
||||
_overlayManager.RemoveOverlay<TileEdgeOverlay>();
|
||||
}
|
||||
|
||||
protected override void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args)
|
||||
{
|
||||
EnsureComp<PhysicsMapComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,9 @@ namespace Robust.Client.GameObjects
|
||||
// should show the entity lerping.
|
||||
// - If the client predicts an entity will move while already lerping due to a state-application, it should
|
||||
// clear the state's lerp, under the assumption that the client predicted the state and already rendered
|
||||
// the entity in the state's final position.
|
||||
// the entity in the final position.
|
||||
// - If the client predicts that an entity moves, then we only lerp if this is the first time that the tick
|
||||
// was predicted. I.e., we assume the entity was already rendered in the final position that was
|
||||
// previously predicted.
|
||||
// was predicted. I.e., we assume the entity was already rendered in it's final of that lerp.
|
||||
// - If the client predicts that an entity should lerp twice in the same tick, then we need to combine them.
|
||||
// I.e. moving from a->b then b->c, the client should lerp from a->c.
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnTerminate(ref EntityTerminatingEvent ev)
|
||||
{
|
||||
if (!_timing.InPrediction || IsClientSide(ev.Entity, ev.Metadata))
|
||||
if (!_timing.InPrediction || IsClientSide(ev.Entity))
|
||||
return;
|
||||
|
||||
// Client-side entity deletion is not supported and will cause errors.
|
||||
|
||||
@@ -5,8 +5,6 @@ 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;
|
||||
@@ -31,9 +29,7 @@ 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
|
||||
{
|
||||
@@ -56,12 +52,11 @@ 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);
|
||||
|
||||
@@ -129,8 +124,6 @@ namespace Robust.Client.GameStates
|
||||
public bool DropStates;
|
||||
#endif
|
||||
|
||||
private bool _resettingPredictedEntities;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -153,7 +146,6 @@ namespace Robust.Client.GameStates
|
||||
_config.OnValueChanged(CVars.NetPredictLagBias, i => PredictLagBias = i, true);
|
||||
_config.OnValueChanged(CVars.NetStateBufMergeThreshold, i => StateBufferMergeThreshold = i, true);
|
||||
_config.OnValueChanged(CVars.NetPVSEntityExitBudget, i => _pvsDetachBudget = i, true);
|
||||
_config.OnValueChanged(CVars.NetMaxBufferSize, i => _processor.MaxBufferSize = i, true);
|
||||
|
||||
_processor.Interpolation = _config.GetCVar(CVars.NetInterp);
|
||||
_processor.BufferSize = _config.GetCVar(CVars.NetBufferSize);
|
||||
@@ -168,8 +160,6 @@ 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.");
|
||||
@@ -177,23 +167,6 @@ 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()
|
||||
{
|
||||
@@ -545,9 +518,7 @@ namespace Robust.Client.GameStates
|
||||
var countReset = 0;
|
||||
var system = _entitySystemManager.GetEntitySystem<ClientDirtySystem>();
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
using var toRemove = new PooledList<IComponent>();
|
||||
using var toAdd = new PooledList<ushort>();
|
||||
using var toAddStates = new PooledList<ComponentState>();
|
||||
RemQueue<IComponent> toRemove = new();
|
||||
|
||||
foreach (var entity in system.DirtyEntities)
|
||||
{
|
||||
@@ -565,64 +536,49 @@ namespace Robust.Client.GameStates
|
||||
|
||||
countReset += 1;
|
||||
|
||||
try
|
||||
foreach (var (netId, comp) in meta.NetComponents)
|
||||
{
|
||||
_resettingPredictedEntities = true;
|
||||
if (!comp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
foreach (var (netId, comp) in meta.NetComponents)
|
||||
// Was this component added during prediction?
|
||||
if (comp.CreationTick > _timing.LastRealTick)
|
||||
{
|
||||
if (!comp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
// Was this component added during prediction?
|
||||
if (comp.CreationTick > _timing.LastRealTick)
|
||||
if (last.ContainsKey(netId))
|
||||
{
|
||||
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;
|
||||
}
|
||||
// Component was probably removed and then re-addedd during a single prediction run
|
||||
// Just reset state as normal.
|
||||
comp.ClearCreationTick();
|
||||
}
|
||||
|
||||
if (comp.LastModifiedTick <= _timing.LastRealTick ||
|
||||
!last.TryGetValue(netId, out var compState))
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_resettingPredictedEntities = false;
|
||||
}
|
||||
|
||||
if (toRemove.Count > 0)
|
||||
{
|
||||
// Remove predicted component additions
|
||||
// TODO: 1 archetype change.
|
||||
foreach (var comp in toRemove)
|
||||
if (comp.LastModifiedTick <= _timing.LastRealTick || !last.TryGetValue(netId, out var compState))
|
||||
{
|
||||
_entities.RemoveComponent(entity, comp, meta);
|
||||
continue;
|
||||
}
|
||||
|
||||
toRemove.Clear();
|
||||
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)
|
||||
{
|
||||
_entities.RemoveComponent(entity, comp);
|
||||
}
|
||||
toRemove.Clear();
|
||||
|
||||
// Re-add predicted removals
|
||||
if (system.RemovedComponents.TryGetValue(entity, out var netIds))
|
||||
{
|
||||
@@ -634,29 +590,15 @@ namespace Robust.Client.GameStates
|
||||
if (!last.TryGetValue(netId, out var state))
|
||||
continue;
|
||||
|
||||
toAdd.Add(netId);
|
||||
toAddStates.Add(state);
|
||||
}
|
||||
var comp = _entityManager.AddComponent(entity, netId, meta);
|
||||
|
||||
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);
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($" A component was removed: {comp.GetType()}");
|
||||
|
||||
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();
|
||||
var stateEv = new ComponentHandleState(state, null);
|
||||
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
|
||||
comp.ClearCreationTick(); // don't undo the re-adding.
|
||||
comp.LastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -739,9 +681,10 @@ namespace Robust.Client.GameStates
|
||||
_config.TickProcessMessages();
|
||||
}
|
||||
|
||||
(IEnumerable<NetEntity> Created, List<NetEntity> Detached) output;
|
||||
using (_prof.Group("Entity"))
|
||||
{
|
||||
ApplyEntityStates(curState, nextState);
|
||||
output = ApplyEntityStates(curState, nextState);
|
||||
}
|
||||
|
||||
using (_prof.Group("Player"))
|
||||
@@ -751,13 +694,13 @@ namespace Robust.Client.GameStates
|
||||
|
||||
using (_prof.Group("Callback"))
|
||||
{
|
||||
GameStateApplied?.Invoke(new GameStateAppliedArgs(curState, _detached));
|
||||
GameStateApplied?.Invoke(new GameStateAppliedArgs(curState, output.Detached));
|
||||
}
|
||||
|
||||
return _created;
|
||||
return output.Created;
|
||||
}
|
||||
|
||||
private void ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
private (IEnumerable<NetEntity> Created, List<NetEntity> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
{
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
@@ -766,8 +709,6 @@ namespace Robust.Client.GameStates
|
||||
var enteringPvs = 0;
|
||||
_toApply.Clear();
|
||||
_toCreate.Clear();
|
||||
_detached.Clear();
|
||||
_created.Clear();
|
||||
_pendingReapplyNetStates.Clear();
|
||||
var curSpan = curState.EntityStates.Span;
|
||||
|
||||
@@ -792,7 +733,6 @@ 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.
|
||||
@@ -846,7 +786,7 @@ namespace Robust.Client.GameStates
|
||||
// Detach entities to null space
|
||||
var containerSys = _entitySystemManager.GetEntitySystem<ContainerSystem>();
|
||||
var lookupSys = _entitySystemManager.GetEntitySystem<EntityLookupSystem>();
|
||||
ProcessPvsDeparture(_detached, curState.ToSequence, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
var detached = ProcessPvsDeparture(curState.ToSequence, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
|
||||
// Check next state (AFTER having created new entities introduced in curstate)
|
||||
if (nextState != null)
|
||||
@@ -948,6 +888,8 @@ namespace Robust.Client.GameStates
|
||||
|
||||
_prof.WriteValue("State Size", ProfData.Int32(curSpan.Length));
|
||||
_prof.WriteValue("Entered PVS", ProfData.Int32(enteringPvs));
|
||||
|
||||
return (_toCreate.Keys, detached);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -978,7 +920,7 @@ namespace Robust.Client.GameStates
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
using var toDelete = new PooledList<EntityUid>();
|
||||
_toDelete.Clear();
|
||||
|
||||
// Client side entities won't need the transform, but that should always be a tiny minority of entities
|
||||
var metaQuery = _entityManager.AllEntityQueryEnumerator<MetaDataComponent, TransformComponent>();
|
||||
@@ -989,7 +931,7 @@ namespace Robust.Client.GameStates
|
||||
if (metadata.NetEntity.IsClientSide())
|
||||
{
|
||||
if (deleteClientEntities)
|
||||
toDelete.Add(ent);
|
||||
_toDelete.Add(ent);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -1016,14 +958,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);
|
||||
}
|
||||
@@ -1083,8 +1025,7 @@ namespace Robust.Client.GameStates
|
||||
Detach(GameTick.MaxValue, null, entities, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
}
|
||||
|
||||
private void ProcessPvsDeparture(
|
||||
IList<NetEntity> detached,
|
||||
private List<NetEntity> ProcessPvsDeparture(
|
||||
GameTick toTick,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
@@ -1092,17 +1033,18 @@ namespace Robust.Client.GameStates
|
||||
ContainerSystem containerSys,
|
||||
EntityLookupSystem lookupSys)
|
||||
{
|
||||
using var toDetach = new PooledList<(GameTick Tick, List<NetEntity> Entities)>();
|
||||
_processor.GetEntitiesToDetach(toDetach, toTick, _pvsDetachBudget);
|
||||
var toDetach = _processor.GetEntitiesToDetach(toTick, _pvsDetachBudget);
|
||||
var detached = new List<NetEntity>();
|
||||
|
||||
if (toDetach.Count == 0)
|
||||
return;
|
||||
return detached;
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -1110,6 +1052,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(detached.Count));
|
||||
return detached;
|
||||
}
|
||||
|
||||
private void Detach(GameTick maxTick,
|
||||
@@ -1120,7 +1063,7 @@ namespace Robust.Client.GameStates
|
||||
SharedTransformSystem xformSys,
|
||||
ContainerSystem containerSys,
|
||||
EntityLookupSystem lookupSys,
|
||||
IList<NetEntity>? detached = null)
|
||||
List<NetEntity>? detached = null)
|
||||
{
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
@@ -1174,7 +1117,7 @@ namespace Robust.Client.GameStates
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
using var brokenEnts = new PooledList<EntityUid>();
|
||||
var brokenEnts = new List<EntityUid>();
|
||||
#endif
|
||||
using (_prof.Group("Initialize Entity"))
|
||||
{
|
||||
@@ -1238,28 +1181,18 @@ namespace Robust.Client.GameStates
|
||||
// First remove any deleted components
|
||||
if (curState?.NetComponents != null)
|
||||
{
|
||||
using var toRemove = new PooledList<IComponent>();
|
||||
using var compTypes = new PooledList<ComponentType>();
|
||||
_toRemove.Clear();
|
||||
|
||||
foreach (var (id, comp) in meta.NetComponents)
|
||||
{
|
||||
if (comp.NetSyncEnabled && !curState.NetComponents.Contains(id))
|
||||
{
|
||||
toRemove.Add(comp);
|
||||
compTypes.Add(comp.GetType());
|
||||
}
|
||||
_toRemove.Add(comp);
|
||||
}
|
||||
|
||||
if (toRemove.Count > 0)
|
||||
foreach (var comp in _toRemove)
|
||||
{
|
||||
foreach (var comp in toRemove)
|
||||
{
|
||||
_entityManager.RemoveComponentInternal(uid, comp, terminating: false, archetypeChange: false, meta);
|
||||
}
|
||||
_entities.RemoveComponent(uid, comp, meta);
|
||||
}
|
||||
|
||||
if (compTypes.Count > 0)
|
||||
_entityManager.RemoveComponentRange(uid, compTypes);
|
||||
}
|
||||
|
||||
if (enteringPvs)
|
||||
@@ -1274,7 +1207,7 @@ namespace Robust.Client.GameStates
|
||||
if (!meta.NetComponents.TryGetValue(id, out var comp))
|
||||
{
|
||||
comp = _compFactory.GetComponent(id);
|
||||
_entityManager.AddComponent(uid, comp, metadata: meta);
|
||||
_entityManager.AddComponent(uid, comp, true, metadata: meta);
|
||||
}
|
||||
|
||||
_compStateWork[id] = (comp, state, null);
|
||||
@@ -1282,49 +1215,18 @@ 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))
|
||||
{
|
||||
var registration = _compFactory.GetRegistration(compChange.NetID);
|
||||
addedRegistrations.Add(registration);
|
||||
comp = _compFactory.GetComponent(registration);
|
||||
comp.Owner = uid;
|
||||
addedComps.Add(comp);
|
||||
addedCompTypes.Add(comp.GetType());
|
||||
comp = _compFactory.GetComponent(compChange.NetID);
|
||||
_entityManager.AddComponent(uid, comp, true, metadata:meta);
|
||||
}
|
||||
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)
|
||||
@@ -1414,7 +1316,7 @@ namespace Robust.Client.GameStates
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], "-1", out uid))
|
||||
if (!EntityUid.TryParse(args[0], out uid))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-parse-failure-uid", ("arg", args[0])));
|
||||
meta = null;
|
||||
@@ -1546,22 +1448,23 @@ namespace Robust.Client.GameStates
|
||||
if (!meta.NetComponents.TryGetValue(id, out var comp))
|
||||
{
|
||||
comp = _compFactory.GetComponent(id);
|
||||
_entityManager.AddComponent(uid, comp, meta);
|
||||
_entityManager.AddComponent(uid, comp, true, meta);
|
||||
}
|
||||
|
||||
var handleState = new ComponentHandleState(state, null);
|
||||
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
|
||||
}
|
||||
|
||||
using var toRemove = new PooledList<IComponent>();
|
||||
// ensure we don't have any extra components
|
||||
_toRemove.Clear();
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1574,10 +1477,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public sealed class GameStateAppliedArgs : EventArgs
|
||||
{
|
||||
public readonly GameState AppliedState;
|
||||
public readonly IReadOnlyList<NetEntity> Detached;
|
||||
public GameState AppliedState { get; }
|
||||
public readonly List<NetEntity> Detached;
|
||||
|
||||
public GameStateAppliedArgs(GameState appliedState, IReadOnlyList<NetEntity> detached)
|
||||
public GameStateAppliedArgs(GameState appliedState, List<NetEntity> detached)
|
||||
{
|
||||
AppliedState = appliedState;
|
||||
Detached = detached;
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Robust.Client.GameStates
|
||||
/// <inheritdoc />
|
||||
internal sealed class GameStateProcessor : IGameStateProcessor
|
||||
{
|
||||
public const int MaxBufferSize = 512;
|
||||
|
||||
private readonly IClientGameTiming _timing;
|
||||
private readonly IClientGameStateManager _state;
|
||||
private readonly ISawmill _logger;
|
||||
@@ -26,8 +28,6 @@ namespace Robust.Client.GameStates
|
||||
public (GameTick Tick, DateTime Time)? LastFullStateRequested { get; private set; } = (GameTick.Zero, DateTime.MaxValue);
|
||||
|
||||
private int _bufferSize;
|
||||
private int _maxBufferSize = 512;
|
||||
public const int MinimumMaxBufferSize = 256;
|
||||
|
||||
/// <summary>
|
||||
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
|
||||
@@ -48,14 +48,7 @@ namespace Robust.Client.GameStates
|
||||
public int BufferSize
|
||||
{
|
||||
get => _bufferSize;
|
||||
set => _bufferSize = Math.Max(value, 0);
|
||||
}
|
||||
|
||||
public int MaxBufferSize
|
||||
{
|
||||
get => _maxBufferSize;
|
||||
// We place a lower bound on the maximum size to avoid spamming servers with full game state requests.
|
||||
set => _maxBufferSize = Math.Max(value, MinimumMaxBufferSize);
|
||||
set => _bufferSize = value < 0 ? 0 : value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -107,21 +100,21 @@ namespace Robust.Client.GameStates
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LastFullState == null && state.FromSequence == GameTick.Zero)
|
||||
if (LastFullState == null && state.FromSequence == GameTick.Zero && state.ToSequence >= LastFullStateRequested!.Value.Tick)
|
||||
{
|
||||
if (state.ToSequence >= LastFullStateRequested!.Value.Tick)
|
||||
{
|
||||
LastFullState = state;
|
||||
_logger.Info($"Received Full GameState: to={state.ToSequence}, sz={state.PayloadSize}");
|
||||
return true;
|
||||
}
|
||||
LastFullState = state;
|
||||
|
||||
_logger.Info($"Received a late full game state. Received: {state.ToSequence}. Requested: {LastFullStateRequested.Value.Tick}");
|
||||
if (Logging)
|
||||
_logger.Info($"Received Full GameState: to={state.ToSequence}, sz={state.PayloadSize}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LastFullState != null && state.ToSequence <= LastFullState.ToSequence)
|
||||
{
|
||||
_logger.Info($"While waiting for full, received late GameState with lower to={state.ToSequence} than the last full state={LastFullState.ToSequence}");
|
||||
if (Logging)
|
||||
_logger.Info($"While waiting for full, received late GameState with lower to={state.ToSequence} than the last full state={LastFullState.ToSequence}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -304,8 +297,9 @@ Had full state: {LastFullState != null}"
|
||||
|
||||
public void ClearDetachQueue() => _pvsDetachMessages.Clear();
|
||||
|
||||
public void GetEntitiesToDetach(IList<(GameTick Tick, List<NetEntity> Entities)> result, GameTick toTick, int budget)
|
||||
public List<(GameTick Tick, List<NetEntity> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
|
||||
{
|
||||
var result = new List<(GameTick Tick, List<NetEntity> Entities)>();
|
||||
foreach (var (tick, entities) in _pvsDetachMessages)
|
||||
{
|
||||
if (tick > toTick)
|
||||
@@ -324,6 +318,7 @@ 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.DrawLight && eye.DrawFov)
|
||||
if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawFov)
|
||||
{
|
||||
ApplyFovToBuffer(viewport, eye);
|
||||
}
|
||||
|
||||
@@ -97,9 +97,6 @@ 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()
|
||||
{
|
||||
|
||||
@@ -335,7 +332,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private void DrawLightsAndFov(Viewport viewport, Box2Rotated worldBounds, Box2 worldAABB, IEye eye)
|
||||
{
|
||||
if (!_lightManager.Enabled || !eye.DrawLight)
|
||||
if (!_lightManager.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -573,28 +570,6 @@ 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,
|
||||
@@ -620,10 +595,20 @@ 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, _lightCap);
|
||||
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;
|
||||
}));
|
||||
|
||||
// Next, sort just the shadow casting lights by distance.
|
||||
Array.Sort(_lightsToRenderList, state.count - state.shadowCastingCount, state.shadowCastingCount, _shadowCap);
|
||||
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);
|
||||
}));
|
||||
|
||||
// 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,20 +42,15 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds, ref _grids);
|
||||
|
||||
var mapSystem = _entManager.System<SharedMapSystem>();
|
||||
var xformSystem = _entManager.System<SharedTransformSystem>();
|
||||
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var tileSize = grid.Comp.TileSize;
|
||||
var tileDimensions = new Vector2(tileSize, tileSize);
|
||||
var (_, _, worldMatrix, invMatrix) = xformSystem.GetWorldPositionRotationMatrixWithInv(grid.Owner);
|
||||
args.WorldHandle.SetTransform(worldMatrix);
|
||||
var localAABB = invMatrix.TransformBox(args.WorldBounds);
|
||||
var xform = xformQuery.GetComponent(grid);
|
||||
args.WorldHandle.SetTransform(xform.WorldMatrix);
|
||||
|
||||
var enumerator = mapSystem.GetLocalTilesEnumerator(grid.Owner, grid.Comp, localAABB, false);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
foreach (var tileRef in grid.Comp.GetTilesIntersecting(args.WorldBounds, false))
|
||||
{
|
||||
var tileDef = _tileDefManager[tileRef.Tile.TypeId];
|
||||
|
||||
@@ -71,7 +66,7 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
continue;
|
||||
|
||||
var neighborIndices = new Vector2i(tileRef.GridIndices.X + x, tileRef.GridIndices.Y + y);
|
||||
var neighborTile = mapSystem.GetTileRef(grid.Owner, grid.Comp, neighborIndices);
|
||||
var neighborTile = grid.Comp.GetTileRef(neighborIndices);
|
||||
var neighborDef = _tileDefManager[neighborTile.Tile.TypeId];
|
||||
|
||||
// If it's the same tile then no edge to be drawn.
|
||||
@@ -123,9 +118,9 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
}
|
||||
|
||||
if (angle == Angle.Zero)
|
||||
args.WorldHandle.DrawTextureRect(texture.Texture, box);
|
||||
args.WorldHandle.DrawTextureRect(texture, box);
|
||||
else
|
||||
args.WorldHandle.DrawTextureRect(texture.Texture, new Box2Rotated(box, angle, box.Center));
|
||||
args.WorldHandle.DrawTextureRect(texture, new Box2Rotated(box, angle, box.Center));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -7,7 +8,6 @@ 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(closestEntity, out SpriteComponent? component) || component.BaseRSI == null)
|
||||
if (!pManager.EntityManager.TryGetComponent<SpriteComponent?>(closestEntity, out var component) || component.BaseRSI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,22 +15,15 @@ public interface IPlayerManager : ISharedPlayerManager
|
||||
event Action? PlayerListUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets attached to a new entity, or when the local
|
||||
/// session gets updated. See also <see cref="LocalPlayerAttachedEvent"/>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets attached to a new entity. See also <see cref="LocalPlayerAttachedEvent"/>
|
||||
/// </summary>
|
||||
event Action<EntityUid>? LocalPlayerAttached;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets detached from an entity, or when the local
|
||||
/// session gets updated. See also <see cref="LocalPlayerDetachedEvent"/>
|
||||
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets detached from new entity. 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>
|
||||
@@ -45,8 +38,34 @@ 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,13 +42,12 @@ namespace Robust.Client.Player
|
||||
/// <inheritdoc />
|
||||
public override int MaxPlayers => _client.GameInfo?.ServerMaxPlayers ?? -1;
|
||||
|
||||
public LocalPlayer? LocalPlayer { get; private set; }
|
||||
public LocalPlayer? LocalPlayer { get; 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)
|
||||
@@ -65,14 +64,22 @@ 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?");
|
||||
|
||||
var session = CreateAndAddSession(default, name);
|
||||
session.ClientSide = true;
|
||||
SetLocalSession(session);
|
||||
LocalSession = CreateAndAddSession(default, name);
|
||||
Startup();
|
||||
PlayerListUpdated?.Invoke();
|
||||
}
|
||||
@@ -82,44 +89,18 @@ namespace Robust.Client.Player
|
||||
if (LocalSession != null)
|
||||
throw new InvalidOperationException($"Player manager already running?");
|
||||
|
||||
SetLocalSession(CreateAndAddSession(channel));
|
||||
var session = CreateAndAddSession(channel.UserId, channel.UserName);
|
||||
session.Channel = channel;
|
||||
LocalSession = session;
|
||||
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()
|
||||
{
|
||||
SetAttachedEntity(LocalSession, null, out _);
|
||||
if (LocalSession != null)
|
||||
SetAttachedEntity(LocalSession, null);
|
||||
LocalPlayer = null;
|
||||
LocalSession = null;
|
||||
_pendingStates.Clear();
|
||||
@@ -127,21 +108,16 @@ namespace Robust.Client.Player
|
||||
PlayerListUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public override bool SetAttachedEntity(ICommonSession? session, EntityUid? uid, out ICommonSession? kicked, bool force = false)
|
||||
public override void SetAttachedEntity(ICommonSession session, EntityUid? uid)
|
||||
{
|
||||
kicked = null;
|
||||
if (session == null)
|
||||
return false;
|
||||
|
||||
if (session.AttachedEntity == uid)
|
||||
return true;
|
||||
return;
|
||||
|
||||
var old = session.AttachedEntity;
|
||||
if (!base.SetAttachedEntity(session, uid, out kicked, force))
|
||||
return false;
|
||||
base.SetAttachedEntity(session, uid);
|
||||
|
||||
if (session != LocalSession)
|
||||
return true;
|
||||
return;
|
||||
|
||||
if (old.HasValue)
|
||||
{
|
||||
@@ -153,13 +129,13 @@ namespace Robust.Client.Player
|
||||
if (uid == null)
|
||||
{
|
||||
Sawmill.Info($"Local player is no longer attached to any entity.");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntManager.EntityExists(uid))
|
||||
{
|
||||
Sawmill.Error($"Attempted to attach player to non-existent entity {uid}!");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntManager.EnsureComponent(uid.Value, out EyeComponent eye))
|
||||
@@ -172,7 +148,6 @@ 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)
|
||||
@@ -218,7 +193,7 @@ namespace Robust.Client.Player
|
||||
_pendingStates.Remove(state.UserId);
|
||||
}
|
||||
|
||||
SetAttachedEntity(LocalSession, uid, out _, true);
|
||||
SetAttachedEntity(LocalSession, uid);
|
||||
SetStatus(LocalSession, state.Status);
|
||||
}
|
||||
|
||||
@@ -258,10 +233,11 @@ namespace Robust.Client.Player
|
||||
{
|
||||
// This is a new userid, so we create a new session.
|
||||
DebugTools.Assert(state.UserId != LocalPlayer?.UserId);
|
||||
var newSession = (CommonSession) CreateAndAddSession(state.UserId, state.Name);
|
||||
var newSession = CreateAndAddSession(state.UserId, state.Name);
|
||||
newSession.Ping = state.Ping;
|
||||
newSession.Name = state.Name;
|
||||
SetStatus(newSession, state.Status);
|
||||
SetAttachedEntity(newSession, controlled, out _, true);
|
||||
SetAttachedEntity(newSession, controlled);
|
||||
dirty = true;
|
||||
continue;
|
||||
}
|
||||
@@ -280,7 +256,7 @@ namespace Robust.Client.Player
|
||||
local.Name = state.Name;
|
||||
local.Ping = state.Ping;
|
||||
SetStatus(local, state.Status);
|
||||
SetAttachedEntity(local, controlled, out _, true);
|
||||
SetAttachedEntity(local, controlled);
|
||||
}
|
||||
|
||||
// Remove old users. This only works if the provided state is a list of all players
|
||||
@@ -288,12 +264,10 @@ 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),
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
@@ -129,11 +128,6 @@ public interface IReplayPlaybackManager
|
||||
/// </summary>
|
||||
event Action? ReplayUnpaused;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked just before a replay applies a game state.
|
||||
/// </summary>
|
||||
event Action<(GameState Current, GameState? Next)>? BeforeApplyState;
|
||||
|
||||
/// <summary>
|
||||
/// If currently replaying a client-side recording, this is the user that recorded the replay.
|
||||
/// Useful for setting default observer spawn positions.
|
||||
@@ -143,5 +137,5 @@ public interface IReplayPlaybackManager
|
||||
/// <summary>
|
||||
/// Fetches the entity that the <see cref="Recorder"/> is currently attached to.
|
||||
/// </summary>
|
||||
bool TryGetRecorderEntity([NotNullWhen(true)] out EntityUid? uid);
|
||||
public bool TryGetRecorderEntity([NotNullWhen(true)] out EntityUid? uid);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,6 @@ internal sealed partial class ReplayPlaybackManager
|
||||
_gameState.ClearDetachQueue();
|
||||
EnsureDetachedExist(checkpoint);
|
||||
_gameState.DetachImmediate(checkpoint.Detached);
|
||||
BeforeApplyState?.Invoke((checkpoint.State, next));
|
||||
_gameState.ApplyGameState(checkpoint.State, next);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -59,13 +58,7 @@ internal sealed partial class ReplayPlaybackManager
|
||||
|
||||
_timing.LastRealTick = _timing.LastProcessedTick = _timing.CurTick = Replay.CurTick;
|
||||
_gameState.UpdateFullRep(state, cloneDelta: true);
|
||||
|
||||
// Clear existing lerps
|
||||
_entMan.EntitySysManager.GetEntitySystem<TransformSystem>().Reset();
|
||||
|
||||
var next = Replay.NextState;
|
||||
BeforeApplyState?.Invoke((state, next));
|
||||
_gameState.ApplyGameState(state, next);
|
||||
_gameState.ApplyGameState(state, Replay.NextState);
|
||||
ProcessMessages(Replay.CurMessages, skipEffectEvents);
|
||||
|
||||
// TODO REPLAYS block audio
|
||||
|
||||
@@ -42,9 +42,7 @@ internal sealed partial class ReplayPlaybackManager
|
||||
{
|
||||
var state = Replay.CurState;
|
||||
_gameState.UpdateFullRep(state, cloneDelta: true);
|
||||
var next = Replay.NextState;
|
||||
BeforeApplyState?.Invoke((state, next));
|
||||
_gameState.ApplyGameState(state, next);
|
||||
_gameState.ApplyGameState(state, Replay.NextState);
|
||||
DebugTools.Assert(Replay.LastApplied >= state.FromSequence);
|
||||
DebugTools.Assert(Replay.LastApplied + 1 <= state.ToSequence);
|
||||
Replay.LastApplied = state.ToSequence;
|
||||
|
||||
@@ -12,7 +12,6 @@ using Robust.Client.Upload;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
@@ -45,7 +44,6 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
|
||||
public event Action? ReplayPlaybackStopped;
|
||||
public event Action? ReplayPaused;
|
||||
public event Action? ReplayUnpaused;
|
||||
public event Action<(GameState Current, GameState? Next)>? BeforeApplyState;
|
||||
|
||||
public ReplayData? Replay { get; private set; }
|
||||
public NetUserId? Recorder => Replay?.Recorder;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.9" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />
|
||||
@@ -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.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.1.0" />
|
||||
<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" />
|
||||
|
||||
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public RichTextLabel()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
VerticalAlignment = VAlignment.Center;
|
||||
}
|
||||
|
||||
public void SetMessage(FormattedMessage message, Type[]? tagsAllowed = null, Color? defaultColor = null)
|
||||
|
||||
@@ -70,7 +70,6 @@ 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))
|
||||
{
|
||||
@@ -81,7 +80,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, xformSystem), Tile.Empty);
|
||||
tile = new TileRef(EntityUid.Invalid, mouseGridPos.ToVector2i(_entityManager, _mapManager), Tile.Empty);
|
||||
}
|
||||
|
||||
var controlHovered = UserInterfaceManager.CurrentlyHovered;
|
||||
@@ -91,35 +90,35 @@ Screen Size: {screenSize} (scale: {screenScale})
|
||||
Mouse Pos:
|
||||
Screen: {mouseScreenPos}
|
||||
{mouseWorldMap}
|
||||
{_entityManager.GetNetCoordinates(mouseGridPos)}
|
||||
{mouseGridPos}
|
||||
{tile}
|
||||
GUI: {controlHovered}");
|
||||
|
||||
_textBuilder.AppendLine("\nAttached NetEntity:");
|
||||
var controlledEntity = _playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid;
|
||||
|
||||
_textBuilder.AppendLine("\nAttached Entity:");
|
||||
var controlledEntity = _playerManager?.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (controlledEntity == EntityUid.Invalid)
|
||||
{
|
||||
_textBuilder.AppendLine("No attached netentity.");
|
||||
_textBuilder.AppendLine("No attached entity.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var entityTransform = _entityManager.GetComponent<TransformComponent>(controlledEntity);
|
||||
var playerWorldOffset = xformSystem.GetMapCoordinates(entityTransform);
|
||||
var playerWorldOffset = entityTransform.MapPosition;
|
||||
var playerScreen = _eyeManager.WorldToScreen(playerWorldOffset.Position);
|
||||
|
||||
var playerCoordinates = entityTransform.Coordinates;
|
||||
var playerRotation = xformSystem.GetWorldRotation(entityTransform);
|
||||
var playerRotation = entityTransform.WorldRotation;
|
||||
var gridRotation = entityTransform.GridUid != null
|
||||
? xformSystem.GetWorldRotation(entityTransform.GridUid.Value)
|
||||
? _entityManager.GetComponent<TransformComponent>(entityTransform.GridUid.Value)
|
||||
.WorldRotation
|
||||
: Angle.Zero;
|
||||
|
||||
_textBuilder.Append($@" Screen: {playerScreen}
|
||||
{playerWorldOffset}
|
||||
{_entityManager.GetNetCoordinates(playerCoordinates)}
|
||||
{playerCoordinates}
|
||||
Rotation: {playerRotation.Degrees:F2}°
|
||||
NEntId: {_entityManager.GetNetEntity(controlledEntity)}
|
||||
Grid NEntId: {_entityManager.GetNetEntity(entityTransform.GridUid)}
|
||||
EntId: {controlledEntity}
|
||||
GridUid: {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, "-1");
|
||||
var gridVal = EntityUid.Parse(gridId.Text);
|
||||
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, ", -1"));
|
||||
ValueChanged(EntityUid.Parse(e.Text));
|
||||
}
|
||||
|
||||
var vvButton = new Button()
|
||||
|
||||
@@ -61,7 +61,7 @@ internal sealed class AssetPassPackRsis : AssetPass
|
||||
foreach (var (key, dat) in _foundRsis)
|
||||
{
|
||||
if (dat.MetaJson == null)
|
||||
continue;
|
||||
return;
|
||||
|
||||
RunJob(() =>
|
||||
{
|
||||
|
||||
@@ -21,11 +21,11 @@ public sealed class RobustClientAssetGraph
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<AssetPass> AllPasses { get; }
|
||||
|
||||
public RobustClientAssetGraph(bool parallel = true)
|
||||
public RobustClientAssetGraph()
|
||||
{
|
||||
// The code injecting the list of source files is assumed to be pretty single-threaded.
|
||||
// We use a parallelizing input to break out all the work on files coming in onto multiple threads.
|
||||
Input = new AssetPassPipe { Name = "RobustClientAssetGraphInput", Parallelize = parallel };
|
||||
Input = new AssetPassPipe { Name = "RobustClientAssetGraphInput", Parallelize = true };
|
||||
PresetPasses = new AssetPassPipe { Name = "RobustClientAssetGraphPresetPasses" };
|
||||
Output = new AssetPassPipe { Name = "RobustClientAssetGraphOutput", CheckDuplicates = true };
|
||||
NormalizeText = new AssetPassNormalizeText { Name = "RobustClientAssetGraphNormalizeText" };
|
||||
|
||||
@@ -4,10 +4,9 @@ namespace Robust.Packaging;
|
||||
|
||||
public sealed class RobustClientPackaging
|
||||
{
|
||||
public static IReadOnlySet<string> ClientIgnoredResources { get; } = new HashSet<string>
|
||||
public static IReadOnlySet<string> ClientIgnoresResources { get; } = new HashSet<string>
|
||||
{
|
||||
"Maps",
|
||||
"ConfigPresets",
|
||||
// Leaving this here for future archaeologists to ponder at.
|
||||
"emotes.xml",
|
||||
"Groups",
|
||||
@@ -19,8 +18,48 @@ public sealed class RobustClientPackaging
|
||||
AssetPass pass,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
var ignoreSet = ClientIgnoredResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet();
|
||||
var ignoreSet = ClientIgnoresResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet();
|
||||
|
||||
await RobustSharedPackaging.DoResourceCopy(Path.Combine(contentDir, "Resources"), pass, ignoreSet, cancel: cancel);
|
||||
await RobustSharedPackaging.DoResourceCopy(Path.Combine(contentDir, "Resources"), pass, ignoreSet, cancel);
|
||||
}
|
||||
|
||||
public static async Task WriteContentAssemblies(
|
||||
AssetPass pass,
|
||||
string contentDir,
|
||||
string binDir,
|
||||
IEnumerable<string> contentAssemblies,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
await WriteContentAssemblies("Assemblies", pass, contentDir, binDir, contentAssemblies, cancel);
|
||||
}
|
||||
|
||||
public static Task WriteContentAssemblies(
|
||||
string target,
|
||||
AssetPass pass,
|
||||
string contentDir,
|
||||
string binDir,
|
||||
IEnumerable<string> contentAssemblies,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
var files = new List<string>();
|
||||
|
||||
var sourceDir = Path.Combine(contentDir, "bin", binDir);
|
||||
|
||||
foreach (var asm in contentAssemblies)
|
||||
{
|
||||
files.Add($"{asm}.dll");
|
||||
|
||||
var pdbPath = $"{asm}.pdb";
|
||||
if (File.Exists(Path.Combine(sourceDir, pdbPath)))
|
||||
files.Add(pdbPath);
|
||||
}
|
||||
|
||||
foreach (var f in files)
|
||||
{
|
||||
cancel.ThrowIfCancellationRequested();
|
||||
pass.InjectFileFromDisk($"{target}/{f}", Path.Combine(sourceDir, f));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using Robust.Packaging.AssetProcessing;
|
||||
|
||||
namespace Robust.Packaging;
|
||||
|
||||
public sealed class RobustServerPackaging
|
||||
{
|
||||
public static IReadOnlySet<string> ServerIgnoresResources { get; } = new HashSet<string>
|
||||
{
|
||||
"Audio",
|
||||
"Textures",
|
||||
"Fonts",
|
||||
"Shaders",
|
||||
};
|
||||
|
||||
public static async Task WriteServerResources(
|
||||
string contentDir,
|
||||
AssetPass pass,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
var ignoreSet = ServerIgnoresResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet();
|
||||
|
||||
await RobustSharedPackaging.DoResourceCopy(Path.Combine(contentDir, "Resources"),
|
||||
pass,
|
||||
ignoreSet,
|
||||
"Resources",
|
||||
cancel);
|
||||
await RobustSharedPackaging.DoResourceCopy(Path.Combine("RobustToolbox", "Resources"),
|
||||
pass,
|
||||
ignoreSet,
|
||||
"Resources",
|
||||
cancel);
|
||||
}
|
||||
}
|
||||
@@ -15,53 +15,10 @@ public sealed class RobustSharedPackaging
|
||||
".DS_Store"
|
||||
};
|
||||
|
||||
// IDK what these are supposed to correspond to but targetDir is the target directory.
|
||||
public static async Task WriteContentAssemblies(
|
||||
AssetPass pass,
|
||||
string contentDir,
|
||||
string binDir,
|
||||
IEnumerable<string> contentAssemblies,
|
||||
string targetDir = "Assemblies",
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
await WriteContentAssemblies(targetDir, pass, contentDir, binDir, contentAssemblies, cancel);
|
||||
}
|
||||
|
||||
public static Task WriteContentAssemblies(
|
||||
string target,
|
||||
AssetPass pass,
|
||||
string contentDir,
|
||||
string binDir,
|
||||
IEnumerable<string> contentAssemblies,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
var files = new List<string>();
|
||||
|
||||
var sourceDir = Path.Combine(contentDir, "bin", binDir);
|
||||
|
||||
foreach (var asm in contentAssemblies)
|
||||
{
|
||||
files.Add($"{asm}.dll");
|
||||
|
||||
var pdbPath = $"{asm}.pdb";
|
||||
if (File.Exists(Path.Combine(sourceDir, pdbPath)))
|
||||
files.Add(pdbPath);
|
||||
}
|
||||
|
||||
foreach (var f in files)
|
||||
{
|
||||
cancel.ThrowIfCancellationRequested();
|
||||
pass.InjectFileFromDisk($"{target}/{f}", Path.Combine(sourceDir, f));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static Task DoResourceCopy(
|
||||
string diskSource,
|
||||
AssetPass pass,
|
||||
IReadOnlySet<string> ignoreSet,
|
||||
string targetDir = "",
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
foreach (var path in Directory.EnumerateFileSystemEntries(diskSource))
|
||||
@@ -72,7 +29,7 @@ public sealed class RobustSharedPackaging
|
||||
if (ignoreSet.Contains(filename))
|
||||
continue;
|
||||
|
||||
var targetPath = Path.Combine(targetDir, filename);
|
||||
var targetPath = filename;
|
||||
if (Directory.Exists(path))
|
||||
CopyDirIntoZip(path, targetPath, pass);
|
||||
else
|
||||
@@ -87,10 +44,10 @@ public sealed class RobustSharedPackaging
|
||||
foreach (var file in Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
var relPath = Path.GetRelativePath(directory, file);
|
||||
var zipPath = $"{basePath}/{relPath}";
|
||||
|
||||
if (Path.DirectorySeparatorChar != '/')
|
||||
zipPath = zipPath.Replace(Path.DirectorySeparatorChar, '/');
|
||||
relPath = relPath.Replace(Path.DirectorySeparatorChar, '/');
|
||||
|
||||
var zipPath = $"{basePath}/{relPath}";
|
||||
|
||||
// Console.WriteLine($"{directory}/{zipPath} -> /{zipPath}");
|
||||
pass.InjectFileFromDisk(zipPath, file);
|
||||
|
||||
@@ -660,14 +660,10 @@ namespace Robust.Server
|
||||
{
|
||||
// Write down exception log
|
||||
var logPath = _config.GetCVar(CVars.LogPath);
|
||||
if (!Path.IsPathRooted(logPath))
|
||||
{
|
||||
logPath = PathHelpers.ExecutableRelativeFile(logPath);
|
||||
}
|
||||
|
||||
var pathToWrite = Path.Combine(logPath,
|
||||
var relPath = PathHelpers.ExecutableRelativeFile(logPath);
|
||||
Directory.CreateDirectory(relPath);
|
||||
var pathToWrite = Path.Combine(relPath,
|
||||
"Runtime-" + DateTime.Now.ToString("yyyy-MM-dd-THH-mm-ss") + ".txt");
|
||||
Directory.CreateDirectory(logPath);
|
||||
File.WriteAllText(pathToWrite, _runtimeLog.Display(), EncodingHelpers.UTF8);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
GC.Collect();
|
||||
|
||||
Span<EntityUid> ents = stackalloc EntityUid[amount];
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
@@ -51,17 +50,12 @@ public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
ents[i] = _entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
|
||||
_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
|
||||
|
||||
189
Robust.Server/GameObjects/EntitySystems/ActorSystem.cs
Normal file
189
Robust.Server/GameObjects/EntitySystems/ActorSystem.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
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,4 +1,5 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ 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;
|
||||
@@ -157,11 +154,10 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
result = Deserialize(data);
|
||||
_logLoader.Debug($"Loaded map in {sw.Elapsed}");
|
||||
|
||||
_logLoader.Info($"Loaded map {resPath} in {sw.Elapsed}");
|
||||
var xformQuery = _serverEntityManager.GetEntityQuery<TransformComponent>();
|
||||
var mapEnt = _mapManager.GetMapEntityId(mapId);
|
||||
|
||||
var xformQuery = _serverEntityManager.GetEntityQuery<TransformComponent>();
|
||||
var rootEnts = new List<EntityUid>();
|
||||
// aeoeoeieioe content
|
||||
|
||||
@@ -178,8 +174,6 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
// EntityManager.CleanupArch();
|
||||
|
||||
rootUids = rootEnts;
|
||||
}
|
||||
|
||||
@@ -296,9 +290,6 @@ 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);
|
||||
|
||||
@@ -314,25 +305,6 @@ 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();
|
||||
@@ -434,10 +406,6 @@ 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>())
|
||||
{
|
||||
@@ -457,30 +425,9 @@ 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
_prototypeManager.TryIndex(type, out proto);
|
||||
|
||||
foreach (var entityDef in entities.Cast<MappingDataNode>())
|
||||
{
|
||||
@@ -495,7 +442,6 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
{
|
||||
deletedPrototypeUids.Add(entity);
|
||||
}
|
||||
// TODO: Move this elsewhere?
|
||||
else if (data.Options.StoreMapUids)
|
||||
{
|
||||
var comp = _serverEntityManager.AddComponent<MapSaveIdComponent>(entity);
|
||||
@@ -604,7 +550,6 @@ 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,6 +24,11 @@ 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;
|
||||
@@ -61,8 +66,9 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private void HandleGridEmpty(EntityUid uid, MapGridComponent component, EmptyGridEvent args)
|
||||
{
|
||||
if (!_deleteEmptyGrids || TerminatingOrDeleted(uid) || HasComp<MapComponent>(uid))
|
||||
return;
|
||||
if (!_deleteEmptyGrids) return;
|
||||
if (!EntityManager.EntityExists(uid)) return;
|
||||
if (EntityManager.GetComponent<MetaDataComponent>(uid).EntityLifeStage >= EntityLifeStage.Terminating) return;
|
||||
|
||||
MapManager.DeleteGrid(args.GridId);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
@@ -20,6 +21,8 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private readonly List<ICommonSession> _sessionCache = new();
|
||||
|
||||
private readonly Dictionary<ICommonSession, List<PlayerBoundUserInterface>> _openInterfaces = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -43,7 +46,7 @@ namespace Robust.Server.GameObjects
|
||||
if (args.NewStatus != SessionStatus.Disconnected)
|
||||
return;
|
||||
|
||||
if (!OpenInterfaces.TryGetValue(args.Session, out var buis))
|
||||
if (!_openInterfaces.TryGetValue(args.Session, out var buis))
|
||||
return;
|
||||
|
||||
foreach (var bui in buis.ToArray())
|
||||
@@ -89,7 +92,7 @@ namespace Robust.Server.GameObjects
|
||||
/// <summary>
|
||||
/// Verify that the subscribed clients are still in range of the interface.
|
||||
/// </summary>
|
||||
private void CheckRange(EntityUid uid, ActiveUserInterfaceComponent activeUis, PlayerBoundUserInterface ui, TransformComponent transform, EntityQuery<TransformComponent> query)
|
||||
private void CheckRange(EntityUid uid, Shared.GameObjects.ActiveUserInterfaceComponent activeUis, PlayerBoundUserInterface ui, TransformComponent transform, EntityQuery<TransformComponent> query)
|
||||
{
|
||||
if (ui.InteractionRange <= 0)
|
||||
return;
|
||||
@@ -136,6 +139,11 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivateInterface(PlayerBoundUserInterface ui)
|
||||
{
|
||||
EnsureComp<ActiveUserInterfaceComponent>(ui.Owner).Interfaces.Add(ui);
|
||||
}
|
||||
|
||||
#region Get BUI
|
||||
|
||||
public bool HasUi(EntityUid uid, Enum uiKey, UserInterfaceComponent? ui = null)
|
||||
@@ -160,14 +168,20 @@ namespace Robust.Server.GameObjects
|
||||
? bui
|
||||
: null;
|
||||
}
|
||||
public bool TryGetUi(EntityUid uid, Enum uiKey, [NotNullWhen(true)] out PlayerBoundUserInterface? bui, UserInterfaceComponent? ui = null)
|
||||
{
|
||||
bui = null;
|
||||
|
||||
return Resolve(uid, ref ui, false) && ui.Interfaces.TryGetValue(uiKey, out bui);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return UIs a session has open.
|
||||
/// Null if empty.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
public List<PlayerBoundUserInterface>? GetAllUIsForSession(ICommonSession session)
|
||||
{
|
||||
OpenInterfaces.TryGetValue(session, out var value);
|
||||
_openInterfaces.TryGetValue(session, out var value);
|
||||
return value;
|
||||
}
|
||||
#endregion
|
||||
@@ -245,14 +259,94 @@ namespace Robust.Server.GameObjects
|
||||
bui.StateDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switches between closed and open for a specific client.
|
||||
/// </summary>
|
||||
public bool TryToggleUi(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null)
|
||||
{
|
||||
if (!TryGetUi(uid, uiKey, out var bui, ui))
|
||||
return false;
|
||||
|
||||
ToggleUi(bui, session);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switches between closed and open for a specific client.
|
||||
/// </summary>
|
||||
public void ToggleUi(PlayerBoundUserInterface bui, ICommonSession session)
|
||||
{
|
||||
if (bui._subscribedSessions.Contains(session))
|
||||
CloseUi(bui, session);
|
||||
else
|
||||
OpenUi(bui, session);
|
||||
}
|
||||
|
||||
#region Open
|
||||
|
||||
public bool TryOpen(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null)
|
||||
{
|
||||
if (!TryGetUi(uid, uiKey, out var bui, ui))
|
||||
return false;
|
||||
|
||||
return OpenUi(bui, session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens this interface for a specific client.
|
||||
/// </summary>
|
||||
public bool OpenUi(PlayerBoundUserInterface bui, ICommonSession session)
|
||||
{
|
||||
if (session.Status == SessionStatus.Connecting || session.Status == SessionStatus.Disconnected)
|
||||
return false;
|
||||
|
||||
if (!bui._subscribedSessions.Add(session))
|
||||
return false;
|
||||
|
||||
_openInterfaces.GetOrNew(session).Add(bui);
|
||||
RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session));
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
|
||||
// Fun fact, clients needs to have BUIs open before they can receive the state.....
|
||||
if (bui.LastStateMsg != null)
|
||||
RaiseNetworkEvent(bui.LastStateMsg, session.ConnectedClient);
|
||||
|
||||
ActivateInterface(bui);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Close
|
||||
public bool TryClose(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null)
|
||||
{
|
||||
if (!TryGetUi(uid, uiKey, out var bui, ui))
|
||||
return false;
|
||||
|
||||
return CloseUi(bui, session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close this interface for a specific client.
|
||||
/// </summary>
|
||||
public bool CloseUi(PlayerBoundUserInterface bui, ICommonSession session, ActiveUserInterfaceComponent? activeUis = null)
|
||||
{
|
||||
if (!bui._subscribedSessions.Remove(session))
|
||||
return false;
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
CloseShared(bui, session, activeUis);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void CloseShared(PlayerBoundUserInterface bui, ICommonSession session, ActiveUserInterfaceComponent? activeUis = null)
|
||||
{
|
||||
var owner = bui.Owner;
|
||||
bui._subscribedSessions.Remove(session);
|
||||
bui.PlayerStateOverrides.Remove(session);
|
||||
|
||||
if (OpenInterfaces.TryGetValue(session, out var buis))
|
||||
if (_openInterfaces.TryGetValue(session, out var buis))
|
||||
buis.Remove(bui);
|
||||
|
||||
RaiseLocalEvent(owner, new BoundUIClosedEvent(bui.UiKey, owner, session));
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Robust.Server.GameObjects
|
||||
/// Manager for entities -- controls things like template loading and instantiation
|
||||
/// </summary>
|
||||
[UsedImplicitly] // DI Container
|
||||
public sealed partial class ServerEntityManager : EntityManager, IServerEntityManagerInternal
|
||||
public sealed 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 _, out _);
|
||||
return AllocEntity(prototype, 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, out TransformComponent xform, IEntityLoadContext? context = null)
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, out metadata, out xform, context);
|
||||
return base.CreateEntity(prototypeName, out metadata, 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, out xform, context);
|
||||
var entity = base.CreateEntity(prototype, out metadata, 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(EntityCount);
|
||||
EntitiesCount.Set(Entities.Count);
|
||||
}
|
||||
|
||||
public uint GetLastMessageSequence(ICommonSession session)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -272,7 +271,6 @@ 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);
|
||||
}
|
||||
@@ -435,15 +433,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
#endregion
|
||||
|
||||
public List<(int, IChunkIndexLocation)> GetChunks(
|
||||
ICommonSession[] sessions,
|
||||
ref HashSet<int>[] playerChunks,
|
||||
ref EntityUid[][] viewerEntities)
|
||||
public (List<(int, IChunkIndexLocation)> , HashSet<int>[], EntityUid[][] viewers) GetChunks(ICommonSession[] sessions)
|
||||
{
|
||||
// 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);
|
||||
var playerChunks = new HashSet<int>[sessions.Length];
|
||||
var viewerEntities = new EntityUid[sessions.Length][];
|
||||
|
||||
_chunkList.Clear();
|
||||
// Keep track of the index of each chunk we use for a faster index lookup.
|
||||
@@ -466,8 +459,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var session = sessions[i];
|
||||
playerChunks[i] = _playerChunkPool.Get();
|
||||
|
||||
ref var viewers = ref viewerEntities[i];
|
||||
GetSessionViewers(session, ref viewers);
|
||||
var viewers = GetSessionViewers(session);
|
||||
viewerEntities[i] = viewers;
|
||||
|
||||
for (var j = 0; j < viewers.Length; j++)
|
||||
{
|
||||
@@ -559,7 +552,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
return _chunkList;
|
||||
return (_chunkList, playerChunks, viewerEntities);
|
||||
}
|
||||
|
||||
public void RegisterNewPreviousChunkTrees(
|
||||
@@ -576,20 +569,23 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
_reusedTrees.Add(chunks[i]);
|
||||
}
|
||||
|
||||
foreach (var (index, chunk) in _previousTrees)
|
||||
var previousIndices = _previousTrees.Keys.ToArray();
|
||||
for (var i = 0; i < previousIndices.Length; i++)
|
||||
{
|
||||
var index = previousIndices[i];
|
||||
// ReSharper disable once InconsistentlySynchronizedField
|
||||
if (_reusedTrees.Contains(index))
|
||||
continue;
|
||||
|
||||
if (chunk != null)
|
||||
if (_reusedTrees.Contains(index)) continue;
|
||||
var chunk = _previousTrees[index];
|
||||
if (chunk.HasValue)
|
||||
{
|
||||
_chunkCachePool.Return(chunk.Value.metadata);
|
||||
_treePool.Return(chunk.Value.tree);
|
||||
}
|
||||
|
||||
if (!chunks.Contains(index))
|
||||
{
|
||||
_previousTrees.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
_previousTrees.EnsureCapacity(chunks.Count);
|
||||
@@ -892,7 +888,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
return null;
|
||||
|
||||
var tick = _gameTiming.CurTick;
|
||||
var minSize = Math.Max(0, lastSent.Count - visibleEnts.Count);
|
||||
var minSize = Math.Max(0, lastSent.Count - lastSent.Count);
|
||||
var leftView = new List<NetEntity>(minSize);
|
||||
|
||||
foreach (var netEntity in lastSent.Keys)
|
||||
@@ -1317,44 +1313,26 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
return entState;
|
||||
}
|
||||
|
||||
private void GetSessionViewers(ICommonSession session, [NotNull] ref EntityUid[]? viewers)
|
||||
private EntityUid[] GetSessionViewers(ICommonSession session)
|
||||
{
|
||||
if (session.Status != SessionStatus.InGame)
|
||||
{
|
||||
viewers = Array.Empty<EntityUid>();
|
||||
return;
|
||||
}
|
||||
return Array.Empty<EntityUid>();
|
||||
|
||||
// Fast path
|
||||
if (session.ViewSubscriptions.Count == 0)
|
||||
{
|
||||
if (session.AttachedEntity == null)
|
||||
{
|
||||
viewers = Array.Empty<EntityUid>();
|
||||
return;
|
||||
}
|
||||
return Array.Empty<EntityUid>();
|
||||
|
||||
Array.Resize(ref viewers, 1);
|
||||
viewers[0] = session.AttachedEntity.Value;
|
||||
return;
|
||||
return new[] { 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);
|
||||
}
|
||||
var viewers = new HashSet<EntityUid>();
|
||||
if (session.AttachedEntity != null)
|
||||
viewers.Add(session.AttachedEntity.Value);
|
||||
|
||||
foreach (var ent in session.ViewSubscriptions)
|
||||
{
|
||||
viewers[i++] = ent;
|
||||
}
|
||||
viewers.UnionWith(session.ViewSubscriptions);
|
||||
return viewers.ToArray();
|
||||
}
|
||||
|
||||
// Read Safe
|
||||
|
||||
@@ -37,9 +37,6 @@ 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!;
|
||||
@@ -270,7 +267,7 @@ Oldest acked clients: {string.Join(", ", players)}
|
||||
|
||||
private PvsData? GetPVSData(ICommonSession[] players)
|
||||
{
|
||||
var chunks= _pvs.GetChunks(players, ref _playerChunks, ref _viewerEntities);
|
||||
var (chunks, playerChunks, viewerEntities) = _pvs.GetChunks(players);
|
||||
const int ChunkBatchSize = 2;
|
||||
var chunksCount = chunks.Count;
|
||||
var chunkBatches = (int)MathF.Ceiling((float)chunksCount / ChunkBatchSize);
|
||||
@@ -313,8 +310,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,
|
||||
};
|
||||
}
|
||||
@@ -381,10 +378,15 @@ Oldest acked clients: {string.Join(", ", players)}
|
||||
if (_gameTiming.CurTick.Value > lastAck.Value + _pvs.ForceAckThreshold)
|
||||
{
|
||||
stateUpdateMessage.ForceSendReliably = true;
|
||||
|
||||
// Aside from the time shortly after connecting, this shouldn't be common. If it is happening.
|
||||
// something is probably wrong (or we have a malicious client). Hence we log an error.
|
||||
// If it is more frequent than I think, this can be downgraded to a warning.
|
||||
|
||||
#if FULL_RELEASE
|
||||
var connectedTime = (DateTime.UtcNow - session.ConnectedTime).TotalMinutes;
|
||||
if (lastAck > GameTick.Zero && connectedTime > 1)
|
||||
_logger.Warning($"Client {session} exceeded ack-tick threshold. Last ack: {lastAck}. Cur tick: {_gameTiming.CurTick}. Connect time: {connectedTime} minutes");
|
||||
_logger.Error($"Client {session} exceeded ack-tick threshold. Last ack: {lastAck}. Cur tick: {_gameTiming.CurTick}. Connect time: {connectedTime} minutes");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ 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;
|
||||
|
||||
@@ -94,7 +93,6 @@ 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);
|
||||
|
||||
@@ -31,10 +31,6 @@ namespace Robust.Server.Placement
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private EntityLookupSystem _lookup => _entityManager.System<EntityLookupSystem>();
|
||||
private SharedMapSystem _maps => _entityManager.System<SharedMapSystem>();
|
||||
private SharedTransformSystem _xformSystem => _entityManager.System<SharedTransformSystem>();
|
||||
|
||||
//TO-DO: Expand for multiple permission per mob?
|
||||
// Add support for multi-use placeables (tiles etc.).
|
||||
public List<PlacementInformation> BuildPermissions { get; set; } = new();
|
||||
@@ -49,7 +45,6 @@ namespace Robust.Server.Placement
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
// Someday PlacementManagerSystem my beloved.
|
||||
_sawmill = _logManager.GetSawmill("placement");
|
||||
|
||||
_networkManager.RegisterNetMessage<MsgPlacement>(HandleNetMessage);
|
||||
@@ -148,7 +143,7 @@ namespace Robust.Server.Placement
|
||||
if (_entityManager.TryGetComponent<MapGridComponent>(gridUid, out var grid))
|
||||
{
|
||||
var replacementQuery = _entityManager.GetEntityQuery<PlacementReplacementComponent>();
|
||||
var anc = _maps.GetAnchoredEntitiesEnumerator(gridUid.Value, grid, _maps.LocalToTile(gridUid.Value, grid, coordinates));
|
||||
var anc = grid.GetAnchoredEntitiesEnumerator(grid.LocalToTile(coordinates));
|
||||
var toDelete = new ValueList<EntityUid>();
|
||||
|
||||
while (anc.MoveNext(out var ent))
|
||||
@@ -191,11 +186,14 @@ namespace Robust.Server.Placement
|
||||
|
||||
MapGridComponent? grid;
|
||||
|
||||
EntityUid gridId = coordinates.EntityId;
|
||||
if (_entityManager.TryGetComponent(coordinates.EntityId, out grid)
|
||||
|| _mapManager.TryFindGridAt(coordinates.ToMap(_entityManager, _xformSystem), out gridId, out grid))
|
||||
_mapManager.TryGetGrid(coordinates.EntityId, out grid);
|
||||
|
||||
if (grid == null)
|
||||
_mapManager.TryFindGridAt(coordinates.ToMap(_entityManager), out _, out grid);
|
||||
|
||||
if (grid != null) // stick to existing grid
|
||||
{
|
||||
_maps.SetTile(gridId, grid, coordinates, new Tile(tileType));
|
||||
grid.SetTile(coordinates, new Tile(tileType));
|
||||
|
||||
var placementEraseEvent = new PlacementTileEvent(tileType, coordinates, placingUserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
|
||||
@@ -204,9 +202,9 @@ namespace Robust.Server.Placement
|
||||
{
|
||||
var newGrid = _mapManager.CreateGridEntity(coordinates.GetMapId(_entityManager));
|
||||
var newGridXform = _entityManager.GetComponent<TransformComponent>(newGrid);
|
||||
_xformSystem.SetWorldPosition(newGridXform, coordinates.Position - newGrid.Comp.TileSizeHalfVector); // assume bottom left tile origin
|
||||
newGridXform.WorldPosition = coordinates.Position - newGrid.Comp.TileSizeHalfVector; // assume bottom left tile origin
|
||||
var tilePos = newGrid.Comp.WorldToTile(coordinates.Position);
|
||||
_maps.SetTile(newGrid.Owner, newGrid.Comp, tilePos, new Tile(tileType));
|
||||
newGrid.Comp.SetTile(tilePos, new Tile(tileType));
|
||||
|
||||
var placementEraseEvent = new PlacementTileEvent(tileType, coordinates, placingUserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
|
||||
@@ -230,16 +228,11 @@ namespace Robust.Server.Placement
|
||||
{
|
||||
EntityCoordinates start = _entityManager.GetCoordinates(msg.NetCoordinates);
|
||||
Vector2 rectSize = msg.RectSize;
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(start.GetMapId(_entityManager),
|
||||
foreach (EntityUid entity in EntitySystem.Get<EntityLookupSystem>().GetEntitiesIntersecting(start.GetMapId(_entityManager),
|
||||
new Box2(start.Position, start.Position + rectSize)))
|
||||
{
|
||||
if (_entityManager.Deleted(entity) ||
|
||||
_entityManager.HasComponent<MapGridComponent>(entity) ||
|
||||
_entityManager.HasComponent<ActorComponent>(entity))
|
||||
{
|
||||
if (_entityManager.Deleted(entity) || _entityManager.HasComponent<MapGridComponent>(entity) || _entityManager.HasComponent<ActorComponent>(entity))
|
||||
continue;
|
||||
}
|
||||
|
||||
var placementEraseEvent = new PlacementEntityEvent(entity, _entityManager.GetComponent<TransformComponent>(entity).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
|
||||
_entityManager.DeleteEntity(entity);
|
||||
@@ -251,19 +244,19 @@ namespace Robust.Server.Placement
|
||||
/// </summary>
|
||||
public void SendPlacementBegin(EntityUid mob, int range, string objectType, string alignOption)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
|
||||
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
|
||||
return;
|
||||
|
||||
var playerConnection = actor.PlayerSession.Channel;
|
||||
var playerConnection = actor.PlayerSession.ConnectedClient;
|
||||
if (playerConnection == null)
|
||||
return;
|
||||
|
||||
var message = new MsgPlacement
|
||||
{
|
||||
PlaceType = PlacementManagerMessage.StartPlacement,
|
||||
Range = range,
|
||||
IsTile = false,
|
||||
ObjType = objectType,
|
||||
AlignOption = alignOption
|
||||
};
|
||||
var message = new MsgPlacement();
|
||||
message.PlaceType = PlacementManagerMessage.StartPlacement;
|
||||
message.Range = range;
|
||||
message.IsTile = false;
|
||||
message.ObjType = objectType;
|
||||
message.AlignOption = alignOption;
|
||||
_networkManager.ServerSendMessage(message, playerConnection);
|
||||
}
|
||||
|
||||
@@ -272,19 +265,19 @@ namespace Robust.Server.Placement
|
||||
/// </summary>
|
||||
public void SendPlacementBeginTile(EntityUid mob, int range, string tileType, string alignOption)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
|
||||
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
|
||||
return;
|
||||
|
||||
var playerConnection = actor.PlayerSession.Channel;
|
||||
var playerConnection = actor.PlayerSession.ConnectedClient;
|
||||
if (playerConnection == null)
|
||||
return;
|
||||
|
||||
var message = new MsgPlacement
|
||||
{
|
||||
PlaceType = PlacementManagerMessage.StartPlacement,
|
||||
Range = range,
|
||||
IsTile = true,
|
||||
ObjType = tileType,
|
||||
AlignOption = alignOption
|
||||
};
|
||||
var message = new MsgPlacement();
|
||||
message.PlaceType = PlacementManagerMessage.StartPlacement;
|
||||
message.Range = range;
|
||||
message.IsTile = true;
|
||||
message.ObjType = tileType;
|
||||
message.AlignOption = alignOption;
|
||||
_networkManager.ServerSendMessage(message, playerConnection);
|
||||
}
|
||||
|
||||
@@ -293,15 +286,15 @@ namespace Robust.Server.Placement
|
||||
/// </summary>
|
||||
public void SendPlacementCancel(EntityUid mob)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor))
|
||||
if (!_entityManager.TryGetComponent<ActorComponent?>(mob, out var actor))
|
||||
return;
|
||||
|
||||
var playerConnection = actor.PlayerSession.Channel;
|
||||
var playerConnection = actor.PlayerSession.ConnectedClient;
|
||||
if (playerConnection == null)
|
||||
return;
|
||||
|
||||
var message = new MsgPlacement
|
||||
{
|
||||
PlaceType = PlacementManagerMessage.CancelPlacement
|
||||
};
|
||||
var message = new MsgPlacement();
|
||||
message.PlaceType = PlacementManagerMessage.CancelPlacement;
|
||||
_networkManager.ServerSendMessage(message, playerConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,9 @@ namespace Robust.Server.Player
|
||||
/// <param name="args"></param>
|
||||
private void NewSession(object? sender, NetChannelArgs args)
|
||||
{
|
||||
CreateAndAddSession(args.Channel);
|
||||
var session = CreateAndAddSession(args.Channel.UserId, args.Channel.UserName);
|
||||
session.Channel = args.Channel;
|
||||
|
||||
PlayerCountMetric.Set(PlayerCount);
|
||||
// Synchronize base time.
|
||||
var msgTimeBase = new MsgSyncTimeBase();
|
||||
@@ -104,7 +106,8 @@ namespace Robust.Server.Player
|
||||
DebugTools.Assert(session.Channel == args.Channel);
|
||||
|
||||
SetStatus(session, SessionStatus.Disconnected);
|
||||
SetAttachedEntity(session, null, out _, true);
|
||||
if (session.AttachedEntity != null)
|
||||
EntManager.System<ActorSystem>().Detach(session.AttachedEntity.Value);
|
||||
|
||||
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="7.0.0" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.2" 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,7 +3,6 @@ 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;
|
||||
@@ -295,9 +294,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) ?? Task.FromResult<CompletionList?>(null));
|
||||
var results = await CompletionService
|
||||
.GetService(document)
|
||||
.GetCompletionsAsync(document, message.Cursor);
|
||||
|
||||
if (results is not null)
|
||||
{
|
||||
|
||||
@@ -34,12 +34,12 @@ public sealed class DefaultMagicAczProvider : IMagicAczProvider
|
||||
var inputPass = graph.Input;
|
||||
|
||||
var contentDir = FindContentRootPath(_deps);
|
||||
await RobustSharedPackaging.WriteContentAssemblies(
|
||||
await RobustClientPackaging.WriteContentAssemblies(
|
||||
inputPass,
|
||||
contentDir,
|
||||
binFolderPath,
|
||||
assemblyNames,
|
||||
cancel: cancel);
|
||||
cancel);
|
||||
|
||||
await RobustClientPackaging.WriteClientResources(contentDir, inputPass, cancel);
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
getStateInit.Append($@"
|
||||
{name} = GetNetEntitySet(component.{name}),");
|
||||
handleStateSetters.Append($@"
|
||||
EnsureEntitySet<{componentName}>(state.{name}, uid, component.{name});");
|
||||
component.{name} = EnsureEntitySet<{componentName}>(state.{name}, uid);");
|
||||
|
||||
break;
|
||||
case GlobalEntityUidListName:
|
||||
@@ -177,7 +177,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
getStateInit.Append($@"
|
||||
{name} = GetNetEntityList(component.{name}),");
|
||||
handleStateSetters.Append($@"
|
||||
EnsureEntityList<{componentName}>(state.{name}, uid, component.{name});");
|
||||
component.{name} = EnsureEntityList<{componentName}>(state.{name}, uid);");
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ILReader.Core" Version="1.0.0.4" />
|
||||
<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="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="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, -1));
|
||||
return gpos(x, y, new EntityUid(gridId));
|
||||
}
|
||||
|
||||
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, -1);
|
||||
return new(i);
|
||||
}
|
||||
|
||||
public MapGridComponent getgrid(int i)
|
||||
{
|
||||
return map.GetGrid(new EntityUid(i, -1));
|
||||
return map.GetGrid(new EntityUid(i));
|
||||
}
|
||||
|
||||
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) where T : IComponent
|
||||
public bool HasComp<T>(EntityUid uid)
|
||||
=> ent.HasComponent<T>(uid);
|
||||
|
||||
public EntityUid Spawn(string? prototype, EntityCoordinates position)
|
||||
|
||||
@@ -53,16 +53,6 @@ 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>
|
||||
@@ -129,13 +119,6 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<int> NetBufferSize =
|
||||
CVarDef.Create("net.buffer_size", 2, CVar.ARCHIVE | CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum size of the game state buffer. If this is exceeded the client will request a full game state.
|
||||
/// Values less than <see cref="GameStateProcessor.MinimumMaxBufferSize"/> will be ignored.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> NetMaxBufferSize =
|
||||
CVarDef.Create("net.max_buffer_size", 512, CVar.ARCHIVE | CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Enable verbose game state/networking logging.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
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,7 +131,6 @@ 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";
|
||||
@@ -145,13 +144,10 @@ internal sealed class ListMapsCommand : LocalizedCommands
|
||||
|
||||
foreach (var mapId in _map.GetAllMapIds().OrderBy(id => id.Value))
|
||||
{
|
||||
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),
|
||||
msg.AppendFormat("{0}: init: {1}, paused: {2}, ent: {3}, grids: {4}\n",
|
||||
mapId, _map.IsMapInitialized(mapId),
|
||||
_map.IsMapPaused(mapId),
|
||||
_entManager.GetNetEntity(_map.GetMapEntityId(mapId)),
|
||||
_map.GetMapEntityId(mapId),
|
||||
string.Join(",", _map.GetAllGrids(mapId).Select(grid => grid.Owner)));
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,20 @@ namespace Robust.Shared.Containers
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
[Obsolete("Use container system method")]
|
||||
/// <summary>
|
||||
/// Attempts to insert the entity into this container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the insertion is successful, the inserted entity will end up parented to the
|
||||
/// container entity, and the inserted entity's local position will be set to the zero vector.
|
||||
/// </remarks>
|
||||
/// <param name="toinsert">The entity to insert.</param>
|
||||
/// <param name="entMan"></param>
|
||||
/// <returns>False if the entity could not be inserted.</returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if this container is a child of the entity,
|
||||
/// which would cause infinite loops.
|
||||
/// </exception>
|
||||
public bool Insert(
|
||||
EntityUid toinsert,
|
||||
IEntityManager? entMan = null,
|
||||
@@ -125,23 +138,16 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
transform ??= transformQuery.GetComponent(toinsert);
|
||||
|
||||
//Verify we can insert into this container
|
||||
if (!force && !containerSys.CanInsert(toinsert, this, containerXform: ownerTransform))
|
||||
if (!force && !containerSys.CanInsert(toinsert, this))
|
||||
return false;
|
||||
|
||||
// Please somebody ecs containers
|
||||
var lookupSys = entMan.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
|
||||
var xformSys = entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
transform ??= transformQuery.GetComponent(toinsert);
|
||||
meta ??= entMan.GetComponent<MetaDataComponent>(toinsert);
|
||||
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
Logger.ErrorS("container",
|
||||
$"Attempted to insert a terminating entity {entMan.ToPrettyString(toinsert)} into a container {ID} in entity: {entMan.ToPrettyString(Owner)}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove from any old containers.
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
@@ -264,7 +270,16 @@ namespace Robust.Shared.Containers
|
||||
/// <param name="assumeEmpty">Whether to assume that the container is currently empty.</param>
|
||||
protected internal virtual bool CanInsert(EntityUid toInsert, bool assumeEmpty, IEntityManager entMan) => true;
|
||||
|
||||
[Obsolete("Use container system method")]
|
||||
/// <summary>
|
||||
/// Attempts to remove the entity from this container.
|
||||
/// </summary>
|
||||
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
|
||||
/// destination is not null</param>
|
||||
/// <param name="force">If true, this will not perform can-remove checks.</param>
|
||||
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
|
||||
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
|
||||
/// container, the grid, or the map.</param>
|
||||
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
|
||||
public bool Remove(
|
||||
EntityUid toRemove,
|
||||
IEntityManager? entMan = null,
|
||||
@@ -344,7 +359,7 @@ namespace Robust.Shared.Containers
|
||||
return true;
|
||||
}
|
||||
|
||||
[Obsolete("Use container system method")]
|
||||
[Obsolete("use force option in Remove()")]
|
||||
public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null)
|
||||
=> Remove(toRemove, entMan, meta: meta, reparent: false, force: true);
|
||||
|
||||
|
||||
@@ -1,41 +1,10 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Containers;
|
||||
|
||||
public abstract partial class SharedContainerSystem
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert the entity into this container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the insertion is successful, the inserted entity will end up parented to the
|
||||
/// container entity, and the inserted entity's local position will be set to the zero vector.
|
||||
/// </remarks>
|
||||
/// <param name="toInsert">The entity to insert.</param>
|
||||
/// <param name="container">The container to insert into.</param>
|
||||
/// <param name="containerXform">The container's transform component.</param>
|
||||
/// <param name="force">Whether to bypass normal insertion checks.</param>
|
||||
/// <returns>False if the entity could not be inserted.</returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if this container is a child of the entity,
|
||||
/// which would cause infinite loops.
|
||||
/// </exception>
|
||||
public bool Insert(Entity<TransformComponent?, MetaDataComponent?, PhysicsComponent?> toInsert,
|
||||
BaseContainer container,
|
||||
TransformComponent? containerXform = null,
|
||||
bool force = false)
|
||||
{
|
||||
// Cannot Use Resolve(ref toInsert) as the physics component is optional
|
||||
if (!Resolve(toInsert.Owner, ref toInsert.Comp1, ref toInsert.Comp2))
|
||||
return false;
|
||||
|
||||
// TODO move logic over to the system.
|
||||
return container.Insert(toInsert, EntityManager, toInsert, containerXform, toInsert, toInsert, force);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity can be inserted into the given container.
|
||||
/// </summary>
|
||||
@@ -44,8 +13,8 @@ public abstract partial class SharedContainerSystem
|
||||
public bool CanInsert(
|
||||
EntityUid toInsert,
|
||||
BaseContainer container,
|
||||
bool assumeEmpty = false,
|
||||
TransformComponent? containerXform = null)
|
||||
TransformComponent? toInsertXform = null,
|
||||
bool assumeEmpty = false)
|
||||
{
|
||||
if (container.Owner == toInsert)
|
||||
return false;
|
||||
@@ -56,12 +25,15 @@ public abstract partial class SharedContainerSystem
|
||||
if (!container.CanInsert(toInsert, assumeEmpty, EntityManager))
|
||||
return false;
|
||||
|
||||
if (!TransformQuery.Resolve(toInsert, ref toInsertXform))
|
||||
return false;
|
||||
|
||||
// no, you can't put maps or grids into containers
|
||||
if (_mapQuery.HasComponent(toInsert) || _gridQuery.HasComponent(toInsert))
|
||||
return false;
|
||||
|
||||
// Prevent circular insertion.
|
||||
if (_transform.ContainsEntity(toInsert, (container.Owner, containerXform)))
|
||||
if (_transform.ContainsEntity(toInsertXform, container.Owner))
|
||||
return false;
|
||||
|
||||
var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(container, toInsert, assumeEmpty);
|
||||
|
||||
@@ -1,43 +1,9 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Containers;
|
||||
|
||||
public abstract partial class SharedContainerSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to remove the entity from this container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the insertion is successful, the inserted entity will end up parented to the
|
||||
/// container entity, and the inserted entity's local position will be set to the zero vector.
|
||||
/// </remarks>
|
||||
/// <param name="toRemove">The entity to remove.</param>
|
||||
/// <param name="container">The container to remove from.</param>
|
||||
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
|
||||
/// destination is not null</param>
|
||||
/// <param name="force">If true, this will not perform can-remove checks.</param>
|
||||
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
|
||||
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
|
||||
/// container, the grid, or the map.</param>
|
||||
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
|
||||
public bool Remove(
|
||||
Entity<TransformComponent?, MetaDataComponent?> toRemove,
|
||||
BaseContainer container,
|
||||
bool reparent = true,
|
||||
bool force = false,
|
||||
EntityCoordinates? destination = null,
|
||||
Angle? localRotation = null)
|
||||
{
|
||||
// Cannot Use Resolve(ref toInsert) as the physics component is optional
|
||||
if (!Resolve(toRemove.Owner, ref toRemove.Comp1, ref toRemove.Comp2))
|
||||
return false;
|
||||
|
||||
// TODO move logic over to the system.
|
||||
return container.Remove(toRemove, EntityManager, toRemove, toRemove, reparent, force, destination, localRotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity can be removed from this container.
|
||||
/// </summary>
|
||||
|
||||
@@ -216,27 +216,35 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively check if the entity or any parent is inside of a container.
|
||||
/// Recursively if the entity, or any parent entity, is inside of a container.
|
||||
/// </summary>
|
||||
/// <returns>If the entity is inside of a container.</returns>
|
||||
public bool IsEntityOrParentInContainer(
|
||||
EntityUid uid,
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null)
|
||||
TransformComponent? xform = null,
|
||||
EntityQuery<MetaDataComponent>? metas = null,
|
||||
EntityQuery<TransformComponent>? xforms = null)
|
||||
{
|
||||
if (!MetaQuery.Resolve(uid, ref meta))
|
||||
return false;
|
||||
if (meta == null)
|
||||
{
|
||||
metas ??= GetEntityQuery<MetaDataComponent>();
|
||||
meta = metas.Value.GetComponent(uid);
|
||||
}
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer)
|
||||
return true;
|
||||
|
||||
if (!TransformQuery.Resolve(uid, ref xform))
|
||||
return false;
|
||||
if (xform == null)
|
||||
{
|
||||
xforms ??= GetEntityQuery<TransformComponent>();
|
||||
xform = xforms.Value.GetComponent(uid);
|
||||
}
|
||||
|
||||
if (!xform.ParentUid.Valid)
|
||||
return false;
|
||||
|
||||
return IsEntityOrParentInContainer(xform.ParentUid);
|
||||
return IsEntityOrParentInContainer(xform.ParentUid, metas: metas, xforms: xforms);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Shared.ContentPack
|
||||
String("short").ThenReturn(PrimitiveTypeCode.Int16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> UInt16TypeParser =
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt32);
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> Int32TypeParser =
|
||||
String("int").ThenReturn(PrimitiveTypeCode.Int32);
|
||||
|
||||
@@ -73,20 +73,148 @@ 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
|
||||
Methods:
|
||||
- "byte[] get_Data()"
|
||||
- "void set_Data(byte[])"
|
||||
- "int get_LengthBytes()"
|
||||
- "void set_LengthBytes(int)"
|
||||
- "int get_LengthBits()"
|
||||
- "void set_LengthBits(int)"
|
||||
- "long get_Position()"
|
||||
- "void set_Position(long)"
|
||||
- "int get_PositionInBytes()"
|
||||
- "byte[] PeekDataBuffer()"
|
||||
- "bool PeekBoolean()"
|
||||
- "byte PeekByte()"
|
||||
- "sbyte PeekSByte()"
|
||||
- "byte PeekByte(int)"
|
||||
- "System.Span`1<byte> PeekBytes(System.Span`1<byte>)"
|
||||
- "byte[] PeekBytes(int)"
|
||||
- "void PeekBytes(byte[], int, int)"
|
||||
- "short PeekInt16()"
|
||||
- "ushort PeekUInt16()"
|
||||
- "int PeekInt32()"
|
||||
- "int PeekInt32(int)"
|
||||
- "uint PeekUInt32()"
|
||||
- "uint PeekUInt32(int)"
|
||||
- "ulong PeekUInt64()"
|
||||
- "long PeekInt64()"
|
||||
- "ulong PeekUInt64(int)"
|
||||
- "long PeekInt64(int)"
|
||||
- "float PeekFloat()"
|
||||
- "System.Half PeekHalf()"
|
||||
- "float PeekSingle()"
|
||||
- "double PeekDouble()"
|
||||
- "string PeekString()"
|
||||
- "int PeekStringSize()"
|
||||
- "bool ReadBoolean()"
|
||||
- "byte ReadByte()"
|
||||
- "bool ReadByte(ref byte)"
|
||||
- "sbyte ReadSByte()"
|
||||
- "byte ReadByte(int)"
|
||||
- "System.Span`1<byte> ReadBytes(System.Span`1<byte>)"
|
||||
- "byte[] ReadBytes(int)"
|
||||
- "bool ReadBytes(int, ref byte[])"
|
||||
- "bool TryReadBytes(System.Span`1<byte>)"
|
||||
- "void ReadBytes(byte[], int, int)"
|
||||
- "void ReadBits(System.Span`1<byte>, int)"
|
||||
- "void ReadBits(byte[], int, int)"
|
||||
- "short ReadInt16()"
|
||||
- "ushort ReadUInt16()"
|
||||
- "int ReadInt32()"
|
||||
- "bool ReadInt32(ref int)"
|
||||
- "int ReadInt32(int)"
|
||||
- "uint ReadUInt32()"
|
||||
- "bool ReadUInt32(ref uint)"
|
||||
- "uint ReadUInt32(int)"
|
||||
- "ulong ReadUInt64()"
|
||||
- "long ReadInt64()"
|
||||
- "ulong ReadUInt64(int)"
|
||||
- "long ReadInt64(int)"
|
||||
- "float ReadFloat()"
|
||||
- "System.Half ReadHalf()"
|
||||
- "float ReadSingle()"
|
||||
- "bool ReadSingle(ref float)"
|
||||
- "double ReadDouble()"
|
||||
- "uint ReadVariableUInt32()"
|
||||
- "bool ReadVariableUInt32(ref uint)"
|
||||
- "int ReadVariableInt32()"
|
||||
- "long ReadVariableInt64()"
|
||||
- "ulong ReadVariableUInt64()"
|
||||
- "float ReadSignedSingle(int)"
|
||||
- "float ReadUnitSingle(int)"
|
||||
- "float ReadRangedSingle(float, float, int)"
|
||||
- "int ReadRangedInteger(int, int)"
|
||||
- "long ReadRangedInteger(long, long)"
|
||||
- "string ReadString()"
|
||||
- "bool ReadString(ref string)"
|
||||
- "double ReadTime(Lidgren.Network.NetConnection, bool)"
|
||||
- "System.Net.IPEndPoint ReadIPEndPoint()"
|
||||
- "void SkipPadBits()"
|
||||
- "void ReadPadBits()"
|
||||
- "void SkipPadBits(int)"
|
||||
- "void EnsureBufferSize(int)"
|
||||
- "void Write(bool)"
|
||||
- "void Write(byte)"
|
||||
- "void WriteAt(int, byte)"
|
||||
- "void Write(sbyte)"
|
||||
- "void Write(byte, int)"
|
||||
- "void Write(byte[])"
|
||||
- "void Write(System.ReadOnlySpan`1<byte>)"
|
||||
- "void Write(byte[], int, int)"
|
||||
- "void Write(ushort)"
|
||||
- "void WriteAt(int, ushort)"
|
||||
- "void Write(ushort, int)"
|
||||
- "void Write(short)"
|
||||
- "void WriteAt(int, short)"
|
||||
- "void Write(int)"
|
||||
- "void WriteAt(int, int)"
|
||||
- "void Write(uint)"
|
||||
- "void WriteAt(int, uint)"
|
||||
- "void Write(uint, int)"
|
||||
- "void Write(int, int)"
|
||||
- "void Write(ulong)"
|
||||
- "void WriteAt(int, ulong)"
|
||||
- "void Write(ulong, int)"
|
||||
- "void Write(long)"
|
||||
- "void Write(long, int)"
|
||||
- "void Write(System.Half)"
|
||||
- "void Write(float)"
|
||||
- "void Write(double)"
|
||||
- "int WriteVariableUInt32(uint)"
|
||||
- "int WriteVariableInt32(int)"
|
||||
- "int WriteVariableInt64(long)"
|
||||
- "int WriteVariableUInt64(ulong)"
|
||||
- "void WriteSignedSingle(float, int)"
|
||||
- "void WriteUnitSingle(float, int)"
|
||||
- "void WriteRangedSingle(float, float, float, int)"
|
||||
- "int WriteRangedInteger(int, int, int)"
|
||||
- "int WriteRangedInteger(long, long, long)"
|
||||
- "void Write(string)"
|
||||
- "void Write(System.Net.IPEndPoint)"
|
||||
- "void WriteTime(bool)"
|
||||
- "void WriteTime(double, bool)"
|
||||
- "void WritePadBits()"
|
||||
- "void WritePadBits(int)"
|
||||
- "void Write(Lidgren.Network.NetBuffer)"
|
||||
- "void Zero(int)"
|
||||
- "void .ctor()"
|
||||
NetDeliveryMethod: { }
|
||||
NetIncomingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "Lidgren.Network.NetIncomingMessageType get_MessageType()"
|
||||
- "Lidgren.Network.NetDeliveryMethod get_DeliveryMethod()"
|
||||
- "int get_SequenceChannel()"
|
||||
- "System.Net.IPEndPoint get_SenderEndPoint()"
|
||||
- "Lidgren.Network.NetConnection get_SenderConnection()"
|
||||
- "double get_ReceiveTime()"
|
||||
- "double ReadTime(bool)"
|
||||
- "string ToString()"
|
||||
NetOutgoingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "string ToString()"
|
||||
Nett:
|
||||
CommentLocation: { } # Enum
|
||||
Toml:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ContentPack
|
||||
@@ -135,11 +136,37 @@ namespace Robust.Shared.ContentPack
|
||||
path = path.Directory;
|
||||
|
||||
var fullPath = GetFullPath(path);
|
||||
Process.Start(new ProcessStartInfo
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = fullPath,
|
||||
});
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "open",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "xdg-open",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Opening OS windows not supported on this OS");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
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];
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
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,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Arch.Core.Utils;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -14,7 +13,6 @@ 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;
|
||||
|
||||
@@ -64,13 +62,12 @@ 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), typeof(T));
|
||||
public static readonly CompIdx Index = new(Interlocked.Increment(ref _CompIdxMaster));
|
||||
}
|
||||
|
||||
internal CompIdx(int value, in ComponentType type)
|
||||
internal CompIdx(int value)
|
||||
{
|
||||
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 ComponentRegistration ComponentType;
|
||||
public readonly CompIdx ComponentType;
|
||||
|
||||
public AddedComponentEventArgs(ComponentEventArgs baseArgs, ComponentRegistration componentType)
|
||||
public AddedComponentEventArgs(ComponentEventArgs baseArgs, CompIdx componentType)
|
||||
{
|
||||
BaseArgs = baseArgs;
|
||||
ComponentType = componentType;
|
||||
@@ -55,4 +55,17 @@ 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,10 +12,16 @@ namespace Robust.Shared.GameObjects
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedEyeSystem)), AutoGenerateComponentState(true)]
|
||||
public sealed partial class EyeComponent : Component
|
||||
{
|
||||
public const int DefaultVisibilityMask = 1;
|
||||
#region Client
|
||||
|
||||
[ViewVariables] internal Eye? _eye = default!;
|
||||
|
||||
public IEye? Eye => _eye;
|
||||
|
||||
[ViewVariables]
|
||||
public readonly Eye Eye = new();
|
||||
public MapCoordinates? Position => _eye?.Position;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this entity is used to update the eye's position instead of just using the component's owner.
|
||||
@@ -31,9 +37,6 @@ 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;
|
||||
@@ -44,6 +47,8 @@ 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.
|
||||
@@ -53,7 +58,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Single layer used for Eye visibility. Controls what entities they are allowed to see.
|
||||
/// Single layer used for Eye visiblity. 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 = EntityUid.Invalid;
|
||||
[DataField("parent")] internal EntityUid _parent;
|
||||
[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;
|
||||
@@ -311,7 +311,6 @@ namespace Robust.Shared.GameObjects
|
||||
/// This is effectively a more complete version of <see cref="WorldPosition"/>
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[Obsolete("Use TransformSystem.GetMapCoordinates")]
|
||||
public MapCoordinates MapPosition => new(WorldPosition, MapID);
|
||||
|
||||
/// <summary>
|
||||
@@ -733,7 +732,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public bool IsValid() => Uid.IsValid();
|
||||
public bool Valid => IsValid();
|
||||
public static readonly BroadphaseData Invalid = new(EntityUid.Invalid, EntityUid.Invalid, false, false);
|
||||
public static readonly BroadphaseData Invalid = default;
|
||||
|
||||
// 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.Idx);
|
||||
EntAddComponent(e.BaseArgs.Owner, e.ComponentType);
|
||||
}
|
||||
|
||||
public void OnComponentRemoved(in RemovedComponentEventArgs e)
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
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,7 +256,8 @@ public partial class EntityManager
|
||||
/// <inheritdoc />
|
||||
public HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities)
|
||||
{
|
||||
var entities = new HashSet<EntityUid>(netEntities.Count);
|
||||
var entities = new HashSet<EntityUid>();
|
||||
entities.EnsureCapacity(netEntities.Count);
|
||||
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
@@ -291,16 +292,6 @@ 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)
|
||||
{
|
||||
@@ -314,16 +305,6 @@ 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,7 +4,6 @@ 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;
|
||||
@@ -41,16 +40,6 @@ 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,10 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions;
|
||||
using Arch.Core.Utils;
|
||||
using Collections.Pooled;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -16,8 +13,6 @@ 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
|
||||
{
|
||||
@@ -44,8 +39,6 @@ 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;
|
||||
|
||||
@@ -69,8 +62,15 @@ 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);
|
||||
|
||||
InitializeArch();
|
||||
InitializeComponents();
|
||||
_metaReg = _componentFactory.GetRegistration(typeof(MetaDataComponent));
|
||||
_xformReg = _componentFactory.GetRegistration(typeof(TransformComponent));
|
||||
_xformName = _xformReg.Name;
|
||||
@@ -136,6 +136,15 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
var prototype = metadata.EntityPrototype;
|
||||
|
||||
// Prototype may or may not have metadata / transforms
|
||||
var protoComps = prototype.Components.Keys.ToList();
|
||||
|
||||
protoComps.Remove(_xformName);
|
||||
|
||||
// Fast check if the component counts match.
|
||||
if (protoComps.Count != ComponentCount(uid) - 2)
|
||||
return false;
|
||||
|
||||
// Check if entity name / description match
|
||||
if (metadata.EntityName != prototype.Name ||
|
||||
metadata.EntityDescription != prototype.Description)
|
||||
@@ -143,28 +152,43 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
var protoData = PrototypeManager.GetPrototypeData(prototype);
|
||||
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.Length)
|
||||
return false;
|
||||
|
||||
foreach (var comp in comps)
|
||||
// Get default prototype data
|
||||
Dictionary<string, MappingDataNode> protoData = new();
|
||||
try
|
||||
{
|
||||
var component = (IComponent)comp!;
|
||||
_context.WritingReadingPrototypes = true;
|
||||
|
||||
if (component.Deleted)
|
||||
return false;
|
||||
foreach (var compType in protoComps)
|
||||
{
|
||||
if (compType == _xformName)
|
||||
continue;
|
||||
|
||||
var comp = prototype.Components[compType];
|
||||
protoData.Add(compType, _serManager.WriteValueAs<MappingDataNode>(comp.Component.GetType(), comp.Component, alwaysWrite: true, context: _context));
|
||||
}
|
||||
|
||||
_context.WritingReadingPrototypes = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Failed to convert prototype {prototype.ID} into yaml. Exception: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var comps = new HashSet<IComponent>(GetComponents(uid));
|
||||
var compNames = new HashSet<string>(protoComps.Count);
|
||||
foreach (var component in comps)
|
||||
{
|
||||
var compType = component.GetType();
|
||||
var compName = _componentFactory.GetComponentName(compType);
|
||||
if (compName == _xformName || compName == _metaReg.Name)
|
||||
|
||||
if (compType == typeof(MetaDataComponent) || compType == typeof(TransformComponent))
|
||||
continue;
|
||||
|
||||
compNames.Add(compName);
|
||||
|
||||
// If the component isn't on the prototype then it's custom.
|
||||
if (!protoData.TryGetValue(compName, out var protoMapping))
|
||||
if (!protoComps.Contains(compName))
|
||||
return false;
|
||||
|
||||
MappingDataNode compMapping;
|
||||
@@ -178,9 +202,25 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
var diff = compMapping.Except(protoMapping);
|
||||
if (protoData.TryGetValue(compName, out var protoMapping))
|
||||
{
|
||||
var diff = compMapping.Except(protoMapping);
|
||||
|
||||
if (diff != null && diff.Children.Count != 0)
|
||||
if (diff != null && diff.Children.Count != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// An entity may also remove components on init -> check no components are missing.
|
||||
foreach (var compType in protoComps)
|
||||
{
|
||||
if (!compNames.Contains(compType))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -210,7 +250,6 @@ namespace Robust.Shared.GameObjects
|
||||
FlushEntities();
|
||||
_eventBus.ClearEventTables();
|
||||
_entitySystemManager.Shutdown();
|
||||
ShutdownArch();
|
||||
ClearComponents();
|
||||
ShuttingDown = false;
|
||||
Started = false;
|
||||
@@ -218,12 +257,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;
|
||||
@@ -272,27 +311,28 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, out _, out _, overrides);
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, out _, out _, overrides);
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, out _, out var xform, overrides);
|
||||
_xforms.SetCoordinates(newEntity, xform, coordinates, unanchor: false);
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
_xforms.SetCoordinates(newEntity, TransformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, out _, out var transform, overrides);
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
var transform = TransformQuery.GetComponent(newEntity);
|
||||
|
||||
if (coordinates.MapId == MapId.Nullspace)
|
||||
{
|
||||
@@ -322,19 +362,10 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int EntityCount => _world.Size;
|
||||
public int EntityCount => Entities.Count;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetEntities()
|
||||
{
|
||||
var ents = new List<Entity>();
|
||||
_world.GetEntities(_archMetaQuery, ents);
|
||||
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
yield return EntityUid.FromArch(_world, entity);
|
||||
}
|
||||
}
|
||||
public IEnumerable<EntityUid> GetEntities() => Entities;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void DirtyEntity(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
@@ -386,7 +417,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="e">Entity to remove</param>
|
||||
public virtual void DeleteEntity(EntityUid? uid)
|
||||
{
|
||||
if (uid == null || uid == EntityUid.Invalid)
|
||||
if (uid == null)
|
||||
return;
|
||||
var e = uid.Value;
|
||||
|
||||
@@ -411,9 +442,9 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
// Notify all entities they are being terminated prior to detaching & deleting
|
||||
var xform = TransformQuery.GetComponent(e);
|
||||
RecursiveFlagEntityTermination(e, meta, xform);
|
||||
RecursiveFlagEntityTermination(e, meta);
|
||||
|
||||
var xform = TransformQuery.GetComponent(e);
|
||||
TransformComponent? parentXform = null;
|
||||
if (xform.ParentUid.IsValid())
|
||||
TransformQuery.Resolve(xform.ParentUid, ref parentXform);
|
||||
@@ -424,14 +455,14 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private void RecursiveFlagEntityTermination(
|
||||
EntityUid uid,
|
||||
MetaDataComponent metadata,
|
||||
TransformComponent transform)
|
||||
MetaDataComponent metadata)
|
||||
{
|
||||
var transform = TransformQuery.GetComponent(uid);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Terminating;
|
||||
|
||||
try
|
||||
{
|
||||
var ev = new EntityTerminatingEvent(uid, metadata);
|
||||
var ev = new EntityTerminatingEvent(uid);
|
||||
EventBus.RaiseLocalEvent(uid, ref ev, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -448,7 +479,7 @@ namespace Robust.Shared.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
RecursiveFlagEntityTermination(child, childMeta, TransformQuery.GetComponent(child));
|
||||
RecursiveFlagEntityTermination(child, childMeta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,12 +527,8 @@ namespace Robust.Shared.GameObjects
|
||||
_sawmill.Error($"Failed to delete all children of entity: {ToPrettyString(uid)}");
|
||||
|
||||
// Shut down all components.
|
||||
var objComps = _world.GetAllComponents(uid);
|
||||
|
||||
foreach (var comp in objComps)
|
||||
foreach (var component in InSafeOrder(_entCompIndex[uid]))
|
||||
{
|
||||
var component = (IComponent)comp!;
|
||||
|
||||
if (component.Running)
|
||||
{
|
||||
try
|
||||
@@ -516,7 +543,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
// Dispose all my components, in a safe order so transform is available
|
||||
DisposeComponents(uid, metadata);
|
||||
DisposeComponents(uid);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Deleted;
|
||||
|
||||
try
|
||||
@@ -529,14 +556,14 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
_eventBus.OnEntityDeleted(uid);
|
||||
DestroyArch(uid);
|
||||
Entities.Remove(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 || uid.Value == EntityUid.Invalid)
|
||||
if (uid == null)
|
||||
return;
|
||||
|
||||
if (!QueuedDeletionsSet.Add(uid.Value))
|
||||
@@ -550,7 +577,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool EntityExists(EntityUid uid)
|
||||
{
|
||||
return IsAlive(uid);
|
||||
return MetaQuery.HasComponentInternal(uid);
|
||||
}
|
||||
|
||||
public bool EntityExists(EntityUid? uid)
|
||||
@@ -569,26 +596,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool Deleted(EntityUid uid)
|
||||
{
|
||||
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);
|
||||
return !MetaQuery.TryGetComponentInternal(uid, out var comp) || comp.EntityDeleted;
|
||||
}
|
||||
|
||||
public bool Deleted([NotNullWhen(false)] EntityUid? uid)
|
||||
{
|
||||
return !uid.HasValue || Deleted(uid.Value);
|
||||
return !uid.HasValue || !MetaQuery.TryGetComponentInternal(uid.Value, out var comp) || comp.EntityDeleted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -603,10 +616,7 @@ namespace Robust.Shared.GameObjects
|
||||
DeleteEntity(e);
|
||||
}
|
||||
|
||||
// Arch bug atm
|
||||
// CleanupArch();
|
||||
|
||||
if (_world.Size > 0)
|
||||
if (Entities.Count != 0)
|
||||
_sawmill.Error("Entities were spawned while flushing entities.");
|
||||
}
|
||||
|
||||
@@ -615,10 +625,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
private protected EntityUid AllocEntity(
|
||||
EntityPrototype? prototype,
|
||||
out MetaDataComponent metadata,
|
||||
out TransformComponent xform)
|
||||
out MetaDataComponent metadata)
|
||||
{
|
||||
var entity = AllocEntity(out metadata, out xform);
|
||||
var entity = AllocEntity(out metadata);
|
||||
metadata._entityPrototype = prototype;
|
||||
Dirty(entity, metadata, metadata);
|
||||
return entity;
|
||||
@@ -627,9 +636,16 @@ 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, out TransformComponent xform)
|
||||
private EntityUid AllocEntity(out MetaDataComponent metadata)
|
||||
{
|
||||
SpawnEntityArch(out var uid);
|
||||
var uid = GenerateEntityUid();
|
||||
|
||||
#if DEBUG
|
||||
if (EntityExists(uid))
|
||||
{
|
||||
throw new InvalidOperationException($"UID already taken: {uid}");
|
||||
}
|
||||
#endif
|
||||
|
||||
// we want this called before adding components
|
||||
EntityAdded?.Invoke(uid);
|
||||
@@ -646,15 +662,16 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
SetNetEntity(uid, netEntity, metadata);
|
||||
|
||||
Entities.Add(uid);
|
||||
// add the required MetaDataComponent directly.
|
||||
AddComponentInternal(uid, metadata, _metaReg, true, metadata);
|
||||
AddComponentInternal(uid, metadata, _metaReg, false, true, metadata);
|
||||
|
||||
// allocate the required TransformComponent
|
||||
xform = Unsafe.As<TransformComponent>(_componentFactory.GetComponent(_xformReg));
|
||||
var xformComp = Unsafe.As<TransformComponent>(_componentFactory.GetComponent(_xformReg));
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
xform.Owner = uid;
|
||||
xformComp.Owner = uid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
AddComponentInternal(uid, xform, true, metadata);
|
||||
AddComponentInternal(uid, xformComp, false, true, metadata);
|
||||
|
||||
return uid;
|
||||
}
|
||||
@@ -662,26 +679,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, out TransformComponent xform, IEntityLoadContext? context = null)
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(out metadata, out xform);
|
||||
return AllocEntity(out metadata);
|
||||
|
||||
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, out xform, context);
|
||||
return CreateEntity(prototype, out metadata, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, out MetaDataComponent metadata, out TransformComponent xform, IEntityLoadContext? context = null)
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = AllocEntity(prototype, out metadata, out xform);
|
||||
var entity = AllocEntity(prototype, out metadata);
|
||||
try
|
||||
{
|
||||
LoadEntity(metadata.EntityPrototype, entity, context);
|
||||
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
return entity;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -695,105 +712,18 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context)
|
||||
{
|
||||
LoadEntity(MetaQuery.GetComponent(entity).EntityPrototype, entity, context);
|
||||
EntityPrototype.LoadEntity(MetaQuery.GetComponent(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
}
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context, EntityPrototype? prototype)
|
||||
{
|
||||
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);
|
||||
}
|
||||
EntityPrototype.LoadEntity(prototype, entity, ComponentFactory, this, _serManager, context);
|
||||
}
|
||||
|
||||
public void InitializeAndStartEntity(EntityUid entity, MapId? mapId = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Pass this + transformcomp around
|
||||
var meta = MetaQuery.GetComponent(entity);
|
||||
InitializeEntity(entity, meta);
|
||||
StartEntity(entity);
|
||||
@@ -872,10 +802,30 @@ 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>
|
||||
protected virtual NetEntity GenerateNetEntity() => new(NextNetworkId++);
|
||||
|
||||
private sealed class EntityDiffContext : ISerializationContext
|
||||
{
|
||||
public SerializationManager.SerializerProvider SerializerProvider { get; }
|
||||
public bool WritingReadingPrototypes { get; set; }
|
||||
|
||||
public EntityDiffContext()
|
||||
{
|
||||
SerializerProvider = new();
|
||||
SerializerProvider.RegisterSerializer(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum EntityMessageType : byte
|
||||
|
||||
@@ -154,26 +154,9 @@ 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
|
||||
{
|
||||
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?
|
||||
{
|
||||
var comp = ent.Comp1;
|
||||
if (comp == null && !EntityManager.TryGetComponent(ent.Owner, out comp))
|
||||
return;
|
||||
|
||||
EntityManager.Dirty(ent.Owner, comp, ent.Comp2);
|
||||
EntityManager.Dirty(ent.Owner, ent.Comp, meta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -525,7 +508,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) where T : IComponent
|
||||
protected bool HasComp<T>(EntityUid uid)
|
||||
{
|
||||
return EntityManager.HasComponent<T>(uid);
|
||||
}
|
||||
@@ -543,7 +526,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) where T : IComponent
|
||||
protected bool HasComp<T>([NotNullWhen(true)] EntityUid? uid)
|
||||
{
|
||||
return EntityManager.HasComponent<T>(uid);
|
||||
}
|
||||
@@ -572,7 +555,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);
|
||||
EntityManager.AddComponent(uid, component, overwrite);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.EnsureComponent<T>(EntityUid)"/>
|
||||
@@ -614,6 +597,13 @@ 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)
|
||||
@@ -656,6 +646,13 @@ 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)
|
||||
@@ -1010,24 +1007,12 @@ 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>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Shared.GameObjects
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Resolve{TComp}(Robust.Shared.GameObjects.EntityUid,ref TComp?,bool)"/>
|
||||
/// <inheritdoc cref="Resolve{TComp}"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref MetaDataComponent? component,
|
||||
bool logMissing = true)
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Shared.GameObjects
|
||||
return EntityManager.MetaQuery.Resolve(uid, ref component);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Resolve{TComp}(Robust.Shared.GameObjects.EntityUid,ref TComp?,bool)"/>
|
||||
/// <inheritdoc cref="Resolve{TComp}"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TransformComponent? component,
|
||||
bool logMissing = true)
|
||||
|
||||
@@ -7,12 +7,10 @@ namespace Robust.Shared.GameObjects
|
||||
public readonly struct EntityTerminatingEvent
|
||||
{
|
||||
public readonly EntityUid Entity;
|
||||
public readonly MetaDataComponent Metadata;
|
||||
|
||||
public EntityTerminatingEvent(EntityUid entity, MetaDataComponent metadata)
|
||||
public EntityTerminatingEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
Metadata = metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
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;
|
||||
@@ -12,7 +9,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// This type contains a local identification number of an entity.
|
||||
/// This type contains a network identification number of an entity.
|
||||
/// This can be used by the EntityManager to access an entity
|
||||
/// </summary>
|
||||
[CopyByRef]
|
||||
@@ -20,40 +17,22 @@ 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(-1 + ArchUidOffset, -1 + ArchVersionOffset);
|
||||
public static readonly EntityUid Invalid = new(0);
|
||||
|
||||
/// <summary>
|
||||
/// The first entity UID the entityManager should use when the manager is initialized.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
public static readonly EntityUid FirstUid = new(1);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of this structure, with the given network ID.
|
||||
/// </summary>
|
||||
public EntityUid(int id, int version)
|
||||
public EntityUid(int id)
|
||||
{
|
||||
Id = id;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public bool Valid => IsValid();
|
||||
@@ -61,16 +40,16 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Creates an entity UID by parsing a string number.
|
||||
/// </summary>
|
||||
public static EntityUid Parse(ReadOnlySpan<char> uid, ReadOnlySpan<char> version)
|
||||
public static EntityUid Parse(ReadOnlySpan<char> uid)
|
||||
{
|
||||
return new EntityUid(int.Parse(uid), int.Parse(version));
|
||||
return new EntityUid(int.Parse(uid));
|
||||
}
|
||||
|
||||
public static bool TryParse(ReadOnlySpan<char> uid, ReadOnlySpan<char> version, out EntityUid entityUid)
|
||||
public static bool TryParse(ReadOnlySpan<char> uid, out EntityUid entityUid)
|
||||
{
|
||||
try
|
||||
{
|
||||
entityUid = Parse(uid, version);
|
||||
entityUid = Parse(uid);
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
@@ -80,15 +59,6 @@ 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.
|
||||
@@ -96,13 +66,13 @@ namespace Robust.Shared.GameObjects
|
||||
[Pure]
|
||||
public bool IsValid()
|
||||
{
|
||||
return Id > Invalid.Id;
|
||||
return Id > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(EntityUid other)
|
||||
{
|
||||
return Id == other.Id && Version == other.Version;
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -115,10 +85,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return Id.GetHashCode() * 397 ^ Version.GetHashCode();
|
||||
}
|
||||
return Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -126,7 +93,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public static bool operator ==(EntityUid a, EntityUid b)
|
||||
{
|
||||
return a.Id == b.Id && a.Version == b.Version;
|
||||
return a.Id == b.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -146,21 +113,6 @@ 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, MetaDataComponent? metadata = null) where T : IComponent;
|
||||
void AddComponent<T>(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the component with the specified reference type,
|
||||
@@ -136,6 +136,14 @@ 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>
|
||||
@@ -156,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) where T : IComponent;
|
||||
bool HasComponent<T>(EntityUid uid);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity has a component type.
|
||||
@@ -164,7 +172,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) where T : IComponent;
|
||||
bool HasComponent<T>(EntityUid? uid);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity has a component type.
|
||||
@@ -273,7 +281,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) where T : IComponent?;
|
||||
bool TryGetComponent<T>(EntityUid uid, [NotNullWhen(true)] out T? component);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
@@ -282,7 +290,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) where T : IComponent?;
|
||||
bool TryGetComponent<T>([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out T? component);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
@@ -497,7 +505,5 @@ 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,7 +19,6 @@ 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);
|
||||
@@ -53,7 +52,7 @@ public partial interface IEntityManager
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
@@ -65,7 +64,7 @@ public partial interface IEntityManager
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
@@ -85,8 +84,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);
|
||||
}
|
||||
|
||||
@@ -171,16 +171,11 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
private void RecursiveAdd(EntityUid uid, ref ValueList<EntityUid> toAdd)
|
||||
{
|
||||
if (!_xformQuery.TryGetComponent(uid, out var xform))
|
||||
{
|
||||
Log.Error($"Encountered deleted entity {uid} while performing entity lookup.");
|
||||
return;
|
||||
}
|
||||
var childEnumerator = _xformQuery.GetComponent(uid).ChildEnumerator;
|
||||
|
||||
toAdd.Add(uid);
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
toAdd.Add(child.Value);
|
||||
RecursiveAdd(child.Value, ref toAdd);
|
||||
}
|
||||
}
|
||||
@@ -190,11 +185,6 @@ public sealed partial class EntityLookupSystem
|
||||
if ((flags & LookupFlags.Contained) == 0x0 || intersecting.Count == 0)
|
||||
return;
|
||||
|
||||
// TODO PERFORMANCE.
|
||||
// toAdd only exists because we can't add directly to intersecting w/o enumeration issues.
|
||||
// If we assume that there are more entities in containers than there are entities in the intersecting set, then
|
||||
// we would be better off creating a fixed-size EntityUid array and coping all intersecting entities into that
|
||||
// instead of creating a value list here that needs to be resized.
|
||||
var toAdd = new ValueList<EntityUid>();
|
||||
|
||||
foreach (var uid in intersecting)
|
||||
@@ -206,6 +196,7 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
foreach (var contained in con.ContainedEntities)
|
||||
{
|
||||
toAdd.Add(contained);
|
||||
RecursiveAdd(contained, ref toAdd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,7 @@ 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;
|
||||
@@ -19,6 +15,18 @@ 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,
|
||||
@@ -80,155 +88,6 @@ 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,
|
||||
@@ -411,7 +270,7 @@ public sealed partial class EntityLookupSystem
|
||||
if (xform.MapID != mapId ||
|
||||
!worldAABB.Contains(_transform.GetWorldPosition(xform)) ||
|
||||
((flags & LookupFlags.Contained) == 0x0 &&
|
||||
_container.IsEntityOrParentInContainer(uid, null, xform)))
|
||||
_container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform, _metaQuery, _xformQuery)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -449,6 +308,15 @@ 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));
|
||||
@@ -464,7 +332,7 @@ public sealed partial class EntityLookupSystem
|
||||
if (xform.MapID != mapId ||
|
||||
!worldAABB.Contains(_transform.GetWorldPosition(xform)) ||
|
||||
((flags & LookupFlags.Contained) == 0x0 &&
|
||||
_container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform)))
|
||||
_container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform, _metaQuery, _xformQuery)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -498,6 +366,14 @@ 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;
|
||||
@@ -540,163 +416,14 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
#endregion
|
||||
|
||||
#region IPhysShape
|
||||
|
||||
public void GetEntitiesIntersecting(Type type, MapId mapId, IPhysShape shape, HashSet<Entity<IComponent>> intersecting, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
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 HashSet<T> GetComponentsInRange<T>(EntityCoordinates coordinates, float range) where T : IComponent
|
||||
{
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
return GetComponentsInRange<T>(mapPos, range);
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange<T>(EntityCoordinates coordinates, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
{
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
@@ -714,34 +441,35 @@ 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)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
var entities = new HashSet<Entity<IComponent>>();
|
||||
GetEntitiesInRange(type, coordinates, range, entities);
|
||||
GetEntitiesInRange(type, coordinates.MapId, coordinates.Position, range, entities);
|
||||
return entities;
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange(Type type, MapCoordinates coordinates, float range, HashSet<Entity<IComponent>> entities)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
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, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
public void GetEntitiesInRange<T>(MapCoordinates coordinates, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
{
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities, flags);
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities);
|
||||
}
|
||||
|
||||
public HashSet<Entity<T>> GetEntitiesInRange<T>(MapCoordinates coordinates, float range, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
public HashSet<Entity<T>> GetEntitiesInRange<T>(MapCoordinates coordinates, float range) where T : IComponent
|
||||
{
|
||||
var entities = new HashSet<Entity<T>>();
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities, flags);
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities);
|
||||
return entities;
|
||||
}
|
||||
|
||||
@@ -749,15 +477,40 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
#region MapId
|
||||
|
||||
public void GetEntitiesInRange(Type type, MapId mapId, Vector2 worldPos, float range, HashSet<Entity<IComponent>> entities, LookupFlags flags = DefaultFlags)
|
||||
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)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
var circle = new PhysShapeCircle(range, worldPos);
|
||||
GetEntitiesIntersecting(type, mapId, circle, entities, flags);
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
GetEntitiesIntersecting(type, mapId, worldAABB, entities);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
@@ -768,41 +521,18 @@ 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, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
public void GetEntitiesInRange<T>(MapId mapId, Vector2 worldPos, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
{
|
||||
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");
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
GetEntitiesIntersecting(mapId, shape, entities, flags);
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
GetEntitiesIntersecting(mapId, worldAABB, entities);
|
||||
}
|
||||
|
||||
#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,11 +11,9 @@ 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;
|
||||
|
||||
@@ -74,14 +72,11 @@ 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;
|
||||
@@ -113,6 +108,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<BroadphaseComponent, EntityTerminatingEvent>(OnBroadphaseTerminating);
|
||||
SubscribeLocalEvent<BroadphaseComponent, ComponentAdd>(OnBroadphaseAdd);
|
||||
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
||||
SubscribeLocalEvent<MapChangedEvent>(OnMapChange);
|
||||
|
||||
SubscribeLocalEvent<MoveEvent>(OnMove);
|
||||
@@ -191,6 +187,12 @@ 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>(
|
||||
@@ -366,7 +368,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
FixturesComponent manager,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
DebugTools.Assert(!_container.IsEntityOrParentInContainer(body.Owner, null, xform));
|
||||
DebugTools.Assert(!_container.IsEntityOrParentInContainer(body.Owner, null, xform, null, xformQuery));
|
||||
DebugTools.Assert(xform.Broadphase == null || xform.Broadphase == new BroadphaseData(broadphase.Owner, physicsMap.Owner, body.CanCollide, body.BodyType == BodyType.Static));
|
||||
DebugTools.Assert(broadphase.Owner == broadUid);
|
||||
|
||||
@@ -432,7 +434,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 BroadphaseData(broadUid, EntityUid.Invalid, false, staticBody);
|
||||
xform.Broadphase ??= new(broadUid, default, false, staticBody);
|
||||
(staticBody ? broadphase.StaticSundriesTree : broadphase.SundriesTree).AddOrUpdate(uid, aabb);
|
||||
}
|
||||
|
||||
@@ -841,7 +843,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
TransformComponent xform,
|
||||
[NotNullWhen(true)] out BroadphaseComponent? broadphase)
|
||||
{
|
||||
if (xform.MapID == MapId.Nullspace || _container.IsEntityOrParentInContainer(xform.Owner, null, xform))
|
||||
if (xform.MapID == MapId.Nullspace || _container.IsEntityOrParentInContainer(xform.Owner, null, xform, null, _xformQuery))
|
||||
{
|
||||
broadphase = null;
|
||||
return false;
|
||||
|
||||
@@ -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(Entity<EyeComponent?> entity)
|
||||
public void UpdateEye(EyeComponent component)
|
||||
{
|
||||
var component = entity.Comp;
|
||||
if (!Resolve(entity, ref component))
|
||||
if (component._eye == null)
|
||||
return;
|
||||
|
||||
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;
|
||||
component._eye.Offset = component.Offset;
|
||||
component._eye.DrawFov = component.DrawFov;
|
||||
component._eye.Rotation = component.Rotation;
|
||||
component._eye.Zoom = component.Zoom;
|
||||
}
|
||||
|
||||
public void SetOffset(EntityUid uid, Vector2 value, EyeComponent? eyeComponent = null)
|
||||
@@ -31,7 +31,10 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.Offset = value;
|
||||
eyeComponent.Eye.Offset = value;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
eyeComponent._eye.Offset = value;
|
||||
}
|
||||
Dirty(uid, eyeComponent);
|
||||
}
|
||||
|
||||
@@ -44,23 +47,13 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.DrawFov = value;
|
||||
eyeComponent.Eye.DrawFov = value;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
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))
|
||||
@@ -70,7 +63,10 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.Rotation = rotation;
|
||||
eyeComponent.Eye.Rotation = rotation;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
eyeComponent._eye.Rotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTarget(EntityUid uid, EntityUid? value, EyeComponent? eyeComponent = null)
|
||||
@@ -94,7 +90,10 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
return;
|
||||
|
||||
eyeComponent.Zoom = value;
|
||||
eyeComponent.Eye.Zoom = value;
|
||||
if (eyeComponent._eye != null)
|
||||
{
|
||||
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, xform.MapUid!.Value);
|
||||
_transform.SetParent(entity, xform, _mapManager.GetMapEntityIdOrThrow(xform.MapID));
|
||||
var ev = new ChangedGridEvent(entity, oldGridId, null);
|
||||
RaiseLocalEvent(entity, ref ev);
|
||||
}
|
||||
|
||||
@@ -429,7 +429,8 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
private void OnGridInit(EntityUid uid, MapGridComponent component, ComponentInit args)
|
||||
{
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xform = xformQuery.GetComponent(uid);
|
||||
|
||||
// Force networkedmapmanager to send it due to non-ECS legacy code.
|
||||
var curTick = _timing.CurTick;
|
||||
@@ -442,7 +443,7 @@ public abstract partial class SharedMapSystem
|
||||
component.LastTileModifiedTick = curTick;
|
||||
|
||||
if (xform.MapUid != null && xform.MapUid != uid)
|
||||
_transform.SetParent(uid, xform, xform.MapUid.Value);
|
||||
_transform.SetParent(uid, xform, xform.MapUid.Value, xformQuery);
|
||||
|
||||
if (!HasComp<MapComponent>(uid))
|
||||
{
|
||||
@@ -558,10 +559,7 @@ public abstract partial class SharedMapSystem
|
||||
internal void RegenerateCollision(EntityUid uid, MapGridComponent grid, IReadOnlySet<MapChunk> chunks)
|
||||
{
|
||||
if (HasComp<MapComponent>(uid))
|
||||
{
|
||||
ClearEmptyMapChunks(uid, grid, chunks);
|
||||
return;
|
||||
}
|
||||
|
||||
var chunkRectangles = new Dictionary<MapChunk, List<Box2i>>(chunks.Count);
|
||||
var removedChunks = new List<MapChunk>();
|
||||
@@ -624,23 +622,6 @@ public abstract partial class SharedMapSystem
|
||||
RaiseLocalEvent(ref ev);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variation of <see cref="RegenerateCollision(Robust.Shared.GameObjects.EntityUid,Robust.Shared.Map.Components.MapGridComponent,Robust.Shared.Map.MapChunk)"/>
|
||||
/// that only simply removes empty chunks. Intended for use with "planet-maps", which have no grid fixtures.
|
||||
/// </summary>
|
||||
private void ClearEmptyMapChunks(EntityUid uid, MapGridComponent grid, IReadOnlySet<MapChunk> modified)
|
||||
{
|
||||
foreach (var chunk in modified)
|
||||
{
|
||||
DebugTools.Assert(chunk.FilledTiles >= 0);
|
||||
if (chunk.FilledTiles > 0)
|
||||
continue;
|
||||
|
||||
DebugTools.AssertEqual(chunk.Fixtures.Count, 0, "maps should not have grid-chunk fixtures");
|
||||
RemoveChunk(uid, grid, chunk.Indices);
|
||||
}
|
||||
}
|
||||
|
||||
#region TileAccess
|
||||
|
||||
public TileRef GetTileRef(EntityUid uid, MapGridComponent grid, MapCoordinates coords)
|
||||
@@ -688,8 +669,9 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
public IEnumerable<TileRef> GetAllTiles(EntityUid uid, MapGridComponent grid, bool ignoreEmpty = true)
|
||||
{
|
||||
foreach (var chunk in grid.Chunks.Values)
|
||||
foreach (var kvChunk in grid.Chunks)
|
||||
{
|
||||
var chunk = kvChunk.Value;
|
||||
for (ushort x = 0; x < grid.ChunkSize; x++)
|
||||
{
|
||||
for (ushort y = 0; y < grid.ChunkSize; y++)
|
||||
@@ -740,7 +722,7 @@ public abstract partial class SharedMapSystem
|
||||
if (tiles.Count == 0)
|
||||
return;
|
||||
|
||||
var modified = new HashSet<MapChunk>(Math.Max(1, tiles.Count / grid.ChunkSize));
|
||||
var chunks = new HashSet<MapChunk>(Math.Max(1, tiles.Count / grid.ChunkSize));
|
||||
|
||||
foreach (var (gridIndices, tile) in tiles)
|
||||
{
|
||||
@@ -748,7 +730,7 @@ public abstract partial class SharedMapSystem
|
||||
if (!grid.Chunks.TryGetValue(chunkIndex, out var chunk))
|
||||
{
|
||||
if (tile.IsEmpty)
|
||||
continue;
|
||||
return;
|
||||
|
||||
grid.Chunks[chunkIndex] = chunk = new MapChunk(chunkIndex.X, chunkIndex.Y, grid.ChunkSize)
|
||||
{
|
||||
@@ -757,67 +739,24 @@ public abstract partial class SharedMapSystem
|
||||
}
|
||||
|
||||
var offset = chunk.GridTileToChunkTile(gridIndices);
|
||||
chunks.Add(chunk);
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
if (SetChunkTile(uid, grid, chunk, (ushort)offset.X, (ushort)offset.Y, tile))
|
||||
modified.Add(chunk);
|
||||
SetChunkTile(uid, grid, chunk, (ushort)offset.X, (ushort)offset.Y, tile);
|
||||
}
|
||||
|
||||
foreach (var chunk in modified)
|
||||
foreach (var chunk in chunks)
|
||||
{
|
||||
chunk.SuppressCollisionRegeneration = false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
RegenerateCollision(uid, grid, chunks);
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated localArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var localAABB = localArea.CalcBoundingBox();
|
||||
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
yield return tileRef;
|
||||
}
|
||||
return GetLocalTilesIntersecting(uid, grid, localAABB, ignoreEmpty, predicate);
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated worldArea, bool ignoreEmpty = true,
|
||||
@@ -826,11 +765,9 @@ public abstract partial class SharedMapSystem
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localArea);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
|
||||
{
|
||||
yield return tileRef;
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,11 +777,46 @@ public abstract partial class SharedMapSystem
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localArea);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
|
||||
{
|
||||
yield return 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,25 +966,12 @@ 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);
|
||||
@@ -1029,32 +988,22 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
public IEnumerable<EntityUid> GetLocalAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 localAABB)
|
||||
{
|
||||
var enumerator = new TilesEnumerator(this, true, null, uid, grid, localAABB);
|
||||
|
||||
while (enumerator.MoveNext(out var tileRef))
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localAABB, true, null))
|
||||
{
|
||||
var anchoredEnumerator = GetAnchoredEntitiesEnumerator(uid, grid, tileRef.GridIndices);
|
||||
|
||||
while (anchoredEnumerator.MoveNext(out var ent))
|
||||
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
|
||||
{
|
||||
yield return ent.Value;
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 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 tile in GetTilesIntersecting(uid, grid, worldAABB))
|
||||
{
|
||||
var anchoredEnumerator = GetAnchoredEntitiesEnumerator(uid, grid, tileRef.GridIndices);
|
||||
|
||||
while (anchoredEnumerator.MoveNext(out var ent))
|
||||
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
|
||||
{
|
||||
yield return ent.Value;
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1310,9 +1259,6 @@ 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);
|
||||
@@ -1355,36 +1301,6 @@ 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);
|
||||
@@ -1441,98 +1357,4 @@ 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,6 +10,7 @@ public abstract partial class SharedMapSystem
|
||||
{
|
||||
private void InitializeMap()
|
||||
{
|
||||
SubscribeLocalEvent<MapComponent, ComponentAdd>(OnMapAdd);
|
||||
SubscribeLocalEvent<MapComponent, ComponentInit>(OnMapInit);
|
||||
SubscribeLocalEvent<MapComponent, ComponentShutdown>(OnMapRemoved);
|
||||
SubscribeLocalEvent<MapComponent, ComponentHandleState>(OnMapHandleState);
|
||||
@@ -42,6 +43,8 @@ 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,7 +12,6 @@ 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!;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user