mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
554e0777b1 | ||
|
|
21b7c5f93e | ||
|
|
9e5c1e9c95 | ||
|
|
6825f09fb9 | ||
|
|
58e3a4eb4a | ||
|
|
9a342f0d11 | ||
|
|
f754ddb96d | ||
|
|
7feede0d95 | ||
|
|
ea152366e3 | ||
|
|
ab47d4e009 | ||
|
|
81b2a3825e | ||
|
|
56d850f389 | ||
|
|
b737ecf9b3 | ||
|
|
ed5223b592 | ||
|
|
f87012e681 | ||
|
|
54529fdbe3 | ||
|
|
1745a12e5a | ||
|
|
d201d787b7 | ||
|
|
904ddea274 | ||
|
|
6b6ec844e8 | ||
|
|
f24d18f470 | ||
|
|
77654a1628 | ||
|
|
f3af813b57 | ||
|
|
0623baedcf | ||
|
|
2ade6c04c5 | ||
|
|
a9df9097c1 | ||
|
|
755dac719f | ||
|
|
7095a58685 | ||
|
|
16e68a4351 | ||
|
|
0152f9d1d8 | ||
|
|
16d916796a | ||
|
|
e865157432 | ||
|
|
24d5ce4bd4 | ||
|
|
662195e4ff | ||
|
|
d00fd6f736 | ||
|
|
2d58c1071d | ||
|
|
a9db89d023 | ||
|
|
684cabf3e6 | ||
|
|
a4f51f0cd9 | ||
|
|
a8ddd837c8 | ||
|
|
82aace7997 | ||
|
|
01ce244b7b | ||
|
|
58aa6e5c75 | ||
|
|
4818c3aab4 | ||
|
|
3b6adeb5ff | ||
|
|
889b8351be | ||
|
|
ac37b0a131 | ||
|
|
f6f1fc425a | ||
|
|
7476628840 | ||
|
|
668cdbe76b | ||
|
|
a0a6e9b111 | ||
|
|
06d28f04e6 | ||
|
|
57897161d0 | ||
|
|
c4c528478e |
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
143
RELEASE-NOTES.md
143
RELEASE-NOTES.md
@@ -54,6 +54,149 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 171.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Change PlaceNextTo method names to be more descriptive.
|
||||
* Rename RefreshRelay for joints to SetRelay to match its behaviour.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PVS error spam for joint relays not being cleaned up.
|
||||
|
||||
### Other
|
||||
|
||||
* Set EntityLastModifiedTick on entity spawn.
|
||||
|
||||
|
||||
## 170.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed obsolete methods and properties in VisibilitySystem, SharedContainerSystem and MetaDataComponent.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed duplicate command error.
|
||||
* Fixed not being able to delete individual entities with the delete command.
|
||||
|
||||
### Other
|
||||
|
||||
* FileLogHandler logs can now be deleted while the engine is running.
|
||||
|
||||
|
||||
## 169.0.1
|
||||
|
||||
### Other
|
||||
|
||||
* The client now knows about registered server-side toolshed commands.
|
||||
|
||||
## 169.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Entity<T> has been introduced to hold a component and its owning entity. Some methods that returned and accepted components directly have been removed or obsoleted to reflect this.
|
||||
|
||||
### Other
|
||||
|
||||
* By-value events may now be subscribed to by-ref.
|
||||
* The manifest's assemblyPrefix value is now respected on the server.
|
||||
|
||||
|
||||
## 168.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The Component.OnRemove method has been removed. Use SubscribeLocalEvent<TComp, ComponentRemove>(OnRemove) from an EntitySystem instead.
|
||||
|
||||
|
||||
## 167.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Remove ComponentExtensions.
|
||||
* Remove ContainerHelpers.
|
||||
* Change some TransformSystem methods to fix clientside lerping.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed PVS bugs from dropped entity states.
|
||||
|
||||
### Other
|
||||
|
||||
* Add more joint debug asserts.
|
||||
|
||||
|
||||
## 166.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* EntityUid-NetEntity conversion methods now return null when given a null value, rather than returning an invalid id.
|
||||
* ExpandPvsEvent now defaults to using null lists to reduce allocations.
|
||||
* Various component lifestage related methods have been moved from the `Component` class to `EntityManager`.
|
||||
* Session/client specific PVS overrides are now always recursive, which means that all children of the overriden entity will also get sent.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a SortedSet yaml serializer.
|
||||
|
||||
### Other
|
||||
|
||||
* AddComponentUninitialized is now marked as obsolete and will be removed in the future.
|
||||
* DebugTools.AssertOwner() now accepts null components.
|
||||
|
||||
|
||||
## 165.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The arguments of `SplitContainer`s resize-finished event have changed.
|
||||
|
||||
### New features
|
||||
|
||||
* The YAML validator now checks the default values of ProtoId<T> and EntProtoId data fields.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* The minimum draggable area of split containers now blocks mouse inputs.
|
||||
|
||||
|
||||
## 164.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Make automatic component states infer cloneData.
|
||||
* Removed cloneData from AutoNetworkedFieldAttribute. This is now automatically inferred.
|
||||
|
||||
### Internal
|
||||
|
||||
* Reduce Transform GetComponents in RecursiveDeleteEntity.
|
||||
|
||||
|
||||
## 163.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Moved TimedDespawn to engine for a component that deletes the attached entity after a timer has elapsed.
|
||||
|
||||
### New features
|
||||
|
||||
* Add ExecuteCommand for integration tests.
|
||||
* Allow adding / removing widgets of cub-controls.
|
||||
* Give maps / grids a default name to help with debugging.
|
||||
* Use ToPrettyString in component resolve errors to help with debugging.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix console backspace exception.
|
||||
* Fix rendering invalid maps spamming exceptions every frame.
|
||||
|
||||
### Internal
|
||||
|
||||
* Move ClientGameStatemanager local variables to fields to avoid re-allocating every tick.
|
||||
|
||||
|
||||
## 162.2.1
|
||||
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@ cmd-error-dir-not-found = Could not find directory: {$dir}.
|
||||
cmd-failure-no-attached-entity = There is no entity attached to this shell.
|
||||
|
||||
## 'help' command
|
||||
cmd-oldhelp-desc = Display general help or help text for a specific command
|
||||
cmd-oldhelp-help = Usage: help [command name]
|
||||
cmd-help-desc = Display general help or help text for a specific command
|
||||
cmd-help-help = Usage: help [command name]
|
||||
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
|
||||
|
||||
cmd-oldhelp-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
cmd-oldhelp-unknown = Unknown command: { $command }
|
||||
cmd-oldhelp-top = { $command } - { $description }
|
||||
cmd-oldhelp-invalid-args = Invalid amount of arguments.
|
||||
cmd-oldhelp-arg-cmdname = [command name]
|
||||
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
cmd-help-unknown = Unknown command: { $command }
|
||||
cmd-help-top = { $command } - { $description }
|
||||
cmd-help-invalid-args = Invalid amount of arguments.
|
||||
cmd-help-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Gets or sets a CVar.
|
||||
|
||||
@@ -23,16 +23,6 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
"Make sure that methods subscribing to a ref event have the ref keyword for the event argument."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor ByValueEventSubscribedByRefRule = new(
|
||||
Diagnostics.IdValueEventRaisedByRef,
|
||||
"Value event subscribed to by-ref",
|
||||
"Tried to subscribe to a value event '{0}' by-ref.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure that methods subscribing to value events do not have the ref keyword for the event argument."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor ByRefEventRaisedByValueRule = new(
|
||||
Diagnostics.IdByRefEventRaisedByValue,
|
||||
"By-ref event raised by value",
|
||||
@@ -55,7 +45,6 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
ByRefEventSubscribedByValueRule,
|
||||
ByValueEventSubscribedByRefRule,
|
||||
ByRefEventRaisedByValueRule,
|
||||
ByValueEventRaisedByRefRule
|
||||
);
|
||||
@@ -64,71 +53,9 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterOperationAction(CheckEventSubscription, OperationKind.Invocation);
|
||||
context.RegisterOperationAction(CheckEventRaise, OperationKind.Invocation);
|
||||
}
|
||||
|
||||
private void CheckEventSubscription(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation operation)
|
||||
return;
|
||||
|
||||
var subscribeMethods = context.Compilation
|
||||
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
|
||||
.GetMembers()
|
||||
.Where(m => m.Name.Contains("SubscribeLocalEvent"))
|
||||
.Cast<IMethodSymbol>();
|
||||
|
||||
if (subscribeMethods == null)
|
||||
return;
|
||||
|
||||
if (!subscribeMethods.Any(m => m.Equals(operation.TargetMethod.OriginalDefinition, Default)))
|
||||
return;
|
||||
|
||||
var typeArguments = operation.TargetMethod.TypeArguments;
|
||||
if (typeArguments.Length < 1 || typeArguments.Length > 2)
|
||||
return;
|
||||
|
||||
if (operation.Arguments.First().Value is not IDelegateCreationOperation delegateCreation)
|
||||
return;
|
||||
|
||||
if (delegateCreation.Target is not IMethodReferenceOperation methodReference)
|
||||
return;
|
||||
|
||||
var eventParameter = methodReference.Method.Parameters.LastOrDefault();
|
||||
if (eventParameter == null)
|
||||
return;
|
||||
|
||||
ITypeSymbol eventArgument;
|
||||
switch (typeArguments.Length)
|
||||
{
|
||||
case 1:
|
||||
eventArgument = typeArguments[0];
|
||||
break;
|
||||
case 2:
|
||||
eventArgument = typeArguments[1];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var byRefAttribute = context.Compilation.GetTypeByMetadataName(ByRefAttribute);
|
||||
if (byRefAttribute == null)
|
||||
return;
|
||||
|
||||
var isByRefEventType = eventArgument
|
||||
.GetAttributes()
|
||||
.Any(attribute => attribute.AttributeClass?.Equals(byRefAttribute, Default) ?? false);
|
||||
var parameterIsRef = eventParameter.RefKind == RefKind.Ref;
|
||||
|
||||
if (isByRefEventType != parameterIsRef)
|
||||
{
|
||||
var descriptor = isByRefEventType ? ByRefEventSubscribedByValueRule : ByValueEventSubscribedByRefRule;
|
||||
var diagnostic = Diagnostic.Create(descriptor, operation.Syntax.GetLocation(), eventArgument);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckEventRaise(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation operation)
|
||||
|
||||
@@ -18,7 +18,6 @@ public static class Diagnostics
|
||||
public const string IdInvalidNotNullableFlagType = "RA0011";
|
||||
public const string IdNotNullableFlagValueType = "RA0012";
|
||||
public const string IdByRefEventSubscribedByValue = "RA0013";
|
||||
public const string IdValueEventSubscribedByRef = "RA0014";
|
||||
public const string IdByRefEventRaisedByValue = "RA0015";
|
||||
public const string IdValueEventRaisedByRef = "RA0016";
|
||||
public const string IdDataDefinitionPartial = "RA0017";
|
||||
|
||||
@@ -54,7 +54,7 @@ public class RecursiveMoveBenchmark
|
||||
var mapSys = _entMan.System<SharedMapSystem>();
|
||||
var mapId = mapMan.CreateMap();
|
||||
var map = mapMan.GetMapEntityId(mapId);
|
||||
var gridComp = mapMan.CreateGrid(mapId);
|
||||
var gridComp = mapMan.CreateGridEntity(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
_mapCoords = new EntityCoordinates(map, 100, 100);
|
||||
|
||||
@@ -26,10 +26,7 @@ namespace Robust.Client.Console.Commands
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
var componentName = args[1];
|
||||
|
||||
var component = (Component) _componentFactory.GetComponent(componentName);
|
||||
|
||||
component.Owner = entity;
|
||||
|
||||
var component = _componentFactory.GetComponent(componentName);
|
||||
_entityManager.AddComponent(entity, component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#if DEBUG
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -8,7 +9,6 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
@@ -19,6 +19,7 @@ namespace Robust.Client.Debugging
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterface = default!;
|
||||
[Dependency] private readonly MapSystem _mapSystem = default!;
|
||||
|
||||
private Label? _label;
|
||||
|
||||
@@ -70,7 +71,7 @@ namespace Robust.Client.Debugging
|
||||
return;
|
||||
}
|
||||
|
||||
var tile = grid.GetTileRef(spot);
|
||||
var tile = _mapSystem.GetTileRef(gridUid, grid, spot);
|
||||
_label.Position = mouseSpot.Position + new Vector2(32, 0);
|
||||
|
||||
if (_hovered?.GridId == gridUid && _hovered?.Tile == tile) return;
|
||||
@@ -79,7 +80,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
var text = new StringBuilder();
|
||||
|
||||
foreach (var ent in grid.GetAnchoredEntities(spot))
|
||||
foreach (var ent in _mapSystem.GetAnchoredEntities(gridUid, grid, spot))
|
||||
{
|
||||
if (EntityManager.TryGetComponent<MetaDataComponent>(ent, out var meta))
|
||||
{
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
@@ -207,6 +206,7 @@ namespace Robust.Client.Debugging
|
||||
private readonly Font _font;
|
||||
|
||||
private HashSet<Joint> _drawnJoints = new();
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public PhysicsDebugOverlay(IEntityManager entityManager, IEyeManager eyeManager, IInputManager inputManager, IMapManager mapManager, IPlayerManager playerManager, IResourceCache cache, DebugPhysicsSystem system, EntityLookupSystem lookup, SharedPhysicsSystem physicsSystem)
|
||||
{
|
||||
@@ -231,32 +231,33 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody)) continue;
|
||||
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody.Owner);
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody);
|
||||
var comp = physBody.Comp;
|
||||
|
||||
const float AlphaModifier = 0.2f;
|
||||
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody.Owner).Fixtures.Values)
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody).Fixtures.Values)
|
||||
{
|
||||
// Invalid shape - Box2D doesn't check for IsSensor but we will for sanity.
|
||||
if (physBody.BodyType == BodyType.Dynamic && fixture.Density == 0f && fixture.Hard)
|
||||
if (comp.BodyType == BodyType.Dynamic && fixture.Density == 0f && fixture.Hard)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, Color.Red.WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if (!physBody.CanCollide)
|
||||
else if (!comp.CanCollide)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.5f, 0.5f, 0.3f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if (physBody.BodyType == BodyType.Static)
|
||||
else if (comp.BodyType == BodyType.Static)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.5f, 0.9f, 0.5f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if ((physBody.BodyType & (BodyType.Kinematic | BodyType.KinematicController)) != 0x0)
|
||||
else if ((comp.BodyType & (BodyType.Kinematic | BodyType.KinematicController)) != 0x0)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.5f, 0.5f, 0.9f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
else if (!physBody.Awake)
|
||||
else if (!comp.Awake)
|
||||
{
|
||||
DrawShape(worldHandle, fixture, xform, new Color(0.6f, 0.6f, 0.6f).WithAlpha(AlphaModifier));
|
||||
}
|
||||
@@ -275,15 +276,18 @@ namespace Robust.Client.Debugging
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
var color = Color.Purple.WithAlpha(Alpha);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(physBody.Owner);
|
||||
worldHandle.DrawCircle(Transform.Mul(transform, physBody.LocalCenter), 0.2f, color);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(physBody);
|
||||
worldHandle.DrawCircle(Transform.Mul(transform, physBody.Comp.LocalCenter), 0.2f, color);
|
||||
}
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, viewBounds))
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(mapId, viewBounds, ref _grids);
|
||||
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var physBody = _entityManager.GetComponent<PhysicsComponent>(grid.Owner);
|
||||
var physBody = _entityManager.GetComponent<PhysicsComponent>(grid);
|
||||
var color = Color.Orange.WithAlpha(Alpha);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(grid.Owner);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(grid);
|
||||
worldHandle.DrawCircle(Transform.Mul(transform, physBody.LocalCenter), 1f, color);
|
||||
}
|
||||
}
|
||||
@@ -292,14 +296,14 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody)) continue;
|
||||
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody.Owner);
|
||||
var xform = _physicsSystem.GetPhysicsTransform(physBody);
|
||||
|
||||
const float AlphaModifier = 0.2f;
|
||||
Box2? aabb = null;
|
||||
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody.Owner).Fixtures.Values)
|
||||
foreach (var fixture in _entityManager.GetComponent<FixturesComponent>(physBody).Fixtures.Values)
|
||||
{
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
{
|
||||
@@ -318,10 +322,11 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
_drawnJoints.Clear();
|
||||
|
||||
foreach (var jointComponent in _entityManager.EntityQuery<JointComponent>(true))
|
||||
var query = _entityManager.AllEntityQueryEnumerator<JointComponent>();
|
||||
while (query.MoveNext(out var uid, out var jointComponent))
|
||||
{
|
||||
if (jointComponent.JointCount == 0 ||
|
||||
!_entityManager.TryGetComponent(jointComponent.Owner, out TransformComponent? xf1) ||
|
||||
!_entityManager.TryGetComponent(uid, out TransformComponent? xf1) ||
|
||||
!viewAABB.Contains(xf1.WorldPosition)) continue;
|
||||
|
||||
foreach (var (_, joint) in jointComponent.Joints)
|
||||
@@ -370,28 +375,31 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.ShapeInfo) != 0x0)
|
||||
{
|
||||
var hoverBodies = new List<PhysicsComponent>();
|
||||
var hoverBodies = new List<Entity<PhysicsComponent>>();
|
||||
var bounds = Box2.UnitCentered.Translated(_eyeManager.PixelToMap(mousePos.Position).Position);
|
||||
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, bounds))
|
||||
{
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
hoverBodies.Add(physBody);
|
||||
var uid = physBody.Owner;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(uid)) continue;
|
||||
hoverBodies.Add((uid, physBody));
|
||||
}
|
||||
|
||||
var lineHeight = _font.GetLineHeight(1f);
|
||||
var drawPos = mousePos.Position + new Vector2(20, 0) + new Vector2(0, -(hoverBodies.Count * 4 * lineHeight / 2f));
|
||||
int row = 0;
|
||||
|
||||
foreach (var body in hoverBodies)
|
||||
foreach (var bodyEnt in hoverBodies)
|
||||
{
|
||||
if (body != hoverBodies[0])
|
||||
if (bodyEnt != hoverBodies[0])
|
||||
{
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), "------");
|
||||
row++;
|
||||
}
|
||||
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {body.Owner}");
|
||||
var body = bodyEnt.Comp;
|
||||
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {bodyEnt.Owner}");
|
||||
row++;
|
||||
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Layer: {Convert.ToString(body.CollisionLayer, 2)}");
|
||||
row++;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Robust.Client.WebViewHook;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
||||
@@ -287,78 +287,6 @@ namespace Robust.Client
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceManifestData LoadResourceManifest()
|
||||
{
|
||||
// Parses /manifest.yml for game-specific settings that cannot be exclusively set up by content code.
|
||||
if (!_resourceCache.TryContentFileRead("/manifest.yml", out var stream))
|
||||
return ResourceManifestData.Default;
|
||||
|
||||
var yamlStream = new YamlStream();
|
||||
using (stream)
|
||||
{
|
||||
using var streamReader = new StreamReader(stream, EncodingHelpers.UTF8);
|
||||
yamlStream.Load(streamReader);
|
||||
}
|
||||
|
||||
if (yamlStream.Documents.Count == 0)
|
||||
return ResourceManifestData.Default;
|
||||
|
||||
if (yamlStream.Documents.Count != 1 || yamlStream.Documents[0].RootNode is not YamlMappingNode mapping)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Expected a single YAML document with root mapping for /manifest.yml");
|
||||
}
|
||||
|
||||
var modules = ReadStringArray(mapping, "modules") ?? Array.Empty<string>();
|
||||
|
||||
string? assemblyPrefix = null;
|
||||
if (mapping.TryGetNode("assemblyPrefix", out var prefixNode))
|
||||
assemblyPrefix = prefixNode.AsString();
|
||||
|
||||
string? defaultWindowTitle = null;
|
||||
if (mapping.TryGetNode("defaultWindowTitle", out var winTitleNode))
|
||||
defaultWindowTitle = winTitleNode.AsString();
|
||||
|
||||
string? windowIconSet = null;
|
||||
if (mapping.TryGetNode("windowIconSet", out var iconSetNode))
|
||||
windowIconSet = iconSetNode.AsString();
|
||||
|
||||
string? splashLogo = null;
|
||||
if (mapping.TryGetNode("splashLogo", out var splashNode))
|
||||
splashLogo = splashNode.AsString();
|
||||
|
||||
bool autoConnect = true;
|
||||
if (mapping.TryGetNode("autoConnect", out var autoConnectNode))
|
||||
autoConnect = autoConnectNode.AsBool();
|
||||
|
||||
var clientAssemblies = ReadStringArray(mapping, "clientAssemblies");
|
||||
|
||||
return new ResourceManifestData(
|
||||
modules,
|
||||
assemblyPrefix,
|
||||
defaultWindowTitle,
|
||||
windowIconSet,
|
||||
splashLogo,
|
||||
autoConnect,
|
||||
clientAssemblies
|
||||
);
|
||||
|
||||
static string[]? ReadStringArray(YamlMappingNode mapping, string key)
|
||||
{
|
||||
if (!mapping.TryGetNode(key, out var node))
|
||||
return null;
|
||||
|
||||
var sequence = (YamlSequenceNode)node;
|
||||
var array = new string[sequence.Children.Count];
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = sequence[i].AsString();
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool StartupSystemSplash(
|
||||
GameControllerOptions options,
|
||||
Func<ILogHandler>? logHandlerFactory,
|
||||
@@ -457,7 +385,7 @@ namespace Robust.Client
|
||||
_modLoader.VerifierExtraLoadHandler = VerifierExtraLoadHandler;
|
||||
}
|
||||
|
||||
_resourceManifest = LoadResourceManifest();
|
||||
_resourceManifest = ResourceManifestData.LoadResourceManifest(_resourceCache);
|
||||
|
||||
{
|
||||
// Handle GameControllerOptions implicit CVar overrides.
|
||||
@@ -786,20 +714,6 @@ namespace Robust.Client
|
||||
_clydeAudio.Shutdown();
|
||||
}
|
||||
|
||||
private sealed record ResourceManifestData(
|
||||
string[] Modules,
|
||||
string? AssemblyPrefix,
|
||||
string? DefaultWindowTitle,
|
||||
string? WindowIconSet,
|
||||
string? SplashLogo,
|
||||
bool AutoConnect,
|
||||
string[]? ClientAssemblies
|
||||
)
|
||||
{
|
||||
public static readonly ResourceManifestData Default =
|
||||
new ResourceManifestData(Array.Empty<string>(), null, null, null, null, true, null);
|
||||
}
|
||||
|
||||
public event Action<FrameEventArgs>? TickUpdateOverride;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Prometheus;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
@@ -43,9 +42,9 @@ namespace Robust.Client.GameObjects
|
||||
base.FlushEntities();
|
||||
}
|
||||
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName)
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, out MetaDataComponent metadata)
|
||||
{
|
||||
return base.CreateEntity(prototypeName);
|
||||
return base.CreateEntity(prototypeName, out metadata);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
|
||||
@@ -86,21 +85,23 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dirty(EntityUid uid, Component component, MetaDataComponent? meta = null)
|
||||
public override void Dirty(EntityUid uid, IComponent component, MetaDataComponent? meta = null)
|
||||
{
|
||||
Dirty(new Entity<IComponent>(uid, component), meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dirty<T>(Entity<T> ent, MetaDataComponent? meta = null)
|
||||
{
|
||||
// Client only dirties during prediction
|
||||
if (_gameTiming.InPrediction)
|
||||
base.Dirty(uid, component, meta);
|
||||
base.Dirty(ent, meta);
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
public override EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metaDataComponent = null)
|
||||
{
|
||||
if (uid == null)
|
||||
return null;
|
||||
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == uid)
|
||||
return base.ToPrettyString(uid).Value with { Session = _playerManager.LocalPlayer.Session };
|
||||
return base.ToPrettyString(uid) with { Session = _playerManager.LocalPlayer.Session };
|
||||
|
||||
return base.ToPrettyString(uid);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using static Robust.Client.Animations.AnimationPlaybackShared;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -21,42 +19,5 @@ namespace Robust.Client.GameObjects
|
||||
= new();
|
||||
|
||||
internal bool HasPlayingAnimation = false;
|
||||
|
||||
/// <summary>
|
||||
/// Start playing an animation.
|
||||
/// </summary>
|
||||
/// <param name="animation">The animation to play.</param>
|
||||
/// <param name="key">
|
||||
/// The key for this animation play. This key can be used to stop playback short later.
|
||||
/// </param>
|
||||
[Obsolete("Use AnimationPlayerSystem.Play() instead")]
|
||||
public void Play(Animation animation, string key)
|
||||
{
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AnimationPlayerSystem>().AddComponent(this);
|
||||
var playback = new AnimationPlayback(animation);
|
||||
|
||||
PlayingAnimations.Add(key, playback);
|
||||
}
|
||||
|
||||
[Obsolete("Use AnimationPlayerSystem.HasRunningAnimation() instead")]
|
||||
public bool HasRunningAnimation(string key)
|
||||
{
|
||||
return PlayingAnimations.ContainsKey(key);
|
||||
}
|
||||
|
||||
[Obsolete("Use AnimationPlayerSystem.Stop() instead")]
|
||||
public void Stop(string key)
|
||||
{
|
||||
PlayingAnimations.Remove(key);
|
||||
}
|
||||
|
||||
[Obsolete("Temporary method until the event is replaced with eventbus")]
|
||||
internal void AnimationComplete(string key)
|
||||
{
|
||||
AnimationCompleted?.Invoke(key);
|
||||
}
|
||||
|
||||
[Obsolete("Use AnimationCompletedEvent instead")]
|
||||
public event Action<string>? AnimationCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public sealed class AnimationPlayerSystem : EntitySystem, IPostInjectInit
|
||||
{
|
||||
private readonly List<AnimationPlayerComponent> _activeAnimations = new();
|
||||
private readonly List<Entity<AnimationPlayerComponent>> _activeAnimations = new();
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
@@ -39,22 +39,22 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Update(uid, anim, frameTime))
|
||||
if (!Update(uid, anim.Comp, frameTime))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_activeAnimations.RemoveSwap(i);
|
||||
i--;
|
||||
anim.HasPlayingAnimation = false;
|
||||
anim.Comp.HasPlayingAnimation = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddComponent(AnimationPlayerComponent component)
|
||||
internal void AddComponent(Entity<AnimationPlayerComponent> ent)
|
||||
{
|
||||
if (component.HasPlayingAnimation) return;
|
||||
_activeAnimations.Add(component);
|
||||
component.HasPlayingAnimation = true;
|
||||
if (ent.Comp.HasPlayingAnimation) return;
|
||||
_activeAnimations.Add(ent);
|
||||
ent.Comp.HasPlayingAnimation = true;
|
||||
}
|
||||
|
||||
private bool Update(EntityUid uid, AnimationPlayerComponent component, float frameTime)
|
||||
@@ -79,7 +79,6 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, new AnimationCompletedEvent {Uid = uid, Key = key}, true);
|
||||
component.AnimationComplete(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -90,22 +89,29 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void Play(EntityUid uid, Animation animation, string key)
|
||||
{
|
||||
var component = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
|
||||
Play(component, animation, key);
|
||||
var component = EnsureComp<AnimationPlayerComponent>(uid);
|
||||
Play(new Entity<AnimationPlayerComponent>(uid, component), animation, key);
|
||||
}
|
||||
|
||||
[Obsolete("Use Play(EntityUid<AnimationPlayerComponent> ent, Animation animation, string key) instead")]
|
||||
public void Play(EntityUid uid, AnimationPlayerComponent? component, Animation animation, string key)
|
||||
{
|
||||
component ??= EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
|
||||
Play(component, animation, key);
|
||||
Play(new Entity<AnimationPlayerComponent>(uid, component), animation, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start playing an animation.
|
||||
/// </summary>
|
||||
[Obsolete("Use Play(EntityUid<AnimationPlayerComponent> ent, Animation animation, string key) instead")]
|
||||
public void Play(AnimationPlayerComponent component, Animation animation, string key)
|
||||
{
|
||||
AddComponent(component);
|
||||
Play(new Entity<AnimationPlayerComponent>(component.Owner, component), animation, key);
|
||||
}
|
||||
|
||||
public void Play(Entity<AnimationPlayerComponent> ent, Animation animation, string key)
|
||||
{
|
||||
AddComponent(ent);
|
||||
var playback = new AnimationPlaybackShared.AnimationPlayback(animation);
|
||||
|
||||
#if DEBUG
|
||||
@@ -117,18 +123,18 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
if (compTrack.ComponentType == null)
|
||||
{
|
||||
_sawmill.Error($"Attempted to play a component animation without any component specified.");
|
||||
_sawmill.Error("Attempted to play a component animation without any component specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetComponent(component.Owner, compTrack.ComponentType, out var animatedComp))
|
||||
if (!EntityManager.TryGetComponent(ent, compTrack.ComponentType, out var animatedComp))
|
||||
{
|
||||
_sawmill.Error(
|
||||
$"Attempted to play a component animation, but the entity {ToPrettyString(component.Owner)} does not have the component to be animated: {compTrack.ComponentType}.");
|
||||
$"Attempted to play a component animation, but the entity {ToPrettyString(ent)} does not have the component to be animated: {compTrack.ComponentType}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsClientSide(component.Owner) || !animatedComp.NetSyncEnabled)
|
||||
if (IsClientSide(ent) || !animatedComp.NetSyncEnabled)
|
||||
continue;
|
||||
|
||||
var reg = _compFact.GetRegistration(animatedComp);
|
||||
@@ -136,12 +142,18 @@ namespace Robust.Client.GameObjects
|
||||
// In principle there is nothing wrong with this, as long as the property of the component being
|
||||
// animated is not part of the networked state and setting it does not dirty the component. Hence only a
|
||||
// warning in debug mode.
|
||||
if (reg.NetID != null)
|
||||
_sawmill.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(component.Owner)}");
|
||||
if (reg.NetID != null && compTrack.Property != null)
|
||||
{
|
||||
if (animatedComp.GetType().GetProperty(compTrack.Property) is { } property &&
|
||||
property.HasCustomAttribute<AutoNetworkedFieldAttribute>())
|
||||
{
|
||||
_sawmill.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(ent)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
component.PlayingAnimations.Add(key, playback);
|
||||
ent.Comp.PlayingAnimations.Add(key, playback);
|
||||
}
|
||||
|
||||
public bool HasRunningAnimation(EntityUid uid, string key)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
@@ -58,6 +59,8 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public GridChunkBoundsOverlay(IEntityManager entManager, IEyeManager eyeManager, IMapManager mapManager)
|
||||
{
|
||||
_entityManager = entManager;
|
||||
@@ -71,13 +74,15 @@ namespace Robust.Client.GameObjects
|
||||
var viewport = args.WorldBounds;
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(currentMap, viewport))
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(currentMap, viewport, ref _grids);
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var worldMatrix = _entityManager.GetComponent<TransformComponent>(grid.Owner).WorldMatrix;
|
||||
var worldMatrix = _entityManager.GetComponent<TransformComponent>(grid).WorldMatrix;
|
||||
worldHandle.SetTransform(worldMatrix);
|
||||
var transform = new Transform(Vector2.Zero, Angle.Zero);
|
||||
|
||||
var chunkEnumerator = grid.GetMapChunks(viewport);
|
||||
var chunkEnumerator = grid.Comp.GetMapChunks(viewport);
|
||||
|
||||
while (chunkEnumerator.MoveNext(out var chunk))
|
||||
{
|
||||
|
||||
@@ -1,51 +1,41 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed partial class TransformSystem
|
||||
{
|
||||
public override void SetLocalPosition(TransformComponent xform, Vector2 value)
|
||||
public override void SetLocalPosition(EntityUid uid, Vector2 value, TransformComponent? xform = null)
|
||||
{
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
if (!XformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
xform.NextPosition = value;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
base.SetLocalPosition(xform, value);
|
||||
ActivateLerp(xform);
|
||||
ActivateLerp(uid, xform);
|
||||
base.SetLocalPosition(uid, value, xform);
|
||||
}
|
||||
|
||||
public override void SetLocalPositionNoLerp(TransformComponent xform, Vector2 value)
|
||||
public override void SetLocalRotation(EntityUid uid, Angle value, TransformComponent? xform = null)
|
||||
{
|
||||
xform.NextPosition = null;
|
||||
xform.LerpParent = EntityUid.Invalid;
|
||||
base.SetLocalPositionNoLerp(xform, value);
|
||||
if (!XformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
xform.NextRotation = value;
|
||||
ActivateLerp(uid, xform);
|
||||
base.SetLocalRotation(uid, value, xform);
|
||||
}
|
||||
|
||||
public override void SetLocalRotationNoLerp(TransformComponent xform, Angle angle)
|
||||
public override void SetLocalPositionRotation(EntityUid uid, Vector2 pos, Angle rot, TransformComponent? xform = null)
|
||||
{
|
||||
xform.NextRotation = null;
|
||||
xform.LerpParent = EntityUid.Invalid;
|
||||
base.SetLocalRotationNoLerp(xform, angle);
|
||||
}
|
||||
if (!XformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
public override void SetLocalRotation(TransformComponent xform, Angle angle)
|
||||
{
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.NextRotation = angle;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
base.SetLocalRotation(xform, angle);
|
||||
ActivateLerp(xform);
|
||||
}
|
||||
|
||||
public override void SetLocalPositionRotation(TransformComponent xform, Vector2 pos, Angle rot)
|
||||
{
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.NextPosition = pos;
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.NextRotation = rot;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
base.SetLocalPositionRotation(xform, pos, rot);
|
||||
ActivateLerp(xform);
|
||||
ActivateLerp(uid, xform);
|
||||
base.SetLocalPositionRotation(uid, pos, rot, xform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
@@ -25,20 +24,15 @@ namespace Robust.Client.GameObjects
|
||||
private const float MinInterpolationDistance = 0.001f;
|
||||
private const float MinInterpolationDistanceSquared = MinInterpolationDistance * MinInterpolationDistance;
|
||||
|
||||
private const double MinInterpolationAngle = Math.PI / 720;
|
||||
|
||||
// 45 degrees.
|
||||
private const double MaxInterpolationAngle = Math.PI / 4;
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
// Only keep track of transforms actively lerping.
|
||||
// Much faster than iterating 3000+ transforms every frame.
|
||||
[ViewVariables] private readonly List<TransformComponent> _lerpingTransforms = new();
|
||||
[ViewVariables] private readonly List<Entity<TransformComponent>> _lerpingTransforms = new();
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
foreach (var xform in _lerpingTransforms)
|
||||
foreach (var (_, xform) in _lerpingTransforms)
|
||||
{
|
||||
xform.ActivelyLerping = false;
|
||||
xform.NextPosition = null;
|
||||
@@ -48,21 +42,77 @@ namespace Robust.Client.GameObjects
|
||||
_lerpingTransforms.Clear();
|
||||
}
|
||||
|
||||
public override void ActivateLerp(TransformComponent xform)
|
||||
public override void ActivateLerp(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
if (xform.ActivelyLerping)
|
||||
// This lerping logic is pretty convoluted and generally assumes that the client does not mispredict.
|
||||
// A more foolproof solution would be to just cache the coordinates at which any given entity was most
|
||||
// recently rendered and using that as the lerp origin. However that'd require enumerating over all entities
|
||||
// every tick which is pretty icky.
|
||||
|
||||
// The general considerations are:
|
||||
// - If the client receives a server state for an entity moving from a->b and predicts nothing else, then it
|
||||
// should show the entity lerping.
|
||||
// - If the client predicts an entity will move while already lerping due to a state-application, it should
|
||||
// clear the state's lerp, under the assumption that the client predicted the state and already rendered
|
||||
// the entity in the 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 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.
|
||||
|
||||
// If the client predicts an entity moves while already lerping, it should clear the
|
||||
// predict a->b, lerp a->b
|
||||
// predicted a->b, then predict b->c. Lerp b->c
|
||||
// predicted a->b, then predict b->c. Lerp b->c
|
||||
// predicted a->b, predicted b->c, then predict c->d. Lerp c->d
|
||||
// server state a->b, then predicted b->c, lerp b->c
|
||||
// server state a->b, then predicted b->c, then predict d, lerp b->c
|
||||
|
||||
if (_gameTiming.ApplyingState)
|
||||
{
|
||||
if (xform.ActivelyLerping)
|
||||
{
|
||||
// This should not happen, but can happen if some bad component state application code modifies an entity's coordinates.
|
||||
Log.Error($"Entity {(ToPrettyString(uid))} tried to lerp twice while applying component states.");
|
||||
return;
|
||||
}
|
||||
|
||||
_lerpingTransforms.Add((uid, xform));
|
||||
xform.ActivelyLerping = true;
|
||||
xform.PredictedLerp = false;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.LastLerp = _gameTiming.CurTick;
|
||||
return;
|
||||
}
|
||||
|
||||
xform.ActivelyLerping = true;
|
||||
_lerpingTransforms.Add(xform);
|
||||
}
|
||||
xform.LastLerp = _gameTiming.CurTick;
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
{
|
||||
xform.ActivelyLerping = false;
|
||||
return;
|
||||
}
|
||||
|
||||
public override void DeactivateLerp(TransformComponent component)
|
||||
{
|
||||
// this should cause the lerp to do nothing
|
||||
component.NextPosition = null;
|
||||
component.NextRotation = null;
|
||||
component.LerpParent = EntityUid.Invalid;
|
||||
if (!xform.ActivelyLerping)
|
||||
{
|
||||
_lerpingTransforms.Add((uid, xform));
|
||||
xform.ActivelyLerping = true;
|
||||
xform.PredictedLerp = true;
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xform.PredictedLerp || xform.LerpParent != xform.ParentUid)
|
||||
{
|
||||
// Existing lerp was not due to prediction, but due to state application. That lerp should already
|
||||
// have been rendered, so we will start a new lerp from the current position.
|
||||
xform.PrevRotation = xform._localRotation;
|
||||
xform.PrevPosition = xform._localPosition;
|
||||
xform.LerpParent = xform.ParentUid;
|
||||
}
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
@@ -73,12 +123,13 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
for (var i = 0; i < _lerpingTransforms.Count; i++)
|
||||
{
|
||||
var transform = _lerpingTransforms[i];
|
||||
var (uid, transform) = _lerpingTransforms[i];
|
||||
var found = false;
|
||||
|
||||
// Only lerp if parent didn't change.
|
||||
// E.g. entering lockers would do it.
|
||||
if (transform.LerpParent == transform.ParentUid
|
||||
if (transform.ActivelyLerping
|
||||
&& transform.LerpParent == transform.ParentUid
|
||||
&& transform.ParentUid.IsValid()
|
||||
&& !transform.Deleted)
|
||||
{
|
||||
@@ -90,8 +141,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
if (distance is > MinInterpolationDistanceSquared and < MaxInterpolationDistanceSquared)
|
||||
{
|
||||
transform.LocalPosition = Vector2.Lerp(lerpSource, lerpDest, step);
|
||||
// Setting LocalPosition clears LerpPosition so fix that.
|
||||
SetLocalPositionNoLerp(uid, Vector2.Lerp(lerpSource, lerpDest, step), transform);
|
||||
transform.NextPosition = lerpDest;
|
||||
found = true;
|
||||
}
|
||||
@@ -101,15 +151,9 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
var lerpDest = transform.NextRotation.Value;
|
||||
var lerpSource = transform.PrevRotation;
|
||||
var distance = Math.Abs(Angle.ShortestDistance(lerpDest, lerpSource));
|
||||
|
||||
if (distance is > MinInterpolationAngle and < MaxInterpolationAngle)
|
||||
{
|
||||
transform.LocalRotation = Angle.Lerp(lerpSource, lerpDest, step);
|
||||
// Setting LocalRotation clears LerpAngle so fix that.
|
||||
transform.NextRotation = lerpDest;
|
||||
found = true;
|
||||
}
|
||||
SetLocalRotationNoLerp(uid, Angle.Lerp(lerpSource, lerpDest, step), transform);
|
||||
transform.NextRotation = lerpDest;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// These methods are used by the Game State Manager.
|
||||
|
||||
EntityUid CreateEntity(string? prototypeName);
|
||||
EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata);
|
||||
|
||||
void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Physics;
|
||||
@@ -52,6 +53,14 @@ namespace Robust.Client.GameStates
|
||||
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
|
||||
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
|
||||
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
|
||||
private 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 ObjectPool<Dictionary<ushort, ComponentState>> _compDataPool =
|
||||
new DefaultObjectPool<Dictionary<ushort, ComponentState>>(new DictPolicy<ushort, ComponentState>(), 256);
|
||||
|
||||
private uint _metaCompNetId;
|
||||
|
||||
@@ -102,6 +111,13 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public event Action<MsgStateLeavePvs>? PvsLeave;
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// If true, this will cause received game states to be ignored. Used by integration tests.
|
||||
/// </summary>
|
||||
public bool DropStates;
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -194,6 +210,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private void HandleStateMessage(MsgState message)
|
||||
{
|
||||
#if DEBUG
|
||||
if (DropStates)
|
||||
return;
|
||||
#endif
|
||||
// We ONLY ack states that are definitely going to get applied. Otherwise the sever might assume that we
|
||||
// applied a state containing entity-creation information, which it would then no longer send to us when
|
||||
// we re-encounter this entity
|
||||
@@ -268,18 +288,15 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PredictionNeedsResetting)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
ResetPredictedEntities();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// avoid exception spam from repeatedly trying to reset the same entity.
|
||||
_entitySystemManager.GetEntitySystem<ClientDirtySystem>().Reset();
|
||||
_runtimeLog.LogException(e, "ResetPredictedEntities");
|
||||
}
|
||||
ResetPredictedEntities();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// avoid exception spam from repeatedly trying to reset the same entity.
|
||||
_entitySystemManager.GetEntitySystem<ClientDirtySystem>().Reset();
|
||||
_runtimeLog.LogException(e, "ResetPredictedEntities");
|
||||
}
|
||||
|
||||
// If we were waiting for a new state, we are now applying it.
|
||||
@@ -468,19 +485,21 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public void ResetPredictedEntities()
|
||||
{
|
||||
PredictionNeedsResetting = false;
|
||||
|
||||
using var _ = _prof.Group("ResetPredictedEntities");
|
||||
using var __ = _timing.StartStateApplicationArea();
|
||||
|
||||
// This is terrible, and I hate it. This also needs to run even when prediction is disabled.
|
||||
_entitySystemManager.GetEntitySystem<SharedGridTraversalSystem>().QueuedEvents.Clear();
|
||||
_entitySystemManager.GetEntitySystem<TransformSystem>().Reset();
|
||||
|
||||
if (!PredictionNeedsResetting)
|
||||
return;
|
||||
|
||||
PredictionNeedsResetting = false;
|
||||
var countReset = 0;
|
||||
var system = _entitySystemManager.GetEntitySystem<ClientDirtySystem>();
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
RemQueue<Component> toRemove = new();
|
||||
|
||||
// This is terrible, and I hate it.
|
||||
_entitySystemManager.GetEntitySystem<SharedGridTraversalSystem>().QueuedEvents.Clear();
|
||||
_entitySystemManager.GetEntitySystem<TransformSystem>().Reset();
|
||||
RemQueue<IComponent> toRemove = new();
|
||||
|
||||
foreach (var entity in system.DirtyEntities)
|
||||
{
|
||||
@@ -585,18 +604,17 @@ namespace Robust.Client.GameStates
|
||||
/// Whenever a new entity is created, the server doesn't send full state data, given that much of the data
|
||||
/// can simply be obtained from the entity prototype information. This function basically creates a fake
|
||||
/// initial server state for any newly created entity. It does this by simply using the standard <see
|
||||
/// cref="IEntityManager.GetComponentState(IEventBus, IComponent)"/>.
|
||||
/// cref="IEntityManager.GetComponentState"/>.
|
||||
/// </remarks>
|
||||
private void MergeImplicitData(IEnumerable<NetEntity> createdEntities)
|
||||
{
|
||||
var outputData = new Dictionary<NetEntity, Dictionary<ushort, ComponentState>>();
|
||||
var bus = _entityManager.EventBus;
|
||||
|
||||
foreach (var netEntity in createdEntities)
|
||||
{
|
||||
var (createdEntity, meta) = _entityManager.GetEntityData(netEntity);
|
||||
var compData = new Dictionary<ushort, ComponentState>();
|
||||
outputData.Add(netEntity, compData);
|
||||
var compData = _compDataPool.Get();
|
||||
_outputData.Add(netEntity, compData);
|
||||
|
||||
foreach (var (netId, component) in meta.NetComponents)
|
||||
{
|
||||
@@ -609,7 +627,14 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
_processor.MergeImplicitData(outputData);
|
||||
_processor.MergeImplicitData(_outputData);
|
||||
|
||||
foreach (var data in _outputData.Values)
|
||||
{
|
||||
_compDataPool.Return(data);
|
||||
}
|
||||
|
||||
_outputData.Clear();
|
||||
}
|
||||
|
||||
private void AckGameState(GameTick sequence)
|
||||
@@ -687,12 +712,10 @@ namespace Robust.Client.GameStates
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(es.NetEntity);
|
||||
|
||||
var uid = _entities.CreateEntity(metaState.PrototypeId);
|
||||
var uid = _entities.CreateEntity(metaState.PrototypeId, out var newMeta);
|
||||
_toCreate.Add(es.NetEntity, es);
|
||||
var newMeta = metas.GetComponent(uid);
|
||||
_toApply.Add(uid, (es.NetEntity, newMeta, false, GameTick.Zero, es, null));
|
||||
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
// We need to clear this mapping before assigning the real net id.
|
||||
// TODO NetEntity Jank: prevent the client from creating this in the first place.
|
||||
@@ -722,9 +745,7 @@ namespace Robust.Client.GameStates
|
||||
if (_toCreate.ContainsKey(es.NetEntity))
|
||||
continue;
|
||||
|
||||
var uid = _entityManager.GetEntity(es.NetEntity);
|
||||
|
||||
if (!metas.TryGetComponent(uid, out var meta))
|
||||
if (!_entityManager.TryGetEntityData(es.NetEntity, out var uid, out var meta))
|
||||
continue;
|
||||
|
||||
bool isEnteringPvs = (meta.Flags & MetaDataFlags.Detached) != 0;
|
||||
@@ -739,7 +760,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
_toApply.Add(uid, (es.NetEntity, meta, isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
_toApply.Add(uid.Value, (es.NetEntity, meta, isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
meta.LastStateApplied = curState.ToSequence;
|
||||
}
|
||||
|
||||
@@ -785,7 +806,7 @@ namespace Robust.Client.GameStates
|
||||
state = (meta.NetEntity, meta, false, GameTick.Zero, null, null);
|
||||
}
|
||||
|
||||
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
|
||||
_queuedBroadphaseUpdates.Clear();
|
||||
|
||||
// Apply entity states.
|
||||
using (_prof.Group("Apply States"))
|
||||
@@ -805,7 +826,7 @@ namespace Robust.Client.GameStates
|
||||
DebugTools.Assert(xform.Broadphase == BroadphaseData.Invalid);
|
||||
xform.Broadphase = null;
|
||||
if (!_toApply.TryGetValue(xform.ParentUid, out var parent) || !parent.EnteringPvs)
|
||||
queuedBroadphaseUpdates.Add((entity, xform));
|
||||
_queuedBroadphaseUpdates.Add((entity, xform));
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(_toApply.Count));
|
||||
@@ -816,7 +837,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var (uid, xform) in queuedBroadphaseUpdates)
|
||||
foreach (var (uid, xform) in _queuedBroadphaseUpdates)
|
||||
{
|
||||
lookupSys.FindAndAddToEntityTree(uid, true, xform);
|
||||
}
|
||||
@@ -833,7 +854,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessDeletions(delSpan, xforms, metas, xformSys);
|
||||
ProcessDeletions(delSpan, xforms, xformSys);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -870,17 +891,17 @@ namespace Robust.Client.GameStates
|
||||
_sawmill.Info($"Resetting all entity states to tick {state.ToSequence}.");
|
||||
|
||||
// Construct hashset for set.Contains() checks.
|
||||
_stateEnts.Clear();
|
||||
var entityStates = state.EntityStates.Span;
|
||||
var stateEnts = new HashSet<NetEntity>();
|
||||
foreach (var entState in entityStates)
|
||||
{
|
||||
stateEnts.Add(entState.NetEntity);
|
||||
_stateEnts.Add(entState.NetEntity);
|
||||
}
|
||||
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
var toDelete = new List<EntityUid>(Math.Max(64, _entities.EntityCount - stateEnts.Count));
|
||||
_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>();
|
||||
@@ -891,14 +912,16 @@ namespace Robust.Client.GameStates
|
||||
if (metadata.NetEntity.IsClientSide())
|
||||
{
|
||||
if (deleteClientEntities)
|
||||
toDelete.Add(ent);
|
||||
_toDelete.Add(ent);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stateEnts.Contains(netEnt))
|
||||
if (_stateEnts.Contains(netEnt))
|
||||
{
|
||||
if (resetAllEntities || metadata.LastStateApplied > state.ToSequence)
|
||||
metadata.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -916,13 +939,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);
|
||||
}
|
||||
@@ -931,7 +955,6 @@ namespace Robust.Client.GameStates
|
||||
private void ProcessDeletions(
|
||||
ReadOnlySpan<NetEntity> delSpan,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
SharedTransformSystem xformSys)
|
||||
{
|
||||
// Processing deletions is non-trivial, because by default deletions will also delete all child entities.
|
||||
@@ -1002,6 +1025,7 @@ namespace Robust.Client.GameStates
|
||||
// things like container insertion and ejection.
|
||||
|
||||
using var _ = _prof.Group("Leave PVS");
|
||||
detached.EnsureCapacity(toDetach.Count);
|
||||
|
||||
foreach (var (tick, ents) in toDetach)
|
||||
{
|
||||
@@ -1024,9 +1048,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
var ent = _entityManager.GetEntity(netEntity);
|
||||
|
||||
if (!metas.TryGetComponent(ent, out var meta))
|
||||
if (!_entityManager.TryGetEntityData(netEntity, out var ent, out var meta))
|
||||
continue;
|
||||
|
||||
if (meta.LastStateApplied > maxTick)
|
||||
@@ -1042,10 +1064,10 @@ namespace Robust.Client.GameStates
|
||||
if (lastStateApplied.HasValue)
|
||||
meta.LastStateApplied = lastStateApplied.Value;
|
||||
|
||||
var xform = xforms.GetComponent(ent);
|
||||
var xform = xforms.GetComponent(ent.Value);
|
||||
if (xform.ParentUid.IsValid())
|
||||
{
|
||||
lookupSys.RemoveFromEntityTree(ent, xform);
|
||||
lookupSys.RemoveFromEntityTree(ent.Value, xform);
|
||||
xform.Broadphase = BroadphaseData.Invalid;
|
||||
|
||||
// In some cursed scenarios an entity inside of a container can leave PVS without the container itself leaving PVS.
|
||||
@@ -1054,13 +1076,13 @@ namespace Robust.Client.GameStates
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, ent, out container, null, true))
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, ent.Value, out container, null, true))
|
||||
{
|
||||
container.Remove(ent, _entities, xform, meta, false, true);
|
||||
container.Remove(ent.Value, _entities, xform, meta, false, true);
|
||||
}
|
||||
|
||||
meta._flags |= MetaDataFlags.Detached;
|
||||
xformSys.DetachParentToNull(ent, xform);
|
||||
xformSys.DetachParentToNull(ent.Value, xform);
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
|
||||
|
||||
if (container != null)
|
||||
@@ -1140,14 +1162,15 @@ namespace Robust.Client.GameStates
|
||||
// First remove any deleted components
|
||||
if (curState?.NetComponents != null)
|
||||
{
|
||||
RemQueue<Component> toRemove = new();
|
||||
_toRemove.Clear();
|
||||
|
||||
foreach (var (id, comp) in meta.NetComponents)
|
||||
{
|
||||
if (comp.NetSyncEnabled && !curState.NetComponents.Contains(id))
|
||||
toRemove.Add(comp);
|
||||
_toRemove.Add(comp);
|
||||
}
|
||||
|
||||
foreach (var comp in toRemove)
|
||||
foreach (var comp in _toRemove)
|
||||
{
|
||||
_entities.RemoveComponent(uid, comp, meta);
|
||||
}
|
||||
@@ -1164,8 +1187,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
if (!meta.NetComponents.TryGetValue(id, out var comp))
|
||||
{
|
||||
comp = (Component) _compFactory.GetComponent(id);
|
||||
comp.Owner = uid;
|
||||
comp = _compFactory.GetComponent(id);
|
||||
_entityManager.AddComponent(uid, comp, true, metadata: meta);
|
||||
}
|
||||
|
||||
@@ -1178,8 +1200,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
if (!meta.NetComponents.TryGetValue(compChange.NetID, out var comp))
|
||||
{
|
||||
comp = (Component) _compFactory.GetComponent(compChange.NetID);
|
||||
comp.Owner = uid;
|
||||
comp = _compFactory.GetComponent(compChange.NetID);
|
||||
_entityManager.AddComponent(uid, comp, true, metadata:meta);
|
||||
}
|
||||
else if (compChange.LastModifiedTick <= lastApplied && lastApplied != GameTick.Zero)
|
||||
@@ -1249,7 +1270,9 @@ namespace Robust.Client.GameStates
|
||||
var handleState = new ComponentHandleState(cur, next);
|
||||
bus.RaiseComponentEvent(comp, ref handleState);
|
||||
}
|
||||
#pragma warning disable CS0168 // Variable is declared but never used
|
||||
catch (Exception e)
|
||||
#pragma warning restore CS0168 // Variable is declared but never used
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
_sawmill.Error($"Failed to apply comp state: entity={_entities.ToPrettyString(uid)}, comp={comp.GetType()}");
|
||||
@@ -1405,8 +1428,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
if (!meta.NetComponents.TryGetValue(id, out var comp))
|
||||
{
|
||||
comp = (Component) _compFactory.GetComponent(id);
|
||||
comp.Owner = uid;
|
||||
comp = _compFactory.GetComponent(id);
|
||||
_entityManager.AddComponent(uid, comp, true, meta);
|
||||
}
|
||||
|
||||
@@ -1415,20 +1437,24 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
// ensure we don't have any extra components
|
||||
RemQueue<Component> toRemove = new();
|
||||
_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);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public bool IsQueuedForDetach(NetEntity entity)
|
||||
=> _processor.IsQueuedForDetach(entity);
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_sawmill = _logMan.GetSawmill(CVars.NetPredict.Name);
|
||||
|
||||
@@ -404,6 +404,18 @@ namespace Robust.Client.GameStates
|
||||
return _lastStateFullRep.TryGetValue(entity, out dictionary);
|
||||
}
|
||||
|
||||
public bool IsQueuedForDetach(NetEntity entity)
|
||||
{
|
||||
// This isn't fast, but its just meant for use in tests & debug asserts.
|
||||
foreach (var msg in _pvsDetachMessages.Values)
|
||||
{
|
||||
if (msg.Contains(entity))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int CalculateBufferSize(GameTick fromTick)
|
||||
{
|
||||
bool foundState;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
{
|
||||
internal sealed class NetInterpOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
@@ -24,6 +24,11 @@ namespace Robust.Client.GameStates
|
||||
private readonly SharedContainerSystem _container;
|
||||
private readonly SharedTransformSystem _xform;
|
||||
|
||||
/// <summary>
|
||||
/// When an entity stops lerping the overlay will continue to draw a box around the entity for this amount of time.
|
||||
/// </summary>
|
||||
public static readonly TimeSpan Delay = TimeSpan.FromSeconds(2f);
|
||||
|
||||
public NetInterpOverlay(EntityLookupSystem lookup)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -40,8 +45,8 @@ namespace Robust.Client.GameStates
|
||||
var worldHandle = (DrawingHandleWorld) handle;
|
||||
var viewport = args.WorldAABB;
|
||||
|
||||
var query = _entityManager.AllEntityQueryEnumerator<PhysicsComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var physics, out var transform))
|
||||
var query = _entityManager.AllEntityQueryEnumerator<TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var transform))
|
||||
{
|
||||
// if not on the same map, continue
|
||||
if (transform.MapID != _eyeManager.CurrentMap || _container.IsEntityInContainer(uid))
|
||||
@@ -50,8 +55,8 @@ namespace Robust.Client.GameStates
|
||||
if (transform.GridUid == uid)
|
||||
continue;
|
||||
|
||||
// This entity isn't lerping, no need to draw debug info for it
|
||||
if(transform.NextPosition == null)
|
||||
var delta = (_timing.CurTick.Value - transform.LastLerp.Value) * _timing.TickPeriod;
|
||||
if(!transform.ActivelyLerping && delta > Delay)
|
||||
continue;
|
||||
|
||||
var aabb = _lookup.GetWorldAABB(uid);
|
||||
@@ -61,7 +66,9 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
|
||||
var (pos, rot) = _xform.GetWorldPositionRotation(transform, _entityManager.GetEntityQuery<TransformComponent>());
|
||||
var boxOffset = transform.NextPosition.Value - transform.LocalPosition;
|
||||
var boxOffset = transform.NextPosition != null
|
||||
? transform.NextPosition.Value - transform.LocalPosition
|
||||
: default;
|
||||
var worldOffset = (rot - transform.LocalRotation).RotateVec(boxOffset);
|
||||
|
||||
var nextPos = pos + worldOffset;
|
||||
|
||||
@@ -4,7 +4,6 @@ using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -41,21 +40,23 @@ namespace Robust.Client.Graphics.Clyde
|
||||
gridProgram.SetUniformTextureMaybe(UniILightTexture, TextureUnit.Texture1);
|
||||
gridProgram.SetUniform(UniIModUV, new Vector4(0, 0, 1, 1));
|
||||
|
||||
foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
|
||||
var grids = new List<Entity<MapGridComponent>>();
|
||||
_mapManager.FindGridsIntersecting(mapId, worldBounds, ref grids);
|
||||
foreach (var mapGrid in grids)
|
||||
{
|
||||
if (!_mapChunkData.ContainsKey(mapGrid.Owner))
|
||||
if (!_mapChunkData.ContainsKey(mapGrid))
|
||||
continue;
|
||||
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(mapGrid.Owner);
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(mapGrid);
|
||||
gridProgram.SetUniform(UniIModelMatrix, transform.WorldMatrix);
|
||||
var enumerator = mapGrid.GetMapChunks(worldBounds);
|
||||
var enumerator = mapGrid.Comp.GetMapChunks(worldBounds);
|
||||
|
||||
while (enumerator.MoveNext(out var chunk))
|
||||
{
|
||||
if (_isChunkDirty(mapGrid, chunk))
|
||||
_updateChunkMesh(mapGrid, chunk);
|
||||
|
||||
var datum = _mapChunkData[mapGrid.Owner][chunk.Indices];
|
||||
var datum = _mapChunkData[mapGrid][chunk.Indices];
|
||||
|
||||
if (datum.TileCount == 0)
|
||||
continue;
|
||||
@@ -70,9 +71,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
private void _updateChunkMesh(MapGridComponent grid, MapChunk chunk)
|
||||
private void _updateChunkMesh(Entity<MapGridComponent> grid, MapChunk chunk)
|
||||
{
|
||||
var data = _mapChunkData[grid.Owner];
|
||||
var data = _mapChunkData[grid];
|
||||
|
||||
if (!data.TryGetValue(chunk.Indices, out var datum))
|
||||
{
|
||||
@@ -83,7 +84,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
Span<Vertex2D> vertexBuffer = stackalloc Vertex2D[_verticesPerChunk(chunk)];
|
||||
|
||||
var i = 0;
|
||||
var cSz = grid.ChunkSize;
|
||||
var cSz = grid.Comp.ChunkSize;
|
||||
var cScaled = chunk.Indices * cSz;
|
||||
for (ushort x = 0; x < cSz; x++)
|
||||
{
|
||||
@@ -130,7 +131,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
datum.TileCount = i;
|
||||
}
|
||||
|
||||
private unsafe MapChunkData _initChunkBuffers(MapGridComponent grid, MapChunk chunk)
|
||||
private unsafe MapChunkData _initChunkBuffers(Entity<MapGridComponent> grid, MapChunk chunk)
|
||||
{
|
||||
var vao = GenVertexArray();
|
||||
BindVertexArray(vao);
|
||||
@@ -158,19 +159,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
Dirty = true
|
||||
};
|
||||
|
||||
_mapChunkData[grid.Owner].Add(chunk.Indices, datum);
|
||||
_mapChunkData[grid].Add(chunk.Indices, datum);
|
||||
return datum;
|
||||
}
|
||||
|
||||
private bool _isChunkDirty(MapGridComponent grid, MapChunk chunk)
|
||||
private bool _isChunkDirty(Entity<MapGridComponent> grid, MapChunk chunk)
|
||||
{
|
||||
var data = _mapChunkData[grid.Owner];
|
||||
var data = _mapChunkData[grid];
|
||||
return !data.TryGetValue(chunk.Indices, out var datum) || datum.Dirty;
|
||||
}
|
||||
|
||||
public void _setChunkDirty(MapGridComponent grid, Vector2i chunk)
|
||||
public void _setChunkDirty(Entity<MapGridComponent> grid, Vector2i chunk)
|
||||
{
|
||||
var data = _mapChunkData.GetOrNew(grid.Owner);
|
||||
var data = _mapChunkData.GetOrNew(grid);
|
||||
if (data.TryGetValue(chunk, out var datum))
|
||||
{
|
||||
datum.Dirty = true;
|
||||
@@ -184,7 +185,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
var grid = args.Grid;
|
||||
var chunk = grid.GridTileToChunkIndices(pos);
|
||||
_setChunkDirty(grid, chunk);
|
||||
_setChunkDirty((args.GridEnt, grid), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +193,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
var grid = _mapManager.GetGrid(args.NewTile.GridUid);
|
||||
var chunk = grid.GridTileToChunkIndices(new Vector2i(args.NewTile.X, args.NewTile.Y));
|
||||
_setChunkDirty(grid, chunk);
|
||||
_setChunkDirty((args.NewTile.GridUid, grid), chunk);
|
||||
}
|
||||
|
||||
private void _updateOnGridCreated(GridStartupEvent ev)
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
|
||||
|
||||
if (entry.Sprite.RaiseShaderEvent)
|
||||
_entityManager.EventBus.RaiseLocalEvent(entry.Sprite.Owner,
|
||||
_entityManager.EventBus.RaiseLocalEvent(entry.Uid,
|
||||
new BeforePostShaderRenderEvent(entry.Sprite, viewport), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,10 +338,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
var mapId = eye.Position.MapId;
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
// If this map has lighting disabled, return
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
if (!_entityManager.GetComponent<MapComponent>(mapUid).LightingEnabled)
|
||||
if (!_entityManager.TryGetComponent<MapComponent>(mapUid, out var map) || !map.LightingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,7 +6,15 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde;
|
||||
|
||||
@@ -260,7 +260,7 @@ internal partial class Clyde
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
|
||||
return a.Sprite.Owner.CompareTo(b.Sprite.Owner);
|
||||
return a.Uid.CompareTo(b.Uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,9 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute;
|
||||
using TextureWrapMode = Robust.Shared.Graphics.TextureWrapMode;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
|
||||
namespace Robust.Client.Map;
|
||||
|
||||
@@ -22,6 +23,8 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public TileEdgeOverlay(IEntityManager entManager, IMapManager mapManager, IResourceCache resource, ITileDefinitionManager tileDefManager)
|
||||
{
|
||||
_entManager = entManager;
|
||||
@@ -36,16 +39,18 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
if (args.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds, ref _grids);
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds))
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var tileSize = grid.TileSize;
|
||||
var tileSize = grid.Comp.TileSize;
|
||||
var tileDimensions = new Vector2(tileSize, tileSize);
|
||||
var xform = xformQuery.GetComponent(grid.Owner);
|
||||
var xform = xformQuery.GetComponent(grid);
|
||||
args.WorldHandle.SetTransform(xform.WorldMatrix);
|
||||
|
||||
foreach (var tileRef in grid.GetTilesIntersecting(args.WorldBounds, false))
|
||||
foreach (var tileRef in grid.Comp.GetTilesIntersecting(args.WorldBounds, false))
|
||||
{
|
||||
var tileDef = _tileDefManager[tileRef.Tile.TypeId];
|
||||
|
||||
@@ -61,7 +66,7 @@ public sealed class TileEdgeOverlay : Overlay
|
||||
continue;
|
||||
|
||||
var neighborIndices = new Vector2i(tileRef.GridIndices.X + x, tileRef.GridIndices.Y + y);
|
||||
var neighborTile = grid.GetTileRef(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.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -26,7 +25,7 @@ namespace Robust.Client.Physics
|
||||
|
||||
protected override void Cleanup(PhysicsMapComponent component, float frameTime)
|
||||
{
|
||||
var toRemove = new List<PhysicsComponent>();
|
||||
var toRemove = new List<Entity<PhysicsComponent>>();
|
||||
|
||||
// Because we're not predicting 99% of bodies its sleep timer never gets incremented so we'll just do it ourselves.
|
||||
// (and serializing it over the network isn't necessary?)
|
||||
@@ -38,13 +37,13 @@ namespace Robust.Client.Physics
|
||||
body.SleepTime += frameTime;
|
||||
if (body.SleepTime > TimeToSleep)
|
||||
{
|
||||
toRemove.Add(body);
|
||||
toRemove.Add(new Entity<PhysicsComponent>(body.Owner, body));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var body in toRemove)
|
||||
{
|
||||
SetAwake(body.Owner, body, false);
|
||||
SetAwake(body, false);
|
||||
}
|
||||
|
||||
base.Cleanup(component, frameTime);
|
||||
@@ -82,11 +81,8 @@ namespace Robust.Client.Physics
|
||||
continue;
|
||||
}
|
||||
|
||||
xform.PrevPosition = position;
|
||||
xform.PrevRotation = rotation;
|
||||
xform.LerpParent = parentUid;
|
||||
xform.NextPosition = xform.LocalPosition;
|
||||
xform.NextRotation = xform.LocalRotation;
|
||||
// Transform system will handle lerping.
|
||||
_transform.SetLocalPositionRotation(uid, xform.LocalPosition, xform.LocalRotation, xform);
|
||||
}
|
||||
|
||||
component.LerpData.Clear();
|
||||
|
||||
12
Robust.Client/Spawners/TimedDespawnSystem.cs
Normal file
12
Robust.Client/Spawners/TimedDespawnSystem.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Spawners;
|
||||
|
||||
namespace Robust.Client.Spawners;
|
||||
|
||||
public sealed class TimedDespawnSystem : SharedTimedDespawnSystem
|
||||
{
|
||||
protected override bool CanDelete(EntityUid uid)
|
||||
{
|
||||
return IsClientSide(uid);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
@@ -14,7 +15,17 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// for each enum value to see how the different options work.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public SplitResizeMode ResizeMode { get; set; }
|
||||
public SplitResizeMode ResizeMode
|
||||
{
|
||||
get => _resizeMode;
|
||||
set
|
||||
{
|
||||
_resizeMode = value;
|
||||
_splitDragArea.Visible = value != SplitResizeMode.NotResizable;
|
||||
}
|
||||
}
|
||||
|
||||
private SplitResizeMode _resizeMode = SplitResizeMode.RespectChildrenMinSize;
|
||||
|
||||
/// <summary>
|
||||
/// Width of the split in virtual pixels
|
||||
@@ -30,7 +41,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private float _splitWidth;
|
||||
private float _splitWidth = 10;
|
||||
|
||||
/// <summary>
|
||||
/// This width determines the minimum size of the draggable area around the split. This has no effect if it
|
||||
@@ -38,11 +49,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
public float MinDraggableWidth = 10f;
|
||||
|
||||
public float DraggableWidth => MathF.Max(MinDraggableWidth, _splitWidth);
|
||||
|
||||
/// <summary>
|
||||
/// Virtual pixel offset from the edge beyond which the split cannot be moved.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SplitEdgeSeparation { get; set; }
|
||||
public float SplitEdgeSeparation { get; set; } = 10;
|
||||
|
||||
private float _splitStart;
|
||||
|
||||
@@ -58,6 +71,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
_splitStart = value - _splitWidth / 2;
|
||||
ClampSplitCenter();
|
||||
InvalidateMeasure();
|
||||
OnSplitResized?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,13 +95,51 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private SplitState _splitState;
|
||||
private bool _dragging;
|
||||
private SplitState _splitState = SplitState.Auto;
|
||||
private SplitOrientation _orientation;
|
||||
private SplitStretchDirection _stretchDirection;
|
||||
private SplitStretchDirection _stretchDirection = SplitStretchDirection.BottomRight;
|
||||
private bool _dragging;
|
||||
private float _dragOffset;
|
||||
|
||||
private bool Vertical => Orientation == SplitOrientation.Vertical;
|
||||
|
||||
private readonly SplitDragControl _splitDragArea = new();
|
||||
|
||||
/// <summary>
|
||||
/// The upper/left control in the split container.
|
||||
/// </summary>
|
||||
public Control? First
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChildCount < 3)
|
||||
return null;
|
||||
|
||||
DebugTools.AssertNotEqual(GetChild(0), _splitDragArea);
|
||||
return GetChild(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The lower/right control in the split container.
|
||||
/// </summary>
|
||||
public Control? Second
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChildCount < 3)
|
||||
return null;
|
||||
|
||||
DebugTools.AssertNotEqual(GetChild(1), _splitDragArea);
|
||||
return GetChild(1);
|
||||
}
|
||||
}
|
||||
|
||||
public (Control First, Control Second)? Splits => ChildCount < 3 ? null : (GetChild(0), GetChild(1));
|
||||
|
||||
public event Action? OnSplitResizeFinished;
|
||||
public event Action? OnSplitResized;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the split position should be set manually or automatically.
|
||||
/// </summary>
|
||||
@@ -131,73 +183,53 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public SplitContainer()
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
_splitState = SplitState.Auto;
|
||||
_stretchDirection = SplitStretchDirection.BottomRight;
|
||||
_dragging = false;
|
||||
ResizeMode = SplitResizeMode.RespectChildrenMinSize;
|
||||
SplitWidth = 10;
|
||||
SplitEdgeSeparation = 10;
|
||||
AddChild(_splitDragArea);
|
||||
_splitDragArea.Visible = _resizeMode != SplitResizeMode.NotResizable;
|
||||
_splitDragArea.DefaultCursorShape = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
_splitDragArea.OnMouseUp += StopDragging;
|
||||
_splitDragArea.OnMouseDown += StartDragging;
|
||||
_splitDragArea.OnMouseMove += OnMove;
|
||||
}
|
||||
|
||||
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
|
||||
private void OnMove(GUIMouseMoveEventArgs args)
|
||||
{
|
||||
base.MouseMove(args);
|
||||
if (ResizeMode == SplitResizeMode.NotResizable)
|
||||
return;
|
||||
|
||||
if (ResizeMode == SplitResizeMode.NotResizable) return;
|
||||
if (!_dragging)
|
||||
return;
|
||||
|
||||
if (_dragging)
|
||||
{
|
||||
var newOffset = Vertical ? args.RelativePosition.Y : args.RelativePosition.X;
|
||||
|
||||
SplitCenter = newOffset;
|
||||
DefaultCursorShape = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// on mouseover, check if they are over the split and change the cursor accordingly
|
||||
var cursor = CursorShape.Arrow;
|
||||
if (CanDragAt(args.RelativePosition))
|
||||
{
|
||||
cursor = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
}
|
||||
|
||||
DefaultCursorShape = cursor;
|
||||
}
|
||||
// Source control might be either the container, or the separator.
|
||||
// So we manually calculate the relative coordinates wrt the container.
|
||||
var relative = args.GlobalPosition - GlobalPosition;
|
||||
SplitCenter = Vertical ? relative.Y - _dragOffset : relative.X + _dragOffset;
|
||||
}
|
||||
|
||||
|
||||
protected internal override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
protected override void ChildAdded(Control newChild)
|
||||
{
|
||||
base.KeyBindDown(args);
|
||||
|
||||
if (ResizeMode == SplitResizeMode.NotResizable) return;
|
||||
|
||||
if (_dragging || args.Function != EngineKeyFunctions.UIClick) return;
|
||||
|
||||
if (CanDragAt(args.RelativePosition))
|
||||
{
|
||||
_dragging = true;
|
||||
_splitState = SplitState.Manual;
|
||||
}
|
||||
base.ChildAdded(newChild);
|
||||
_splitDragArea.SetPositionLast();
|
||||
}
|
||||
|
||||
protected internal override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||
public void StartDragging(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindUp(args);
|
||||
if (ResizeMode == SplitResizeMode.NotResizable || _dragging)
|
||||
return;
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIClick) return;
|
||||
_dragging = true;
|
||||
_dragOffset = DraggableWidth / 2 - (Vertical ? args.RelativePosition.Y : args.RelativePosition.X);
|
||||
_splitState = SplitState.Manual;
|
||||
DefaultCursorShape = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
}
|
||||
|
||||
private void StopDragging(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
if (!_dragging)
|
||||
return;
|
||||
|
||||
_dragging = false;
|
||||
DefaultCursorShape = CursorShape.Arrow;
|
||||
}
|
||||
|
||||
private bool CanDragAt(Vector2 relativePosition)
|
||||
{
|
||||
var distance = Vertical
|
||||
? Math.Abs(relativePosition.Y - SplitCenter)
|
||||
: Math.Abs(relativePosition.X - SplitCenter);
|
||||
|
||||
return distance <= _splitWidth || distance <= MinDraggableWidth;
|
||||
OnSplitResizeFinished?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -218,7 +250,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
desiredSplit += Vertical ? desiredSize.Value.Y - Size.Y : desiredSize.Value.X - Size.X;
|
||||
_splitStart = MathHelper.Clamp(desiredSplit, SplitEdgeSeparation, splitMax);
|
||||
|
||||
if (ResizeMode == SplitResizeMode.RespectChildrenMinSize && ChildCount == 2)
|
||||
if (ResizeMode == SplitResizeMode.RespectChildrenMinSize && ChildCount == 3)
|
||||
{
|
||||
var first = GetChild(0);
|
||||
var second = GetChild(1);
|
||||
@@ -234,7 +266,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
if (ChildCount != 2)
|
||||
if (ChildCount < 3)
|
||||
{
|
||||
return finalSize;
|
||||
}
|
||||
@@ -287,15 +319,23 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
// location & size of the draggable area may be larger than the split area.
|
||||
var dragCenter = _splitStart + _splitWidth / 2;
|
||||
var dragWidth = DraggableWidth;
|
||||
var dragStart = dragCenter - dragWidth / 2;
|
||||
var dragEnd = dragCenter + dragWidth / 2;
|
||||
|
||||
if (Vertical)
|
||||
{
|
||||
first.Arrange(new UIBox2(0, 0, finalSize.X, _splitStart));
|
||||
second.Arrange(new UIBox2(0, _splitStart + _splitWidth, finalSize.X, finalSize.Y));
|
||||
_splitDragArea.Arrange(new UIBox2(0, dragStart, finalSize.X, dragEnd));
|
||||
}
|
||||
else
|
||||
{
|
||||
first.Arrange(new UIBox2(0, 0, _splitStart, finalSize.Y));
|
||||
second.Arrange(new UIBox2(_splitStart + _splitWidth, 0, finalSize.X, finalSize.Y));
|
||||
_splitDragArea.Arrange(new UIBox2(dragStart, 0, dragEnd, finalSize.Y));
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
@@ -303,7 +343,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
if (ChildCount != 2)
|
||||
if (ChildCount < 3)
|
||||
{
|
||||
return Vector2.Zero;
|
||||
}
|
||||
@@ -420,5 +460,40 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
TopLeft,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple control to intercept mous events and redirect them to the parent.
|
||||
/// </summary>
|
||||
private sealed class SplitDragControl : Control
|
||||
{
|
||||
public event Action<GUIBoundKeyEventArgs>? OnMouseDown;
|
||||
public event Action<GUIBoundKeyEventArgs>? OnMouseUp;
|
||||
public event Action<GUIMouseMoveEventArgs>? OnMouseMove;
|
||||
|
||||
public SplitDragControl()
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
}
|
||||
|
||||
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
|
||||
{
|
||||
base.MouseMove(args);
|
||||
OnMouseMove?.Invoke(args);
|
||||
}
|
||||
|
||||
protected internal override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindDown(args);
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
OnMouseDown?.Invoke(args);
|
||||
}
|
||||
|
||||
protected internal override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindUp(args);
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
OnMouseUp?.Invoke(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -18,18 +17,14 @@ namespace Robust.Client.UserInterface.Controls
|
||||
IEntityManager _entMan;
|
||||
|
||||
[ViewVariables]
|
||||
private SpriteComponent? _sprite;
|
||||
public SpriteComponent? Sprite
|
||||
{
|
||||
get => _sprite;
|
||||
[Obsolete("Use SetEntity()")]
|
||||
set => SetEntity(value?.Owner);
|
||||
}
|
||||
public SpriteComponent? Sprite { get; private set; }
|
||||
|
||||
|
||||
[ViewVariables]
|
||||
public EntityUid? Entity { get; private set; }
|
||||
|
||||
public Entity<SpriteComponent>? Ent => Entity == null || Sprite == null ? null : (Entity.Value, Sprite);
|
||||
|
||||
/// <summary>
|
||||
/// This field configures automatic scaling of the sprite. This automatic scaling is done before
|
||||
/// applying the explicitly set scale <see cref="SpriteView.Scale"/>.
|
||||
@@ -124,14 +119,22 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public SpriteView()
|
||||
{
|
||||
_entMan = IoCManager.Resolve<IEntityManager>();
|
||||
_entMan.TryGetComponent(Entity, out _sprite);
|
||||
if (_entMan.TryGetComponent(Entity, out SpriteComponent? sprite))
|
||||
{
|
||||
Sprite = sprite;
|
||||
}
|
||||
|
||||
RectClipContent = true;
|
||||
}
|
||||
|
||||
public void SetEntity(EntityUid? uid)
|
||||
{
|
||||
Entity = uid;
|
||||
_entMan.TryGetComponent(Entity, out _sprite);
|
||||
|
||||
if (_entMan.TryGetComponent(Entity, out SpriteComponent? sprite))
|
||||
{
|
||||
Sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
@@ -143,13 +146,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
private void UpdateSize()
|
||||
{
|
||||
if (Entity == null || _sprite == null)
|
||||
if (Entity == null || Sprite == null)
|
||||
{
|
||||
_spriteSize = default;
|
||||
return;
|
||||
}
|
||||
|
||||
var spriteBox = _sprite.CalculateRotatedBoundingBox(default, _worldRotation ?? Angle.Zero, _eyeRotation)
|
||||
var spriteBox = Sprite.CalculateRotatedBoundingBox(default, _worldRotation ?? Angle.Zero, _eyeRotation)
|
||||
.CalcBoundingBox();
|
||||
|
||||
if (!SpriteOffset)
|
||||
@@ -191,10 +194,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
internal override void DrawInternal(IRenderHandle renderHandle)
|
||||
{
|
||||
if (Entity is not {} uid || _sprite == null)
|
||||
if (Entity is not {} uid || Sprite == null)
|
||||
return;
|
||||
|
||||
if (_sprite.Deleted)
|
||||
if (Sprite.Deleted)
|
||||
{
|
||||
SetEntity(null);
|
||||
return;
|
||||
@@ -214,11 +217,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
var offset = SpriteOffset
|
||||
? Vector2.Zero
|
||||
: - (-_eyeRotation).RotateVec(_sprite.Offset) * new Vector2(1, -1) * EyeManager.PixelsPerMeter;
|
||||
: - (-_eyeRotation).RotateVec(Sprite.Offset) * new Vector2(1, -1) * EyeManager.PixelsPerMeter;
|
||||
|
||||
var position = PixelSize / 2 + offset * stretch * UIScale;
|
||||
var scale = Scale * UIScale * stretch;
|
||||
renderHandle.DrawEntity(uid, position, scale, _worldRotation, _eyeRotation, OverrideDirection, _sprite);
|
||||
renderHandle.DrawEntity(uid, position, scale, _worldRotation, _eyeRotation, OverrideDirection, Sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ Mouse Pos:
|
||||
{playerWorldOffset}
|
||||
{playerCoordinates}
|
||||
Rotation: {playerRotation.Degrees:F2}°
|
||||
EntId: {entityTransform.Owner}
|
||||
EntId: {controlledEntity}
|
||||
GridUid: {entityTransform.GridUid}
|
||||
Grid Rotation: {gridRotation.Degrees:F2}°");
|
||||
}
|
||||
|
||||
@@ -69,12 +69,11 @@ public abstract class UIScreen : LayoutContainer
|
||||
|
||||
public void RemoveWidget<T>() where T : UIWidget, new()
|
||||
{
|
||||
if (_widgets.TryGetValue(typeof(T), out var widget))
|
||||
{
|
||||
RemoveChild(widget);
|
||||
}
|
||||
if (!_widgets.Remove(typeof(T), out var widget))
|
||||
return;
|
||||
|
||||
_widgets.Remove(typeof(T));
|
||||
widget.Parent?.RemoveChild(widget);
|
||||
RemoveChildren(widget);
|
||||
}
|
||||
|
||||
internal void OnRemoved()
|
||||
@@ -103,6 +102,14 @@ public abstract class UIScreen : LayoutContainer
|
||||
AddChild(widget);
|
||||
}
|
||||
|
||||
public void AddWidgetDirect(UIWidget widget)
|
||||
{
|
||||
if (!_widgets.TryAdd(widget.GetType(), widget))
|
||||
throw new Exception("Tried to add duplicate widget to screen!");
|
||||
|
||||
RegisterChildren(widget);
|
||||
}
|
||||
|
||||
public T? GetWidget<T>() where T : UIWidget, new()
|
||||
{
|
||||
return (T?) _widgets.GetValueOrDefault(typeof(T));
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Map.Components;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
@@ -67,13 +67,13 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
var xVal = float.Parse(x.Text, CultureInfo.InvariantCulture);
|
||||
var yVal = float.Parse(y.Text, CultureInfo.InvariantCulture);
|
||||
|
||||
if (!mapManager.TryGetGrid(gridVal, out var grid))
|
||||
if (!entityManager.HasComponent<MapGridComponent>(gridVal))
|
||||
{
|
||||
ValueChanged(new EntityCoordinates(EntityUid.Invalid, new(xVal, yVal)));
|
||||
return;
|
||||
}
|
||||
|
||||
ValueChanged(new EntityCoordinates(grid.Owner, new(xVal, yVal)));
|
||||
ValueChanged(new EntityCoordinates(gridVal, new(xVal, yVal)));
|
||||
}
|
||||
|
||||
if (!ReadOnly)
|
||||
|
||||
@@ -120,7 +120,11 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
};
|
||||
top.HorizontalExpand = true;
|
||||
hBox.AddChild(top);
|
||||
hBox.AddChild(new SpriteView {Sprite = sprite, OverrideDirection = Direction.South});
|
||||
|
||||
var view = new SpriteView { OverrideDirection = Direction.South };
|
||||
view.SetEntity(_entity);
|
||||
hBox.AddChild(view);
|
||||
|
||||
vBoxContainer.AddChild(hBox);
|
||||
}
|
||||
else
|
||||
@@ -270,7 +274,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
ViewVariablesManager.OpenVV(
|
||||
new ViewVariablesComponentSelector(_entityManager.GetNetEntity(_entity), componentType.FullName));
|
||||
new ViewVariablesComponentSelector(_netEntity, componentType.FullName));
|
||||
};
|
||||
removeButton.OnPressed += _ =>
|
||||
{
|
||||
@@ -431,8 +435,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
try
|
||||
{
|
||||
var comp = (Component) componentFactory.GetComponent(registration.Type);
|
||||
comp.Owner = _entity;
|
||||
var comp = componentFactory.GetComponent(registration.Type);
|
||||
_entityManager.AddComponent(_entity, comp);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -306,7 +306,9 @@ namespace Robust.Server
|
||||
_modLoader.SetUseLoadContext(!ContentStart);
|
||||
_modLoader.SetEnableSandboxing(Options.Sandboxing);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(Options.AssemblyDirectory, Options.ContentModulePrefix))
|
||||
var resourceManifest = ResourceManifestData.LoadResourceManifest(_resources);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(Options.AssemblyDirectory, resourceManifest.AssemblyPrefix ?? Options.ContentModulePrefix))
|
||||
{
|
||||
_logger.Fatal("Errors while loading content assemblies.");
|
||||
return true;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -47,11 +46,7 @@ namespace Robust.Server.Console.Commands
|
||||
shell.WriteLine($"Entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName} already has a {componentName} component.");
|
||||
}
|
||||
|
||||
var component = (Component) _componentFactory.GetComponent(registration.Type);
|
||||
|
||||
#pragma warning disable CS0618
|
||||
component.Owner = uid.Value;
|
||||
#pragma warning restore CS0618
|
||||
var component = _componentFactory.GetComponent(registration.Type);
|
||||
_entityManager.AddComponent(uid.Value, component);
|
||||
|
||||
shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName}.");
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Server.Console.Commands
|
||||
{
|
||||
public sealed class DeleteCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
public override string Command => "delete";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine("You should provide exactly one argument.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NetEntity.TryParse(args[0], out var entityNet))
|
||||
{
|
||||
shell.WriteLine("Invalid entity UID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entityManager.TryGetEntity(entityNet, out var entity) || !_entityManager.EntityExists(entity))
|
||||
{
|
||||
shell.WriteLine("That entity does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
_entityManager.DeleteEntity(entity.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -164,16 +163,40 @@ namespace Robust.Server.Console
|
||||
var message = new MsgConCmdReg();
|
||||
|
||||
var counter = 0;
|
||||
message.Commands = new MsgConCmdReg.Command[AvailableCommands.Count];
|
||||
var toolshedCommands = _toolshed.DefaultEnvironment.AllCommands().ToArray();
|
||||
message.Commands = new List<MsgConCmdReg.Command>(AvailableCommands.Count + toolshedCommands.Length);
|
||||
var commands = new HashSet<string>();
|
||||
|
||||
foreach (var command in AvailableCommands.Values)
|
||||
{
|
||||
message.Commands[counter++] = new MsgConCmdReg.Command
|
||||
if (!commands.Add(command.Command))
|
||||
{
|
||||
Sawmill.Error($"Duplicate command: {command.Command}");
|
||||
continue;
|
||||
}
|
||||
message.Commands.Add(new MsgConCmdReg.Command
|
||||
{
|
||||
Name = command.Command,
|
||||
Description = command.Description,
|
||||
Help = command.Help
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var spec in toolshedCommands)
|
||||
{
|
||||
var name = spec.FullName();
|
||||
if (!commands.Add(name))
|
||||
{
|
||||
Sawmill.Warning($"Duplicate toolshed command: {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
message.Commands.Add(new MsgConCmdReg.Command
|
||||
{
|
||||
Name = name,
|
||||
Description = spec.Cmd.Description(spec.SubCommand),
|
||||
Help = spec.Cmd.GetHelp(spec.SubCommand)
|
||||
});
|
||||
}
|
||||
|
||||
NetManager.ServerSendMessage(message, senderConnection);
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Robust.Server.Console
|
||||
break;
|
||||
|
||||
case ConsoleKey.Backspace:
|
||||
if (currentBuffer.Length > 0)
|
||||
if (currentBuffer.Length > 0 && internalCursor > 0)
|
||||
{
|
||||
currentBuffer = currentBuffer.Remove(internalCursor - 1, 1);
|
||||
internalCursor--;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
@@ -16,13 +13,5 @@ namespace Robust.Server.GameObjects
|
||||
/// </summary>
|
||||
[DataField("layer")]
|
||||
public int Layer = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[Obsolete("Do not access directly, only exists for VV")]
|
||||
public int LayerVV
|
||||
{
|
||||
get => Layer;
|
||||
set => EntitySystem.Get<VisibilitySystem>().SetLayer(this, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -733,7 +732,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// get ents that the grids will bind to
|
||||
var gridComps = new MapGridComponent[yamlGrids.Count];
|
||||
var gridComps = new Entity<MapGridComponent>[yamlGrids.Count];
|
||||
var gridQuery = _serverEntityManager.GetEntityQuery<MapGridComponent>();
|
||||
|
||||
// linear search for new grid comps
|
||||
@@ -745,7 +744,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
// These should actually be new, pre-init
|
||||
DebugTools.Assert(gridComp.LifeStage == ComponentLifeStage.Added);
|
||||
|
||||
gridComps[gridComp.GridIndex] = gridComp;
|
||||
gridComps[gridComp.GridIndex] = new Entity<MapGridComponent>(uid, gridComp);
|
||||
}
|
||||
|
||||
for (var index = 0; index < yamlGrids.Count; index++)
|
||||
@@ -764,18 +763,18 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
MappingDataNode yamlGridInfo = (MappingDataNode)yamlGrid["settings"];
|
||||
SequenceDataNode yamlGridChunks = (SequenceDataNode)yamlGrid["chunks"];
|
||||
|
||||
var grid = AllocateMapGrid(gridComp, yamlGridInfo);
|
||||
var gridUid = grid.Owner;
|
||||
AllocateMapGrid(gridComp, yamlGridInfo);
|
||||
var gridUid = gridComp.Owner;
|
||||
|
||||
foreach (var chunkNode in yamlGridChunks.Cast<MappingDataNode>())
|
||||
{
|
||||
var (chunkOffsetX, chunkOffsetY) = _serManager.Read<Vector2i>(chunkNode["ind"]);
|
||||
_serManager.Read(chunkNode, _context, instanceProvider: () => _mapSystem.GetOrAddChunk(gridUid, grid, chunkOffsetX, chunkOffsetY), notNullableOverride: true);
|
||||
_serManager.Read(chunkNode, _context, instanceProvider: () => _mapSystem.GetOrAddChunk(gridUid, gridComp, chunkOffsetX, chunkOffsetY), notNullableOverride: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MapGridComponent AllocateMapGrid(MapGridComponent gridComp, MappingDataNode yamlGridInfo)
|
||||
private static void AllocateMapGrid(MapGridComponent gridComp, MappingDataNode yamlGridInfo)
|
||||
{
|
||||
// sane defaults
|
||||
ushort csz = 16;
|
||||
@@ -795,8 +794,6 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
gridComp.ChunkSize = csz;
|
||||
gridComp.TileSize = tsz;
|
||||
|
||||
return gridComp;
|
||||
}
|
||||
|
||||
private void StartupEntities(MapData data)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using System;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
public sealed class VisibilitySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly PvsSystem _pvs = default!;
|
||||
[Dependency] private readonly IViewVariablesManager _vvManager = default!;
|
||||
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
@@ -21,6 +22,15 @@ namespace Robust.Server.GameObjects
|
||||
_visiblityQuery = GetEntityQuery<VisibilityComponent>();
|
||||
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChange);
|
||||
EntityManager.EntityInitialized += OnEntityInit;
|
||||
|
||||
_vvManager.GetTypeHandler<VisibilityComponent>()
|
||||
.AddPath(nameof(VisibilityComponent.Layer), (_, comp) => comp.Layer, (uid, value, comp) =>
|
||||
{
|
||||
if (!Resolve(uid, ref comp))
|
||||
return;
|
||||
|
||||
SetLayer(uid, comp, value);
|
||||
});
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -40,12 +50,6 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(uid, visibilityComponent: component);
|
||||
}
|
||||
|
||||
[Obsolete("Use overload that takes an EntityUid instead")]
|
||||
public void AddLayer(VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
AddLayer(component.Owner, component, layer, refresh);
|
||||
}
|
||||
|
||||
public void RemoveLayer(EntityUid uid, VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
if ((layer & component.Layer) != layer)
|
||||
@@ -57,12 +61,6 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(uid, visibilityComponent: component);
|
||||
}
|
||||
|
||||
[Obsolete("Use overload that takes an EntityUid instead")]
|
||||
public void RemoveLayer(VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
RemoveLayer(component.Owner, component, layer, refresh);
|
||||
}
|
||||
|
||||
public void SetLayer(EntityUid uid, VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
if (component.Layer == layer)
|
||||
@@ -74,12 +72,6 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(uid, visibilityComponent: component);
|
||||
}
|
||||
|
||||
[Obsolete("Use overload that takes an EntityUid instead")]
|
||||
public void SetLayer(VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
SetLayer(component.Owner, component, layer, refresh);
|
||||
}
|
||||
|
||||
private void OnParentChange(ref EntParentChangedMessage ev)
|
||||
{
|
||||
RefreshVisibility(ev.Entity);
|
||||
@@ -127,12 +119,6 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use overload that takes an EntityUid instead")]
|
||||
public void RefreshVisibility(VisibilityComponent visibilityComponent)
|
||||
{
|
||||
RefreshVisibility(visibilityComponent.Owner, visibilityComponent);
|
||||
}
|
||||
|
||||
private int GetParentVisibilityMask(EntityUid uid, VisibilityComponent? visibilityComponent = null)
|
||||
{
|
||||
int visMask = 1; // apparently some content expects everything to have the first bit/flag set to true.
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace Robust.Server.GameObjects
|
||||
#endif
|
||||
|
||||
private ISawmill _netEntSawmill = default!;
|
||||
private EntityQuery<ActorComponent> _actorQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -53,6 +54,12 @@ namespace Robust.Server.GameObjects
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
_actorQuery = GetEntityQuery<ActorComponent>();
|
||||
}
|
||||
|
||||
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype)
|
||||
{
|
||||
return AllocEntity(prototype, out _);
|
||||
@@ -78,15 +85,15 @@ namespace Robust.Server.GameObjects
|
||||
StartEntity(entity);
|
||||
}
|
||||
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, 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, 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,
|
||||
@@ -109,15 +116,10 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
public override EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (uid == null)
|
||||
return null;
|
||||
|
||||
TryGetComponent(uid, out ActorComponent? actor);
|
||||
|
||||
return base.ToPrettyString(uid).Value with { Session = actor?.PlayerSession };
|
||||
_actorQuery.TryGetComponent(uid, out ActorComponent? actor);
|
||||
return base.ToPrettyString(uid) with { Session = actor?.PlayerSession };
|
||||
}
|
||||
|
||||
#region IEntityNetworkManager impl
|
||||
|
||||
@@ -95,7 +95,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent to a certain <see cref="ICommonSession"/>.
|
||||
/// </summary>
|
||||
private readonly Dictionary<ICommonSession, HashSet<TIndex>> _localOverrides = new();
|
||||
private readonly Dictionary<ICommonSession, HashSet<TIndex>> _sessionOverrides = new();
|
||||
|
||||
/// <summary>
|
||||
/// Which <see cref="TIndex"/> where last seen/sent to a certain <see cref="ICommonSession"/>.
|
||||
@@ -207,7 +207,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
public bool TryGetChunk(EntityUid gridId, Vector2i chunkIndices, [NotNullWhen(true)] out HashSet<TIndex>? indices) =>
|
||||
_gridChunkContents[gridId].TryGetValue(chunkIndices, out indices);
|
||||
|
||||
public HashSet<TIndex>.Enumerator GetElementsForSession(ICommonSession session) => _localOverrides[session].GetEnumerator();
|
||||
public HashSet<TIndex>.Enumerator GetSessionOverrides(ICommonSession session) => _sessionOverrides[session].GetEnumerator();
|
||||
|
||||
private void AddIndexInternal(TIndex index, IIndexLocation location, HashSet<IChunkIndexLocation> dirtyChunks)
|
||||
{
|
||||
@@ -226,10 +226,10 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
gridLoc.Add(index);
|
||||
dirtyChunks.Add(gridChunkLocation);
|
||||
break;
|
||||
case LocalOverride localOverride:
|
||||
// might be gone due to disconnects
|
||||
if(!_localOverrides.ContainsKey(localOverride.Session)) return;
|
||||
_localOverrides[localOverride.Session].Add(index);
|
||||
case SessionOverride sessionOverride:
|
||||
if (!_sessionOverrides.TryGetValue(sessionOverride.Session, out var set))
|
||||
return;
|
||||
set.Add(index);
|
||||
break;
|
||||
case MapChunkLocation mapChunkLocation:
|
||||
// might be gone due to map-deletions
|
||||
@@ -253,16 +253,14 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
switch (location)
|
||||
{
|
||||
case GlobalOverride global:
|
||||
if (global.Recursive)
|
||||
_globalRecursiveOverrides.Remove(index);
|
||||
else
|
||||
_globalOverrides.Remove(index);
|
||||
var set = global.Recursive ? _globalRecursiveOverrides : _globalOverrides;
|
||||
set.Remove(index);
|
||||
break;
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
_gridChunkContents[gridChunkLocation.GridId][gridChunkLocation.ChunkIndices].Remove(index);
|
||||
break;
|
||||
case LocalOverride localOverride:
|
||||
_localOverrides[localOverride.Session].Remove(index);
|
||||
case SessionOverride sessionOverride:
|
||||
_sessionOverrides.GetValueOrDefault(sessionOverride.Session)?.Remove(index);
|
||||
break;
|
||||
case MapChunkLocation mapChunkLocation:
|
||||
_mapChunkContents[mapChunkLocation.MapId][mapChunkLocation.ChunkIndices].Remove(index);
|
||||
@@ -276,7 +274,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <inheritdoc />
|
||||
public bool AddPlayer(ICommonSession session)
|
||||
{
|
||||
return _localOverrides.TryAdd(session, new()) & _lastSeen.TryAdd(session, new());
|
||||
return _sessionOverrides.TryAdd(session, new()) & _lastSeen.TryAdd(session, new());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -292,7 +290,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <inheritdoc />
|
||||
public bool RemovePlayer(ICommonSession session)
|
||||
{
|
||||
if (_localOverrides.Remove(session, out var indices))
|
||||
if (_sessionOverrides.Remove(session, out var indices))
|
||||
{
|
||||
foreach (var index in indices)
|
||||
{
|
||||
@@ -412,7 +410,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride && oldLocation is LocalOverride)
|
||||
if (!removeFromOverride && oldLocation is SessionOverride)
|
||||
return;
|
||||
|
||||
if (oldLocation is GlobalOverride global &&
|
||||
@@ -426,28 +424,29 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
|
||||
/// <summary>
|
||||
/// Updates an <see cref="TIndex"/> to be sent to a specific <see cref="ICommonSession"/> at all times.
|
||||
/// This will always also send all children of the given entity.
|
||||
/// </summary>
|
||||
/// <param name="index">The <see cref="TIndex"/> to update.</param>
|
||||
/// <param name="session">The <see cref="ICommonSession"/> receiving the object.</param>
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void UpdateIndex(TIndex index, ICommonSession session, bool removeFromOverride = false)
|
||||
public void AddSessionOverride(TIndex index, ICommonSession session, bool removeFromOverride)
|
||||
{
|
||||
if (!TryGetLocation(index, out var oldLocation))
|
||||
{
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
RegisterUpdate(index, new SessionOverride(session));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride || oldLocation is GlobalOverride)
|
||||
if (!removeFromOverride && oldLocation is GlobalOverride)
|
||||
return;
|
||||
|
||||
if (oldLocation is LocalOverride local &&
|
||||
if (oldLocation is SessionOverride local &&
|
||||
(!removeFromOverride || local.Session == session))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
RegisterUpdate(index, new SessionOverride(session));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -460,7 +459,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
{
|
||||
if (!removeFromOverride
|
||||
&& TryGetLocation(index, out var oldLocation)
|
||||
&& oldLocation is GlobalOverride or LocalOverride)
|
||||
&& oldLocation is GlobalOverride or SessionOverride)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -510,7 +509,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or SessionOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
if (oldLocation is GridChunkLocation oldGrid &&
|
||||
@@ -541,7 +540,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or SessionOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
// Is this entity just returning to its old location?
|
||||
@@ -641,14 +640,14 @@ public struct GlobalOverride : IIndexLocation
|
||||
}
|
||||
}
|
||||
|
||||
public struct LocalOverride : IIndexLocation
|
||||
public struct SessionOverride : IIndexLocation
|
||||
{
|
||||
public LocalOverride(ICommonSession session)
|
||||
public SessionOverride(ICommonSession session)
|
||||
{
|
||||
Session = session;
|
||||
}
|
||||
|
||||
public ICommonSession Session { get; init; }
|
||||
public readonly ICommonSession Session;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -22,13 +22,14 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure that an entity is always sent to a specific client. Overrides any global or pre-existing
|
||||
/// client-specific overrides.
|
||||
/// Used to ensure that an entity is always sent to a specific client. By default this overrides any global or pre-existing
|
||||
/// client-specific overrides. Unlike global overrides, this is always recursive.
|
||||
/// </summary>
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session, bool removeExistingOverride = true)
|
||||
{
|
||||
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), session, removeExistingOverride);
|
||||
_pvs.EntityPVSCollection.AddSessionOverride(GetNetEntity(uid), session, removeExistingOverride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -188,6 +188,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
Log.Warning(sb.ToString());
|
||||
|
||||
sessionData.LastSeenAt.Clear();
|
||||
sessionData.LastLeftView.Clear();
|
||||
|
||||
if (sessionData.Overflow != null)
|
||||
{
|
||||
@@ -256,6 +257,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
foreach (var sessionData in PlayerData.Values)
|
||||
{
|
||||
sessionData.LastSeenAt.Remove(metadata.NetEntity);
|
||||
sessionData.LastLeftView.Remove(metadata.NetEntity);
|
||||
if (sessionData.SentEntities.TryGetValue(previousTick, out var ents))
|
||||
ents.Remove(metadata.NetEntity);
|
||||
}
|
||||
@@ -703,6 +705,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
sessionData.SentEntities.TryGetValue(toTick - 1, out var lastSent);
|
||||
var lastAcked = sessionData.LastAcked?.Data;
|
||||
var lastSeen = sessionData.LastSeenAt;
|
||||
var lastLeft = sessionData.LastLeftView;
|
||||
var visibleEnts = _visSetPool.Get();
|
||||
|
||||
if (visibleEnts.Count != 0)
|
||||
@@ -730,7 +733,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
foreach (var rootNode in cache.Value.tree.RootNodes)
|
||||
{
|
||||
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, cache.Value.metadata, stack, in fromTick,
|
||||
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, lastLeft, cache.Value.metadata, stack, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
}
|
||||
@@ -741,7 +744,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
{
|
||||
var netEntity = globalEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, lastLeft, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
globalEnumerator.Dispose();
|
||||
@@ -751,39 +754,45 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
{
|
||||
var netEntity = globalRecursiveEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, lastLeft, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
globalRecursiveEnumerator.Dispose();
|
||||
|
||||
var localEnumerator = _entityPvsCollection.GetElementsForSession(session);
|
||||
while (localEnumerator.MoveNext())
|
||||
var sessionOverrides = _entityPvsCollection.GetSessionOverrides(session);
|
||||
while (sessionOverrides.MoveNext())
|
||||
{
|
||||
var netEntity = localEnumerator.Current;
|
||||
var netEntity = sessionOverrides.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, lastLeft, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
localEnumerator.Dispose();
|
||||
sessionOverrides.Dispose();
|
||||
|
||||
foreach (var viewerEntity in viewers)
|
||||
{
|
||||
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, lastLeft, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
var expandEvent = new ExpandPvsEvent(session);
|
||||
RaiseLocalEvent(ref expandEvent);
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
if (expandEvent.Entities != null)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, lastLeft, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
if (expandEvent.RecursiveEntities != null)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, lastLeft, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
}
|
||||
|
||||
var entityStates = new List<EntityState>(entStateCount);
|
||||
@@ -820,7 +829,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
|
||||
// tell a client to detach entities that have left their view
|
||||
var leftView = ProcessLeavePVS(visibleEnts, lastSent);
|
||||
var leftView = ProcessLeavePvs(visibleEnts, lastSent, lastLeft);
|
||||
|
||||
if (sessionData.SentEntities.Add(toTick, visibleEnts, out var oldEntry))
|
||||
{
|
||||
@@ -857,18 +866,26 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
/// Figure out what entities are no longer visible to the client. These entities are sent reliably to the client
|
||||
/// in a separate net message.
|
||||
/// </summary>
|
||||
private List<NetEntity>? ProcessLeavePVS(
|
||||
private List<NetEntity>? ProcessLeavePvs(
|
||||
Dictionary<NetEntity, PvsEntityVisibility> visibleEnts,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent)
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, GameTick> lastLeft)
|
||||
{
|
||||
if (lastSent == null)
|
||||
return null;
|
||||
|
||||
var leftView = new List<NetEntity>();
|
||||
var tick = _gameTiming.CurTick;
|
||||
var minSize = Math.Max(0, lastSent.Count - lastSent.Count);
|
||||
var leftView = new List<NetEntity>(minSize);
|
||||
|
||||
foreach (var netEntity in lastSent.Keys)
|
||||
{
|
||||
if (!visibleEnts.ContainsKey(netEntity))
|
||||
{
|
||||
leftView.Add(netEntity);
|
||||
lastLeft[netEntity] = tick;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return leftView.Count > 0 ? leftView : null;
|
||||
@@ -880,6 +897,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
Dictionary<NetEntity, GameTick> lastLeft,
|
||||
Dictionary<NetEntity, MetaDataComponent> metaDataCache,
|
||||
Stack<NetEntity> stack,
|
||||
in GameTick fromTick,
|
||||
@@ -902,7 +920,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(toSend, currentNodeIndex, out var exists);
|
||||
if (!exists)
|
||||
{
|
||||
var (entered, shouldAdd) = ProcessEntry(in currentNodeIndex, lastAcked, lastSent, lastSeen,
|
||||
var (entered, shouldAdd) = ProcessEntry(in currentNodeIndex, lastAcked, lastSent, lastSeen, lastLeft, fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
|
||||
if (!shouldAdd)
|
||||
@@ -932,6 +950,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
Dictionary<NetEntity, GameTick> lastLeft,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -947,7 +966,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
var parent = xform.ParentUid;
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in fromTick,
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, lastLeft, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget,
|
||||
in enteredEntityBudget))
|
||||
{
|
||||
@@ -957,7 +976,6 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var metadata = _metaQuery.GetComponent(uid);
|
||||
var netEntity = metadata.NetEntity;
|
||||
|
||||
//did we already get added?
|
||||
// Note that we check this AFTER adding parents. This is because while this entity may already have been added
|
||||
// to the toSend set, it doesn't guarantee that its parents have been. E.g., if a player ghost just teleported
|
||||
// to follow a far away entity, the player's own entity is still being sent, but we need to ensure that we also
|
||||
@@ -965,17 +983,13 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(toSend, netEntity, out var exists);
|
||||
if (!exists)
|
||||
{
|
||||
// TODO PERFORMANCE.
|
||||
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
|
||||
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
|
||||
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
|
||||
var (entered, _) = ProcessEntry(in netEntity, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
var (entered, _) = ProcessEntry(in netEntity, lastAcked, lastSent, lastSeen, lastLeft, fromTick, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
AddToSendSet(in netEntity, metadata, ref value, toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
if (addChildren)
|
||||
{
|
||||
RecursivelyAddChildren(xform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
RecursivelyAddChildren(xform, lastAcked, lastSent, toSend, lastSeen, lastLeft, fromTick, ref newEntityCount,
|
||||
ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
@@ -987,6 +1001,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
Dictionary<NetEntity, GameTick> lastLeft,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -1005,22 +1020,23 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(toSend, childNetEntity, out var exists);
|
||||
if (!exists)
|
||||
{
|
||||
var (entered, _) = ProcessEntry(in childNetEntity, lastAcked, lastSent, lastSeen, ref newEntityCount,
|
||||
var (entered, _) = ProcessEntry(in childNetEntity, lastAcked, lastSent, lastSeen, lastLeft, fromTick, ref newEntityCount,
|
||||
ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
|
||||
AddToSendSet(in childNetEntity, metadata, ref value, toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, lastLeft, fromTick, ref newEntityCount,
|
||||
ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
}
|
||||
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(
|
||||
in NetEntity netEntity,
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(in NetEntity netEntity,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
Dictionary<NetEntity, GameTick> lastLeft,
|
||||
GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
in int newEntityBudget,
|
||||
@@ -1028,16 +1044,15 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
{
|
||||
var enteredSinceLastSent = lastSent == null || !lastSent.ContainsKey(netEntity);
|
||||
|
||||
var entered = enteredSinceLastSent || // OR, entered since last ack:
|
||||
lastAcked == null || !lastAcked.ContainsKey(netEntity);
|
||||
var entered = enteredSinceLastSent
|
||||
|| lastAcked == null
|
||||
|| !lastAcked.ContainsKey(netEntity) // entered since last acked
|
||||
|| lastLeft.GetValueOrDefault(netEntity) >= fromTick; // Just in case a packet was lost. I love dictionary lookups
|
||||
|
||||
// If the entity is entering, but we already sent this entering entity in the last message, we won't add it to
|
||||
// the budget. Chances are the packet will arrive in a nice and orderly fashion, and the client will stick to
|
||||
// their requested budget. However this can cause issues if a packet gets dropped, because a player may create
|
||||
// 2x or more times the normal entity creation budget.
|
||||
//
|
||||
// The fix for that would be to just also give the PVS budget a client-side aspect that controls entity creation
|
||||
// rate.
|
||||
if (enteredSinceLastSent)
|
||||
{
|
||||
if (newEntityCount >= newEntityBudget || enteredEntityCount >= enteredEntityBudget)
|
||||
@@ -1114,8 +1129,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var query = EntityManager.AllEntityQueryEnumerator<MetaDataComponent>();
|
||||
while (query.MoveNext(out var uid, out var md))
|
||||
{
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized, $"Entity {ToPrettyString(uid)} has not been initialized");
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating, $"Entity {ToPrettyString(uid)} is/has been terminated");
|
||||
if (md.EntityLastModifiedTick <= fromTick)
|
||||
continue;
|
||||
|
||||
@@ -1150,10 +1165,10 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
if (!toSend.Add(uid) || !_metaQuery.TryGetComponent(uid, out var md))
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick);
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized, $"Entity {ToPrettyString(uid)} has not been initialized");
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating, $"Entity {ToPrettyString(uid)} is/has been terminated");
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick, $"Entity {ToPrettyString(uid)} last modified tick is less than creation tick");
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick, $"Entity {ToPrettyString(uid)} last modified tick is less than from tick");
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
|
||||
@@ -1177,10 +1192,10 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
if (!toSend.Add(uid) || !_metaQuery.TryGetComponent(uid, out var md))
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick);
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized, $"Entity {ToPrettyString(uid)} has not been initialized");
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating, $"Entity {ToPrettyString(uid)} is/has been terminated");
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick, $"Entity {ToPrettyString(uid)} last modified tick is less than creation tick");
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick, $"Entity {ToPrettyString(uid)} last modified tick is less than from tick");
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
if (!state.Empty)
|
||||
@@ -1368,6 +1383,11 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
/// </summary>
|
||||
public readonly Dictionary<NetEntity, GameTick> LastSeenAt = new();
|
||||
|
||||
/// <summary>
|
||||
/// Tick at which an entity last left a player's PVS view.
|
||||
/// </summary>
|
||||
public readonly Dictionary<NetEntity, GameTick> LastLeftView = new();
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SentEntities"/> overflow in case a player's last ack is more than <see cref="DirtyBufferSize"/> ticks behind the current tick.
|
||||
/// </summary>
|
||||
@@ -1390,20 +1410,20 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public readonly struct ExpandPvsEvent
|
||||
public struct ExpandPvsEvent
|
||||
{
|
||||
public readonly IPlayerSession Session;
|
||||
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set.
|
||||
/// </summary>
|
||||
public readonly List<EntityUid> Entities = new();
|
||||
public List<EntityUid>? Entities;
|
||||
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set. Unlike <see cref="Entities"/> this will also
|
||||
/// recursively add all children of the given entity.
|
||||
/// </summary>
|
||||
public readonly List<EntityUid> RecursiveEntities = new();
|
||||
public List<EntityUid>? RecursiveEntities;
|
||||
|
||||
public ExpandPvsEvent(IPlayerSession session)
|
||||
{
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace Robust.Server.Physics
|
||||
for (var i = 0; i < grids.Count - 1; i++)
|
||||
{
|
||||
var group = grids[i];
|
||||
var newGrid = _mapManager.CreateGrid(mapId);
|
||||
var newGrid = _mapManager.CreateGridEntity(mapId);
|
||||
var newGridUid = newGrid.Owner;
|
||||
var newGridXform = xformQuery.GetComponent(newGridUid);
|
||||
newGrids[i] = newGridUid;
|
||||
@@ -287,7 +287,7 @@ namespace Robust.Server.Physics
|
||||
}
|
||||
}
|
||||
|
||||
newGrid.SetTiles(tileData);
|
||||
newGrid.Comp.SetTiles(tileData);
|
||||
DebugTools.Assert(_mapManager.IsGrid(newGridUid), "A split grid had no tiles?");
|
||||
|
||||
// Set tiles on new grid + update anchored entities
|
||||
|
||||
@@ -199,11 +199,11 @@ namespace Robust.Server.Placement
|
||||
}
|
||||
else if (tileType != 0) // create a new grid
|
||||
{
|
||||
var newGrid = _mapManager.CreateGrid(coordinates.GetMapId(_entityManager));
|
||||
var newGridXform = _entityManager.GetComponent<TransformComponent>(newGrid.Owner);
|
||||
newGridXform.WorldPosition = coordinates.Position - newGrid.TileSizeHalfVector; // assume bottom left tile origin
|
||||
var tilePos = newGrid.WorldToTile(coordinates.Position);
|
||||
newGrid.SetTile(tilePos, new Tile(tileType));
|
||||
var newGrid = _mapManager.CreateGridEntity(coordinates.GetMapId(_entityManager));
|
||||
var newGridXform = _entityManager.GetComponent<TransformComponent>(newGrid);
|
||||
newGridXform.WorldPosition = coordinates.Position - newGrid.Comp.TileSizeHalfVector; // assume bottom left tile origin
|
||||
var tilePos = newGrid.Comp.WorldToTile(coordinates.Position);
|
||||
newGrid.Comp.SetTile(tilePos, new Tile(tileType));
|
||||
|
||||
var placementEraseEvent = new PlacementTileEvent(tileType, coordinates, placingUserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
|
||||
|
||||
12
Robust.Server/Spawners/TimedDespawnSystem.cs
Normal file
12
Robust.Server/Spawners/TimedDespawnSystem.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Spawners;
|
||||
|
||||
namespace Robust.Server.Spawners;
|
||||
|
||||
public sealed class TimedDespawnSystem : SharedTimedDespawnSystem
|
||||
{
|
||||
protected override bool CanDelete(EntityUid uid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ namespace Robust.Server.ViewVariables
|
||||
// Auto-dirty component. Only works when modifying a field that is directly on a component,
|
||||
// Does not work for nested objects.
|
||||
if (Object is Component comp)
|
||||
EntityManager.Dirty(comp);
|
||||
EntityManager.Dirty(comp.Owner, comp);
|
||||
}
|
||||
|
||||
public bool TryGetRelativeObject(object[] propertyIndex, out object? value)
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Robust.Server.ViewVariables
|
||||
}
|
||||
|
||||
if (value is EntityUid uid)
|
||||
return IoCManager.Resolve<IEntityManager>().GetNetEntity(uid);
|
||||
return IoCManager.Resolve<IEntityManager>().GetComponentOrNull<MetaDataComponent>(uid)?.NetEntity ?? NetEntity.Invalid;
|
||||
|
||||
var valType = value.GetType();
|
||||
if (!valType.IsValueType)
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using static Microsoft.CodeAnalysis.SymbolDisplayFormat;
|
||||
|
||||
namespace Robust.Shared.CompNetworkGenerator
|
||||
{
|
||||
@@ -14,15 +15,23 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
{
|
||||
private const string ClassAttributeName = "Robust.Shared.Analyzers.AutoGenerateComponentStateAttribute";
|
||||
private const string MemberAttributeName = "Robust.Shared.Analyzers.AutoNetworkedFieldAttribute";
|
||||
|
||||
private const string GlobalEntityUidName = "global::Robust.Shared.GameObjects.EntityUid";
|
||||
private const string GlobalNullableEntityUidName = "global::Robust.Shared.GameObjects.EntityUid?";
|
||||
|
||||
private const string GlobalEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates";
|
||||
private const string GlobalNullableEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates?";
|
||||
|
||||
private const string GlobalEntityUidSetName = "global::System.Collections.Generic.HashSet<global::Robust.Shared.GameObjects.EntityUid>";
|
||||
private const string GlobalNetEntityUidSetName = "global::System.Collections.Generic.HashSet<global::Robust.Shared.GameObjects.NetEntity>";
|
||||
|
||||
private const string GlobalEntityUidListName = "global::System.Collections.Generic.List<global::Robust.Shared.GameObjects.EntityUid>";
|
||||
private const string GlobalNetEntityUidListName = "global::System.Collections.Generic.List<global::Robust.Shared.GameObjects.NetEntity>";
|
||||
|
||||
private const string GlobalDictionaryName = "global::System.Collections.Generic.Dictionary<TKey, TValue>";
|
||||
private const string GlobalHashSetName = "global::System.Collections.Generic.HashSet<T>";
|
||||
private const string GlobalListName = "global::System.Collections.Generic.List<T>";
|
||||
|
||||
private static string GenerateSource(in GeneratorExecutionContext context, INamedTypeSymbol classSymbol, CSharpCompilation comp, bool raiseAfterAutoHandle)
|
||||
{
|
||||
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString();
|
||||
@@ -30,7 +39,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
var stateName = $"{componentName}_AutoState";
|
||||
|
||||
var members = classSymbol.GetMembers();
|
||||
var fields = new List<(ITypeSymbol Type, string FieldName, AttributeData Attribute)>();
|
||||
var fields = new List<(ITypeSymbol Type, string FieldName)>();
|
||||
var fieldAttr = comp.GetTypeByMetadataName(MemberAttributeName);
|
||||
|
||||
foreach (var mem in members)
|
||||
@@ -47,7 +56,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
switch (mem)
|
||||
{
|
||||
case IFieldSymbol field:
|
||||
fields.Add((field.Type, field.Name, attribute));
|
||||
fields.Add((field.Type, field.Name));
|
||||
break;
|
||||
case IPropertySymbol prop:
|
||||
{
|
||||
@@ -83,7 +92,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
continue;
|
||||
}
|
||||
|
||||
fields.Add((prop.Type, prop.Name, attribute));
|
||||
fields.Add((prop.Type, prop.Name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -121,9 +130,9 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
// component.Count = state.Count;
|
||||
var handleStateSetters = new StringBuilder();
|
||||
|
||||
foreach (var (type, name, attribute) in fields)
|
||||
foreach (var (type, name) in fields)
|
||||
{
|
||||
var typeDisplayStr = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
var typeDisplayStr = type.ToDisplayString(FullyQualifiedFormat);
|
||||
var nullable = type.NullableAnnotation == NullableAnnotation.Annotated;
|
||||
var nullableAnnotation = nullable ? "?" : string.Empty;
|
||||
|
||||
@@ -150,31 +159,6 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = EnsureCoordinates<{componentName}>(state.{name}, uid);");
|
||||
|
||||
break;
|
||||
default:
|
||||
stateFields.Append($@"
|
||||
public {typeDisplayStr} {name} = default!;");
|
||||
|
||||
if (attribute.ConstructorArguments[0].Value is bool val && val)
|
||||
{
|
||||
// get first ctor arg of the field attribute, which determines whether the field should be cloned
|
||||
// (like if its a dict or list)
|
||||
getStateInit.Append($@"
|
||||
{name} = component.{name},");
|
||||
|
||||
handleStateSetters.Append($@"
|
||||
if (state.{name} != null)
|
||||
component.{name} = new(state.{name});");
|
||||
}
|
||||
else
|
||||
{
|
||||
getStateInit.Append($@"
|
||||
{name} = component.{name},");
|
||||
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = state.{name};");
|
||||
}
|
||||
|
||||
break;
|
||||
case GlobalEntityUidSetName:
|
||||
stateFields.Append($@"
|
||||
@@ -195,6 +179,33 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = EnsureEntityList<{componentName}>(state.{name}, uid);");
|
||||
|
||||
break;
|
||||
default:
|
||||
stateFields.Append($@"
|
||||
public {typeDisplayStr} {name} = default!;");
|
||||
|
||||
if (IsCloneType(type))
|
||||
{
|
||||
// get first ctor arg of the field attribute, which determines whether the field should be cloned
|
||||
// (like if its a dict or list)
|
||||
getStateInit.Append($@"
|
||||
{name} = component.{name},");
|
||||
|
||||
handleStateSetters.Append($@"
|
||||
if (state.{name} == null)
|
||||
component.{name} = null;
|
||||
else
|
||||
component.{name} = new(state.{name});");
|
||||
}
|
||||
else
|
||||
{
|
||||
getStateInit.Append($@"
|
||||
{name} = component.{name},");
|
||||
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = state.{name};");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -353,5 +364,20 @@ public partial class {componentName}
|
||||
}
|
||||
context.RegisterForSyntaxNotifications(() => new NameReferenceSyntaxReceiver());
|
||||
}
|
||||
|
||||
private static bool IsCloneType(ITypeSymbol type)
|
||||
{
|
||||
if (type is not INamedTypeSymbol named || !named.IsGenericType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var constructed = named.ConstructedFrom.ToDisplayString(FullyQualifiedFormat);
|
||||
return constructed switch
|
||||
{
|
||||
GlobalDictionaryName or GlobalHashSetName or GlobalListName => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>9</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
@@ -188,10 +187,10 @@ namespace Robust.Shared.Scripting
|
||||
}
|
||||
|
||||
#region EntityManager proxy methods
|
||||
public T Comp<T>(EntityUid uid) where T : Component
|
||||
public T Comp<T>(EntityUid uid) where T : IComponent
|
||||
=> ent.GetComponent<T>(uid);
|
||||
|
||||
public bool TryComp<T>(EntityUid uid, out T? comp) where T : Component
|
||||
public bool TryComp<T>(EntityUid uid, out T? comp) where T : IComponent
|
||||
=> ent.TryGetComponent(uid, out comp);
|
||||
|
||||
public bool HasComp<T>(EntityUid uid)
|
||||
@@ -207,7 +206,7 @@ namespace Robust.Shared.Scripting
|
||||
=> ent.DirtyEntity(uid);
|
||||
|
||||
public void Dirty(Component comp)
|
||||
=> ent.Dirty(comp);
|
||||
=> ent.Dirty(comp.Owner, comp);
|
||||
|
||||
public string Name(EntityUid uid)
|
||||
=> ent.GetComponent<MetaDataComponent>(uid).EntityName;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Shared.Analyzers;
|
||||
/// component state replication beyond just directly setting variables should not use this attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[BaseTypeRequired(typeof(Component))]
|
||||
[BaseTypeRequired(typeof(IComponent))]
|
||||
public sealed class AutoGenerateComponentStateAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
@@ -32,20 +32,6 @@ public sealed class AutoGenerateComponentStateAttribute : Attribute
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class AutoNetworkedFieldAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the data should be wrapped in a new() when setting in get/handlestate
|
||||
/// e.g. for cloning collections like dictionaries or hashsets which is sometimes necessary.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should only be true if the type actually has a constructor that takes in itself.
|
||||
/// </remarks>
|
||||
[UsedImplicitly]
|
||||
public bool CloneData;
|
||||
|
||||
public AutoNetworkedFieldAttribute(bool cloneData=false)
|
||||
{
|
||||
CloneData = cloneData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -132,6 +132,11 @@ namespace Robust.Shared.Configuration
|
||||
return long.Parse(input);
|
||||
}
|
||||
|
||||
if (type == typeof(ushort))
|
||||
{
|
||||
return ushort.Parse(input);
|
||||
}
|
||||
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,6 +617,11 @@ namespace Robust.Shared.Configuration
|
||||
return long.Parse(value);
|
||||
}
|
||||
|
||||
if (type == typeof(ushort))
|
||||
{
|
||||
return ushort.Parse(value);
|
||||
}
|
||||
|
||||
// Must be a string.
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1,47 +1,73 @@
|
||||
using System.Linq;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Console.Commands;
|
||||
|
||||
internal sealed class HelpCommand : LocalizedCommands
|
||||
{
|
||||
public override string Command => "oldhelp";
|
||||
private static readonly string Gold = Color.Gold.ToHex();
|
||||
private static readonly string Aqua = Color.Aqua.ToHex();
|
||||
|
||||
public override string Command => "help";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
// Not a toolshed command since it doesn't support optional arguments
|
||||
ExecuteStatic(shell, argStr, args);
|
||||
}
|
||||
|
||||
public static void ExecuteStatic(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
shell.WriteLine(Loc.GetString("cmd-oldhelp-no-args"));
|
||||
shell.WriteLine($@"
|
||||
TOOLSHED
|
||||
/.\\\\\\\\
|
||||
/___\\\\\\\\
|
||||
|''''|'''''|
|
||||
| 8 | === |
|
||||
|_0__|_____|");
|
||||
shell.WriteMarkup($@"
|
||||
For a list of commands, run [color={Gold}]cmd:list[/color].
|
||||
To search for commands, run [color={Gold}]cmd:list search ""[color={Aqua}]query[/color]""[/color].
|
||||
For a breakdown of how a string of commands flows, run [color={Gold}]explain [color={Aqua}]commands here[/color][/color].
|
||||
For help with old console commands, run [color={Gold}]oldhelp[/color].
|
||||
");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
var commandName = args[0];
|
||||
if (!shell.ConsoleHost.AvailableCommands.TryGetValue(commandName, out var cmd))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-oldhelp-unknown", ("command", commandName)));
|
||||
shell.WriteError(Loc.GetString("cmd-help-unknown", ("command", commandName)));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-oldhelp-top", ("command", cmd.Command),
|
||||
shell.WriteLine(Loc.GetString("cmd-help-top", ("command", cmd.Command),
|
||||
("description", cmd.Description)));
|
||||
shell.WriteLine(cmd.Help);
|
||||
break;
|
||||
|
||||
default:
|
||||
shell.WriteError(Loc.GetString("cmd-oldhelp-invalid-args"));
|
||||
shell.WriteError(Loc.GetString("cmd-help-invalid-args"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
return GetCompletionStatic(shell, args);
|
||||
}
|
||||
|
||||
public static CompletionResult GetCompletionStatic(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
var host = shell.ConsoleHost;
|
||||
return CompletionResult.FromHintOptions(
|
||||
host.AvailableCommands.Values.OrderBy(c => c.Command).Select(c => new CompletionOption(c.Command, c.Description)).ToArray(),
|
||||
Loc.GetString("cmd-oldhelp-arg-cmdname"));
|
||||
Loc.GetString("cmd-help-arg-cmdname"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
|
||||
@@ -148,7 +148,7 @@ internal sealed class ListMapsCommand : LocalizedCommands
|
||||
mapId, _map.IsMapInitialized(mapId),
|
||||
_map.IsMapPaused(mapId),
|
||||
_map.GetMapEntityId(mapId),
|
||||
string.Join(",", _map.GetAllMapGrids(mapId).Select(grid => grid.Owner)));
|
||||
string.Join(",", _map.GetAllGrids(mapId).Select(grid => grid.Owner)));
|
||||
}
|
||||
|
||||
shell.WriteLine(msg.ToString());
|
||||
|
||||
17
Robust.Shared/Console/Commands/OldHelpCommand.cs
Normal file
17
Robust.Shared/Console/Commands/OldHelpCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Robust.Shared.Console.Commands;
|
||||
|
||||
public sealed class OldHelpCommand : LocalizedCommands
|
||||
{
|
||||
public override string Command => "oldhelp";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
// For the people that got used to oldhelp
|
||||
HelpCommand.ExecuteStatic(shell, argStr, args);
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
return HelpCommand.GetCompletionStatic(shell, args);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -173,7 +171,7 @@ public static class CompletionHelper
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CompletionOption> Components<T>(string text, IEntityManager? entManager = null) where T : Component
|
||||
public static IEnumerable<CompletionOption> Components<T>(string text, IEntityManager? entManager = null) where T : IComponent
|
||||
{
|
||||
IoCManager.Resolve(ref entManager);
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ namespace Robust.Shared.Console
|
||||
|
||||
private readonly CommandBuffer _commandBuffer = new CommandBuffer();
|
||||
|
||||
// TODO add Initialize() method.
|
||||
protected ISawmill Sawmill => LogManager.GetSawmill(SawmillName);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsServer { get; }
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
@@ -9,7 +8,7 @@ namespace Robust.Shared.Console
|
||||
/// Basic interface to handle console commands. Any class implementing this will be
|
||||
/// registered with the console system through reflection.
|
||||
/// </summary>
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors), Obsolete("New commands should utilize ToolshedCommand.")]
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IConsoleCommand
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -57,6 +57,11 @@ namespace Robust.Shared.Console
|
||||
|
||||
void WriteLine(FormattedMessage message);
|
||||
|
||||
void WriteMarkup(string markup)
|
||||
{
|
||||
WriteLine(FormattedMessage.FromMarkup(markup));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an error line to the console window.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -6,7 +5,6 @@ using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Shared.Console;
|
||||
|
||||
[Obsolete("You should use ToolshedCommand instead.")]
|
||||
public abstract class LocalizedCommands : IConsoleCommand
|
||||
{
|
||||
[Dependency] protected readonly ILocalizationManager LocalizationManager = default!;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
@@ -9,12 +14,6 @@ using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
@@ -62,7 +61,7 @@ namespace Robust.Shared.Containers
|
||||
/// The entity that owns this container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid Owner => Manager.Owner;
|
||||
public EntityUid Owner { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the contents of this container be shown? False for closed containers like lockers, true for
|
||||
@@ -77,10 +76,7 @@ namespace Robust.Shared.Containers
|
||||
DebugTools.AssertNull(ID);
|
||||
ID = id;
|
||||
Manager = component;
|
||||
|
||||
// TODO fix container init.
|
||||
// Eventually, we want an owner field, but currently it needs to use component.Owner
|
||||
// Owner = owner;
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -107,10 +103,9 @@ namespace Robust.Shared.Containers
|
||||
bool force = false)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(transform == null || transform.Owner == toinsert);
|
||||
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
|
||||
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
|
||||
DebugTools.Assert(physics == null || physics.Owner == toinsert);
|
||||
DebugTools.AssertOwner(toinsert, transform);
|
||||
DebugTools.AssertOwner(Owner, ownerTransform);
|
||||
DebugTools.AssertOwner(toinsert, physics);
|
||||
DebugTools.Assert(!ExpectedEntities.Contains(entMan.GetNetEntity(toinsert)));
|
||||
DebugTools.Assert(Manager.Containers.ContainsKey(ID));
|
||||
|
||||
@@ -214,7 +209,7 @@ namespace Robust.Shared.Containers
|
||||
DebugTools.Assert(transform.LocalRotation == Angle.Zero);
|
||||
DebugTools.Assert(!physicsQuery.TryGetComponent(toinsert, out var phys) || (!phys.Awake && !phys.CanCollide));
|
||||
|
||||
entMan.Dirty(Manager);
|
||||
entMan.Dirty(Owner, Manager);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -298,8 +293,8 @@ namespace Robust.Shared.Containers
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.AssertNotNull(Manager);
|
||||
DebugTools.Assert(entMan.EntityExists(toRemove));
|
||||
DebugTools.Assert(xform == null || xform.Owner == toRemove);
|
||||
DebugTools.Assert(meta == null || meta.Owner == toRemove);
|
||||
DebugTools.AssertOwner(toRemove, xform);
|
||||
DebugTools.AssertOwner(toRemove, meta);
|
||||
|
||||
xform ??= entMan.GetComponent<TransformComponent>(toRemove);
|
||||
meta ??= entMan.GetComponent<MetaDataComponent>(toRemove);
|
||||
@@ -337,7 +332,7 @@ namespace Robust.Shared.Containers
|
||||
else if (reparent)
|
||||
{
|
||||
// Container ECS when.
|
||||
sys.AttachParentToContainerOrGrid(xform);
|
||||
sys.AttachParentToContainerOrGrid((toRemove, xform));
|
||||
if (localRotation != null)
|
||||
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetLocalRotation(xform, localRotation.Value);
|
||||
}
|
||||
@@ -360,7 +355,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
DebugTools.Assert(destination == null || xform.Coordinates.Equals(destination.Value));
|
||||
|
||||
entMan.Dirty(Manager);
|
||||
entMan.Dirty(Owner, Manager);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper functions for the container system.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static class ContainerHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Am I inside a container? Only checks the direct parent. To see if the entity, or any parent entity, is
|
||||
/// inside a container, use <see cref="ContainerSystem.IsEntityOrParentInContainer"/>
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <returns>If the entity is inside of a container.</returns>
|
||||
[Obsolete("Use ContainerSystem.IsEntityInContainer() instead")]
|
||||
public static bool IsInContainer(this EntityUid entity,
|
||||
IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
return (entMan.GetComponent<MetaDataComponent>(entity).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the container manager that this entity is inside (if any).
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <param name="manager">The container manager that this entity is inside of.</param>
|
||||
/// <returns>If a container manager was found.</returns>
|
||||
public static bool TryGetContainerMan(this EntityUid entity, [NotNullWhen(true)] out ContainerManagerComponent? manager, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(entMan.EntityExists(entity));
|
||||
|
||||
var parent = entMan.GetComponent<TransformComponent>(entity).ParentUid;
|
||||
if (parent.IsValid() && TryGetManagerComp(parent, out manager, entMan) && manager.ContainsEntity(entity))
|
||||
return true;
|
||||
|
||||
manager = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the container that this entity is inside (if any).
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <param name="container">The container that this entity is inside of.</param>
|
||||
/// <returns>If a container was found.</returns>
|
||||
[Obsolete("Use ContainerSystem.TryGetContainingContainer() instead")]
|
||||
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out BaseContainer? container, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(entMan.EntityExists(entity));
|
||||
|
||||
if (TryGetContainerMan(entity, out var manager, entMan))
|
||||
return manager.TryGetContainer(entity, out container);
|
||||
|
||||
container = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove an entity from its container, if any.
|
||||
/// <see cref="SharedContainerSystem.TryRemoveFromContainer"/>
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <param name="force">Whether to forcibly remove the entity from the container.</param>
|
||||
/// <param name="wasInContainer">Whether the entity was actually inside a container or not.</param>
|
||||
/// <returns>If the entity could be removed. Also returns false if it wasn't inside a container.</returns>
|
||||
[Obsolete("Use SharedContainerSystem.TryRemoveFromContainer() instead")]
|
||||
public static bool TryRemoveFromContainer(this EntityUid entity, bool force, out bool wasInContainer, IEntityManager? entMan = null)
|
||||
{
|
||||
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
.TryRemoveFromContainer(entity, force, out wasInContainer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove an entity from its container, if any.
|
||||
/// <see cref="SharedContainerSystem.TryRemoveFromContainer"/>
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <param name="force">Whether to forcibly remove the entity from the container.</param>
|
||||
/// <returns>If the entity could be removed. Also returns false if it wasn't inside a container.</returns>
|
||||
[Obsolete("Use SharedContainerSystem.TryRemoveFromContainer() instead")]
|
||||
public static bool TryRemoveFromContainer(this EntityUid entity, bool force = false, IEntityManager? entMan = null)
|
||||
{
|
||||
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
.TryRemoveFromContainer(entity, force);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove all entities in a container.
|
||||
/// <see cref="SharedContainerSystem.EmptyContainer"/>
|
||||
/// </summary>
|
||||
[Obsolete("Use SharedContainerSystem.EmptyContainer() instead")]
|
||||
public static void EmptyContainer(this BaseContainer container, bool force = false, EntityCoordinates? moveTo = null,
|
||||
bool attachToGridOrMap = false, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
.EmptyContainer(container, force, moveTo, attachToGridOrMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove and delete all entities in a container.
|
||||
/// <see cref="SharedContainerSystem.CleanContainer"/>
|
||||
/// </summary>
|
||||
[Obsolete("Use SharedContainerSystem.CleanContainer() instead")]
|
||||
public static void CleanContainer(this BaseContainer container, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
.CleanContainer(container);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SharedContainerSystem.AttachParentToContainerOrGrid"/>
|
||||
/// </summary>
|
||||
[Obsolete("Use SharedContainerSystem.AttachParentToContainerOrGrid() instead")]
|
||||
public static void AttachParentToContainerOrGrid(this TransformComponent transform, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
.AttachParentToContainerOrGrid(transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SharedContainerSystem.TryGetManagerComp"/>
|
||||
/// </summary>
|
||||
[Obsolete("Use SharedContainerSystem.TryGetManagerComp() instead")]
|
||||
private static bool TryGetManagerComp(this EntityUid entity, [NotNullWhen(true)] out ContainerManagerComponent? manager, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
return entMan.System<SharedContainerSystem>()
|
||||
.TryGetManagerComp(entity, out manager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut method to make creation of containers easier.
|
||||
/// Creates a new container on the entity and gives it back to you.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to create the container for.</param>
|
||||
/// <param name="containerId"></param>
|
||||
/// <returns>The new container.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if there already is a container with the specified ID.</exception>
|
||||
/// <seealso cref="BaseContainerManager.MakeContainer{T}(string)" />
|
||||
[Obsolete("Use ContainerSystem.MakeContainer() instead")]
|
||||
public static T CreateContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
|
||||
where T : BaseContainer
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
var containermanager = entMan.EnsureComponent<ContainerManagerComponent>(entity);
|
||||
return containermanager.MakeContainer<T>(containerId);
|
||||
}
|
||||
|
||||
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
|
||||
public static T EnsureContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
|
||||
where T : BaseContainer
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
return EnsureContainer<T>(entity, containerId, out _, entMan);
|
||||
}
|
||||
|
||||
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
|
||||
public static T EnsureContainer<T>(this EntityUid entity, string containerId, out bool alreadyExisted, IEntityManager? entMan = null)
|
||||
where T : BaseContainer
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
var containerManager = entMan.EnsureComponent<ContainerManagerComponent>(entity);
|
||||
|
||||
if (!containerManager.TryGetContainer(containerId, out var existing))
|
||||
{
|
||||
alreadyExisted = false;
|
||||
return containerManager.MakeContainer<T>(containerId);
|
||||
}
|
||||
|
||||
if (!(existing is T container))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"The container exists but is of a different type: {existing.GetType()}");
|
||||
}
|
||||
|
||||
alreadyExisted = true;
|
||||
return container;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
@@ -22,7 +21,6 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
|
||||
[DataField("containers")]
|
||||
public Dictionary<string, BaseContainer> Containers = new();
|
||||
@@ -30,54 +28,37 @@ namespace Robust.Shared.Containers
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
// TODO custom type serializer
|
||||
// TODO set owner uid on init.
|
||||
foreach (var (id, container) in Containers)
|
||||
{
|
||||
container.Manager = this;
|
||||
container.Owner = Owner;
|
||||
container.ID = id;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
foreach (var container in Containers.Values)
|
||||
{
|
||||
container.Shutdown(_entMan, _netMan);
|
||||
}
|
||||
|
||||
Containers.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T MakeContainer<T>(string id)
|
||||
public T MakeContainer<T>(EntityUid uid, string id)
|
||||
where T : BaseContainer
|
||||
{
|
||||
if (HasContainer(id))
|
||||
throw new ArgumentException($"Container with specified ID already exists: '{id}'");
|
||||
|
||||
var container = _dynFactory.CreateInstanceUnchecked<T>(typeof(T), inject: false);
|
||||
container.Init(id, Owner, this);
|
||||
container.Init(id, uid, this);
|
||||
Containers[id] = container;
|
||||
_entMan.Dirty(this);
|
||||
_entMan.Dirty(uid, this);
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public BaseContainer GetContainer(string id)
|
||||
{
|
||||
return Containers[id];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasContainer(string id)
|
||||
{
|
||||
return Containers.ContainsKey(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetContainer(string id, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
var ret = Containers.TryGetValue(id, out var cont);
|
||||
@@ -85,7 +66,6 @@ namespace Robust.Shared.Containers
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
foreach (var contain in Containers.Values)
|
||||
@@ -101,7 +81,6 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsEntity(EntityUid entity)
|
||||
{
|
||||
foreach (var container in Containers.Values)
|
||||
@@ -112,7 +91,6 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Remove(EntityUid toremove,
|
||||
TransformComponent? xform = null,
|
||||
MetaDataComponent? meta = null,
|
||||
@@ -227,7 +205,9 @@ namespace Robust.Shared.Containers
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose() { }
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -16,6 +17,7 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
public abstract partial class SharedContainerSystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
@@ -34,6 +36,7 @@ namespace Robust.Shared.Containers
|
||||
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChanged);
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentStartup>(OnStartupValidation);
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentGetState>(OnContainerGetState);
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentRemove>(OnContainerManagerRemove);
|
||||
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_mapQuery = GetEntityQuery<MapComponent>();
|
||||
@@ -62,6 +65,16 @@ namespace Robust.Shared.Containers
|
||||
args.State = new ContainerManagerComponent.ContainerManagerComponentState(containerSet);
|
||||
}
|
||||
|
||||
private void OnContainerManagerRemove(EntityUid uid, ContainerManagerComponent component, ComponentRemove args)
|
||||
{
|
||||
foreach (var container in component.Containers.Values)
|
||||
{
|
||||
container.Shutdown(EntityManager, _net);
|
||||
}
|
||||
|
||||
component.Containers.Clear();
|
||||
}
|
||||
|
||||
// TODO: Make ContainerManagerComponent ECS and make these proxy methods the real deal.
|
||||
|
||||
#region Proxy Methods
|
||||
@@ -70,16 +83,16 @@ namespace Robust.Shared.Containers
|
||||
where T : BaseContainer
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false))
|
||||
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid); // Happy Vera.
|
||||
containerManager = AddComp<ContainerManagerComponent>(uid); // Happy Vera.
|
||||
|
||||
return containerManager.MakeContainer<T>(id);
|
||||
return containerManager.MakeContainer<T>(uid, id);
|
||||
}
|
||||
|
||||
public T EnsureContainer<T>(EntityUid uid, string id, out bool alreadyExisted, ContainerManagerComponent? containerManager = null)
|
||||
where T : BaseContainer
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false))
|
||||
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid);
|
||||
containerManager = AddComp<ContainerManagerComponent>(uid);
|
||||
|
||||
if (TryGetContainer(uid, id, out var container, containerManager))
|
||||
{
|
||||
@@ -128,7 +141,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
|
||||
{
|
||||
if (Resolve(uid, ref containerManager, false) && (skipExistCheck || EntityManager.EntityExists(containedUid)))
|
||||
if (Resolve(uid, ref containerManager, false) && (skipExistCheck || Exists(containedUid)))
|
||||
return containerManager.TryGetContainer(containedUid, out container);
|
||||
|
||||
container = null;
|
||||
@@ -137,7 +150,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
public bool ContainsEntity(EntityUid uid, EntityUid containedUid, ContainerManagerComponent? containerManager = null)
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false) || !EntityManager.EntityExists(containedUid))
|
||||
if (!Resolve(uid, ref containerManager, false) || !Exists(containedUid))
|
||||
return false;
|
||||
|
||||
return containerManager.ContainsEntity(containedUid);
|
||||
@@ -215,7 +228,7 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
if (meta == null)
|
||||
{
|
||||
metas ??= EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
metas ??= GetEntityQuery<MetaDataComponent>();
|
||||
meta = metas.Value.GetComponent(uid);
|
||||
}
|
||||
|
||||
@@ -224,7 +237,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
if (xform == null)
|
||||
{
|
||||
xforms ??= EntityManager.GetEntityQuery<TransformComponent>();
|
||||
xforms ??= GetEntityQuery<TransformComponent>();
|
||||
xform = xforms.Value.GetComponent(uid);
|
||||
}
|
||||
|
||||
@@ -242,7 +255,7 @@ namespace Robust.Shared.Containers
|
||||
EntityQuery<T> entityQuery,
|
||||
[NotNullWhen(true)] ref T? foundComponent,
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null) where T : Component
|
||||
TransformComponent? xform = null) where T : IComponent
|
||||
{
|
||||
if (!MetaQuery.Resolve(uid, ref meta))
|
||||
return false;
|
||||
@@ -270,7 +283,7 @@ namespace Robust.Shared.Containers
|
||||
EntityQuery<T> entityQuery,
|
||||
List<T> foundComponents,
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null) where T : Component
|
||||
TransformComponent? xform = null) where T : IComponent
|
||||
{
|
||||
if (!MetaQuery.Resolve(uid, ref meta))
|
||||
return foundComponents.Any();
|
||||
@@ -284,7 +297,7 @@ namespace Robust.Shared.Containers
|
||||
if (!xform.ParentUid.Valid)
|
||||
return foundComponents.Any();
|
||||
|
||||
if (EntityManager.TryGetComponent(xform.ParentUid, out T? foundComponent))
|
||||
if (TryComp(xform.ParentUid, out T? foundComponent))
|
||||
foundComponents.Add(foundComponent);
|
||||
|
||||
return TryFindComponentsOnEntityContainerOrParent(xform.ParentUid, entityQuery, foundComponents);
|
||||
@@ -381,7 +394,7 @@ namespace Robust.Shared.Containers
|
||||
/// </summary>
|
||||
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
return TryGetOuterContainer(uid, xform, out container, xformQuery);
|
||||
}
|
||||
|
||||
@@ -393,8 +406,8 @@ namespace Robust.Shared.Containers
|
||||
if (!uid.IsValid())
|
||||
return false;
|
||||
|
||||
var conQuery = EntityManager.GetEntityQuery<ContainerManagerComponent>();
|
||||
var metaQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
var conQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
var child = uid;
|
||||
var parent = xform.ParentUid;
|
||||
|
||||
@@ -488,20 +501,20 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachParentToContainerOrGrid(TransformComponent transform)
|
||||
public void AttachParentToContainerOrGrid(Entity<TransformComponent> transform)
|
||||
{
|
||||
// TODO make this check upwards for any container, and parent to that.
|
||||
// Currently this just checks the direct parent, so entities will still teleport through containers.
|
||||
|
||||
if (!transform.ParentUid.IsValid()
|
||||
|| !TryGetContainingContainer(transform.ParentUid, out var container)
|
||||
if (!transform.Comp.ParentUid.IsValid()
|
||||
|| !TryGetContainingContainer(transform.Comp.ParentUid, out var container)
|
||||
|| !TryInsertIntoContainer(transform, container))
|
||||
transform.AttachToGridOrMap();
|
||||
transform.Comp.AttachToGridOrMap();
|
||||
}
|
||||
|
||||
private bool TryInsertIntoContainer(TransformComponent transform, BaseContainer container)
|
||||
private bool TryInsertIntoContainer(Entity<TransformComponent> transform, BaseContainer container)
|
||||
{
|
||||
if (container.Insert(transform.Owner)) return true;
|
||||
if (container.Insert(transform)) return true;
|
||||
|
||||
if (Transform(container.Owner).ParentUid.IsValid()
|
||||
&& TryGetContainingContainer(container.Owner, out var newContainer))
|
||||
|
||||
92
Robust.Shared/ContentPack/ResourceManifestData.cs
Normal file
92
Robust.Shared/ContentPack/ResourceManifestData.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Shared.ContentPack;
|
||||
|
||||
internal sealed record ResourceManifestData(
|
||||
string[] Modules,
|
||||
string? AssemblyPrefix,
|
||||
string? DefaultWindowTitle,
|
||||
string? WindowIconSet,
|
||||
string? SplashLogo,
|
||||
bool AutoConnect,
|
||||
string[]? ClientAssemblies
|
||||
)
|
||||
{
|
||||
public static readonly ResourceManifestData Default =
|
||||
new ResourceManifestData(Array.Empty<string>(), null, null, null, null, true, null);
|
||||
|
||||
public static ResourceManifestData LoadResourceManifest(IResourceManager res)
|
||||
{
|
||||
// Parses /manifest.yml for game-specific settings that cannot be exclusively set up by content code.
|
||||
if (!res.TryContentFileRead("/manifest.yml", out var stream))
|
||||
return ResourceManifestData.Default;
|
||||
|
||||
var yamlStream = new YamlStream();
|
||||
using (stream)
|
||||
{
|
||||
using var streamReader = new StreamReader(stream, EncodingHelpers.UTF8);
|
||||
yamlStream.Load(streamReader);
|
||||
}
|
||||
|
||||
if (yamlStream.Documents.Count == 0)
|
||||
return ResourceManifestData.Default;
|
||||
|
||||
if (yamlStream.Documents.Count != 1 || yamlStream.Documents[0].RootNode is not YamlMappingNode mapping)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Expected a single YAML document with root mapping for /manifest.yml");
|
||||
}
|
||||
|
||||
var modules = ReadStringArray(mapping, "modules") ?? Array.Empty<string>();
|
||||
|
||||
string? assemblyPrefix = null;
|
||||
if (mapping.TryGetNode("assemblyPrefix", out var prefixNode))
|
||||
assemblyPrefix = prefixNode.AsString();
|
||||
|
||||
string? defaultWindowTitle = null;
|
||||
if (mapping.TryGetNode("defaultWindowTitle", out var winTitleNode))
|
||||
defaultWindowTitle = winTitleNode.AsString();
|
||||
|
||||
string? windowIconSet = null;
|
||||
if (mapping.TryGetNode("windowIconSet", out var iconSetNode))
|
||||
windowIconSet = iconSetNode.AsString();
|
||||
|
||||
string? splashLogo = null;
|
||||
if (mapping.TryGetNode("splashLogo", out var splashNode))
|
||||
splashLogo = splashNode.AsString();
|
||||
|
||||
bool autoConnect = true;
|
||||
if (mapping.TryGetNode("autoConnect", out var autoConnectNode))
|
||||
autoConnect = autoConnectNode.AsBool();
|
||||
|
||||
var clientAssemblies = ReadStringArray(mapping, "clientAssemblies");
|
||||
|
||||
return new ResourceManifestData(
|
||||
modules,
|
||||
assemblyPrefix,
|
||||
defaultWindowTitle,
|
||||
windowIconSet,
|
||||
splashLogo,
|
||||
autoConnect,
|
||||
clientAssemblies
|
||||
);
|
||||
|
||||
static string[]? ReadStringArray(YamlMappingNode mapping, string key)
|
||||
{
|
||||
if (!mapping.TryGetNode(key, out var node))
|
||||
return null;
|
||||
|
||||
var sequence = (YamlSequenceNode)node;
|
||||
var array = new string[sequence.Children.Count];
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = sequence[i].AsString();
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
using System;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc cref="IComponent"/>
|
||||
[Reflect(false)]
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class Component : IComponent
|
||||
@@ -20,7 +16,8 @@ namespace Robust.Shared.GameObjects
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private bool _netSync { get; set; } = true;
|
||||
|
||||
internal bool Networked = true;
|
||||
[Obsolete("Do not use from content")]
|
||||
public bool Networked { get; set; } = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool NetSyncEnabled
|
||||
@@ -36,108 +33,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public ComponentLifeStage LifeStage { get; private set; } = ComponentLifeStage.PreAdd;
|
||||
public ComponentLifeStage LifeStage { get; [Obsolete("Do not use from content")] set; } = ComponentLifeStage.PreAdd;
|
||||
|
||||
/// <summary>
|
||||
/// If true, and if this is a networked component, then component data will only be sent to players if their
|
||||
/// controlled entity is the owner of this component. This is less performance intensive than <see cref="SessionSpecific"/>.
|
||||
/// </summary>
|
||||
public virtual bool SendOnlyToOwner => false;
|
||||
|
||||
/// <summary>
|
||||
/// If true, and if this is a networked component, then this component will cause <see
|
||||
/// cref="ComponentGetStateAttemptEvent"/> events to be raised to check whether a given player should
|
||||
/// receive this component's state.
|
||||
/// </summary>
|
||||
public virtual bool SessionSpecific => false;
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.PreAdd" /> to <see cref="ComponentLifeStage.Added" />,
|
||||
/// after raising a <see cref="ComponentAdd"/> event.
|
||||
/// </summary>
|
||||
internal void LifeAddToEntity(IEntityManager entManager, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.PreAdd);
|
||||
|
||||
LifeStage = ComponentLifeStage.Adding;
|
||||
CreationTick = entManager.CurrentTick;
|
||||
// networked components are assumed to be dirty when added to entities. See also: ClearTicks()
|
||||
LastModifiedTick = entManager.CurrentTick;
|
||||
entManager.EventBus.RaiseComponentEvent(this, type, CompAddInstance);
|
||||
LifeStage = ComponentLifeStage.Added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Added" /> to <see cref="ComponentLifeStage.Initialized" />,
|
||||
/// calling <see cref="Initialize" />.
|
||||
/// </summary>
|
||||
internal void LifeInitialize(IEntityManager entManager, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.Added);
|
||||
|
||||
LifeStage = ComponentLifeStage.Initializing;
|
||||
entManager.EventBus.RaiseComponentEvent(this, type, CompInitInstance);
|
||||
LifeStage = ComponentLifeStage.Initialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Initialized" /> to
|
||||
/// <see cref="ComponentLifeStage.Running" />, calling <see cref="Startup" />.
|
||||
/// </summary>
|
||||
internal void LifeStartup(IEntityManager entManager)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.Initialized);
|
||||
|
||||
LifeStage = ComponentLifeStage.Starting;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompStartupInstance);
|
||||
LifeStage = ComponentLifeStage.Running;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Running" /> to <see cref="ComponentLifeStage.Stopped" />,
|
||||
/// calling <see cref="Shutdown" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components are allowed to remove themselves in their own Startup function.
|
||||
/// </remarks>
|
||||
internal void LifeShutdown(IEntityManager entManager)
|
||||
{
|
||||
DebugTools.Assert(LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
|
||||
|
||||
if (LifeStage <= ComponentLifeStage.Initialized)
|
||||
{
|
||||
// Component was never started, no shutdown logic necessary. Simply mark it as stopped.
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
LifeStage = ComponentLifeStage.Stopping;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompShutdownInstance);
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Stopped" /> to <see cref="ComponentLifeStage.Deleted" />,
|
||||
/// calling <see cref="OnRemove" />.
|
||||
/// </summary>
|
||||
internal void LifeRemoveFromEntity(IEntityManager entManager)
|
||||
{
|
||||
// can be called at any time after PreAdd, including inside other life stage events.
|
||||
DebugTools.Assert(LifeStage != ComponentLifeStage.PreAdd);
|
||||
|
||||
LifeStage = ComponentLifeStage.Removing;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompRemoveInstance);
|
||||
|
||||
OnRemove();
|
||||
|
||||
#if DEBUG
|
||||
if (LifeStage != ComponentLifeStage.Deleted)
|
||||
{
|
||||
DebugTools.Assert($"Component {this.GetType().Name} did not call base {nameof(OnRemove)} in derived method.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public bool Initialized => LifeStage >= ComponentLifeStage.Initializing;
|
||||
@@ -152,45 +53,31 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public GameTick CreationTick { get; private set; }
|
||||
public GameTick CreationTick { get; [Obsolete("Do not use from content")] set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public GameTick LastModifiedTick { get; internal set; }
|
||||
|
||||
private static readonly ComponentAdd CompAddInstance = new();
|
||||
private static readonly ComponentInit CompInitInstance = new();
|
||||
private static readonly ComponentStartup CompStartupInstance = new();
|
||||
private static readonly ComponentShutdown CompShutdownInstance = new();
|
||||
private static readonly ComponentRemove CompRemoveInstance = new();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the component is removed from an entity.
|
||||
/// Shuts down the component.
|
||||
/// The component has already been marked as deleted in the component manager.
|
||||
/// </summary>
|
||||
protected virtual void OnRemove()
|
||||
{
|
||||
LifeStage = ComponentLifeStage.Deleted;
|
||||
}
|
||||
public GameTick LastModifiedTick { get; [Obsolete("Do not use from content")] set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete]
|
||||
public void Dirty(IEntityManager? entManager = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entManager);
|
||||
entManager.Dirty(this);
|
||||
entManager.Dirty(Owner, this);
|
||||
}
|
||||
|
||||
// these two methods clear the LastModifiedTick/CreationTick to mark it as "not different from prototype load".
|
||||
// This is used as optimization in the game state system to avoid sending redundant component data.
|
||||
internal virtual void ClearTicks()
|
||||
[Obsolete("Do not use from content")]
|
||||
public virtual void ClearTicks()
|
||||
{
|
||||
LastModifiedTick = GameTick.Zero;
|
||||
ClearCreationTick();
|
||||
}
|
||||
|
||||
internal void ClearCreationTick()
|
||||
[Obsolete("Do not use from content")]
|
||||
public void ClearCreationTick()
|
||||
{
|
||||
CreationTick = GameTick.Zero;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Component that this event relates to.
|
||||
/// </summary>
|
||||
public Component Component { get; }
|
||||
public IComponent Component { get; }
|
||||
|
||||
/// <summary>
|
||||
/// EntityUid of the entity this component belongs to.
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
/// <param name="component">The relevant component</param>
|
||||
/// <param name="owner">EntityUid of the entity this component belongs to.</param>
|
||||
public ComponentEventArgs(Component component, EntityUid owner)
|
||||
public ComponentEventArgs(IComponent component, EntityUid owner)
|
||||
{
|
||||
Component = component;
|
||||
Owner = owner;
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[PublicAPI]
|
||||
public static class ComponentExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Convenience wrapper to implement "create component if it does not already exist".
|
||||
/// Always gives you back a component, and creates it if it does not exist yet.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to fetch or create the component on.</param>
|
||||
/// <typeparam name="T">The type of the component to fetch or create.</typeparam>
|
||||
/// <returns>The existing component, or the new component if none existed yet.</returns>
|
||||
[Obsolete]
|
||||
public static T EnsureComponent<T>(this EntityUid entity) where T : Component, new()
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
if (entMan.TryGetComponent(entity, out T? component))
|
||||
{
|
||||
return component;
|
||||
}
|
||||
|
||||
return entMan.AddComponent<T>(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience wrapper to implement "create component if it does not already exist".
|
||||
/// Always gives you back a component, and creates it if it does not exist yet.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to fetch or create the component on.</param>
|
||||
/// <typeparam name="T">The type of the component to fetch or create.</typeparam>
|
||||
/// <param name="warning">
|
||||
/// The custom warning message to log if the component did not exist already.
|
||||
/// Defaults to a predetermined warning if null.
|
||||
/// </param>
|
||||
/// <returns>The existing component, or the new component if none existed yet.</returns>
|
||||
[Obsolete]
|
||||
public static T EnsureComponentWarn<T>(this EntityUid entity, string? warning = null) where T : Component, new()
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
if (entMan.TryGetComponent(entity, out T? component))
|
||||
{
|
||||
return component;
|
||||
}
|
||||
|
||||
warning ??= $"Entity {entity} at {entMan.GetComponent<TransformComponent>(entity).MapPosition} did not have a {typeof(T)}";
|
||||
|
||||
Logger.Warning(warning);
|
||||
|
||||
return entMan.AddComponent<T>(entity);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static IComponent SetAndDirtyIfChanged<TValue>(
|
||||
this IComponent comp,
|
||||
ref TValue backingField,
|
||||
TValue value)
|
||||
{
|
||||
if (EqualityComparer<TValue>.Default.Equals(backingField, value))
|
||||
{
|
||||
return comp;
|
||||
}
|
||||
|
||||
backingField = value;
|
||||
comp.Dirty();
|
||||
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
@@ -33,14 +32,6 @@ public sealed partial class AppearanceComponent : Component
|
||||
|
||||
[ViewVariables] internal Dictionary<Enum, object> AppearanceData = new();
|
||||
|
||||
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
|
||||
|
||||
[Obsolete("Use SharedAppearanceSystem instead")]
|
||||
public void SetData(Enum key, object value)
|
||||
{
|
||||
_sysMan.GetEntitySystem<SharedAppearanceSystem>().SetData(Owner, key, value, this);
|
||||
}
|
||||
|
||||
[Obsolete("Use SharedAppearanceSystem instead")]
|
||||
public bool TryGetData<T>(Enum key, [NotNullWhen(true)] out T data)
|
||||
{
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
using JetBrains.Annotations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
@@ -68,7 +66,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// The components attached to the entity that are currently networked.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
internal readonly Dictionary<ushort, Component> NetComponents = new();
|
||||
internal readonly Dictionary<ushort, IComponent> NetComponents = new();
|
||||
|
||||
/// <summary>
|
||||
/// Network identifier for this entity.
|
||||
@@ -112,14 +110,6 @@ namespace Robust.Shared.GameObjects
|
||||
return _entityPrototype != null ? _entityPrototype.Name : string.Empty;
|
||||
return _entityName;
|
||||
}
|
||||
[Obsolete("Use MetaDataSystem.SetEntityName")]
|
||||
set
|
||||
{
|
||||
if (value == EntityName)
|
||||
return;
|
||||
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<MetaDataSystem>().SetEntityName(Owner, value, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -134,14 +124,6 @@ namespace Robust.Shared.GameObjects
|
||||
return _entityPrototype != null ? _entityPrototype.Description : string.Empty;
|
||||
return _entityDescription;
|
||||
}
|
||||
[Obsolete("Use MetaDataSystem.SetEntityDescription")]
|
||||
set
|
||||
{
|
||||
if (value == EntityDescription)
|
||||
return;
|
||||
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<MetaDataSystem>().SetEntityDescription(Owner, value, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -199,7 +181,8 @@ namespace Robust.Shared.GameObjects
|
||||
public bool EntityInitializing => EntityLifeStage == EntityLifeStage.Initializing;
|
||||
public bool EntityDeleted => EntityLifeStage >= EntityLifeStage.Deleted;
|
||||
|
||||
internal override void ClearTicks()
|
||||
[Obsolete("Do not use from content")]
|
||||
public override void ClearTicks()
|
||||
{
|
||||
// Do not clear modified ticks.
|
||||
// MetaDataComponent is used in the game state system to carry initial data like prototype ID.
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
@@ -85,8 +83,9 @@ namespace Robust.Shared.GameObjects
|
||||
[ViewVariables]
|
||||
public Angle PrevRotation { get; internal set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
internal bool ActivelyLerping { get; set; }
|
||||
[ViewVariables] public bool ActivelyLerping;
|
||||
|
||||
[ViewVariables] public GameTick LastLerp = GameTick.Zero;
|
||||
|
||||
[ViewVariables] internal readonly HashSet<EntityUid> _children = new();
|
||||
|
||||
@@ -129,7 +128,7 @@ namespace Robust.Shared.GameObjects
|
||||
LocalRotation = Angle.Zero;
|
||||
|
||||
_noLocalRotation = value;
|
||||
_entMan.Dirty(this);
|
||||
_entMan.Dirty(Owner, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +150,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
var oldRotation = _localRotation;
|
||||
_localRotation = value;
|
||||
_entMan.Dirty(this);
|
||||
_entMan.Dirty(Owner, this);
|
||||
MatricesDirty = true;
|
||||
|
||||
if (!Initialized)
|
||||
@@ -334,7 +333,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
var oldGridPos = Coordinates;
|
||||
_localPosition = value;
|
||||
_entMan.Dirty(this);
|
||||
_entMan.Dirty(Owner, this);
|
||||
MatricesDirty = true;
|
||||
|
||||
if (!Initialized)
|
||||
@@ -397,7 +396,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
[ViewVariables] public int ChildCount => _children.Count;
|
||||
|
||||
[ViewVariables] internal EntityUid LerpParent { get; set; }
|
||||
[ViewVariables] public EntityUid LerpParent;
|
||||
public bool PredictedLerp;
|
||||
|
||||
/// <summary>
|
||||
/// Detaches this entity from its parent.
|
||||
|
||||
584
Robust.Shared/GameObjects/Entity.cs
Normal file
584
Robust.Shared/GameObjects/Entity.cs
Normal file
@@ -0,0 +1,584 @@
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public record struct Entity<T>
|
||||
where T : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T Comp;
|
||||
|
||||
public Entity(EntityUid owner, T comp)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp);
|
||||
|
||||
Owner = owner;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T>((EntityUid Owner, T Comp) tuple)
|
||||
{
|
||||
return new Entity<T>(tuple.Owner, tuple.Comp);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T?>(owner, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T(Entity<T> ent)
|
||||
{
|
||||
return ent.Comp;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T comp)
|
||||
{
|
||||
owner = Owner;
|
||||
comp = Comp;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Entity<T1, T2>
|
||||
where T1 : IComponent? where T2 : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T1 Comp1;
|
||||
public T2 Comp2;
|
||||
|
||||
public Entity(EntityUid owner, T1 comp1, T2 comp2)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp1);
|
||||
DebugTools.AssertOwner(owner, comp2);
|
||||
|
||||
Owner = owner;
|
||||
Comp1 = comp1;
|
||||
Comp2 = comp2;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1, T2>((EntityUid Owner, T1 Comp1, T2 Comp2) tuple)
|
||||
{
|
||||
return new Entity<T1, T2>(tuple.Owner, tuple.Comp1, tuple.Comp2);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1?, T2?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T1?, T2?>(owner, default, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T1, T2> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T1(Entity<T1, T2> ent)
|
||||
{
|
||||
return ent.Comp1;
|
||||
}
|
||||
|
||||
public static implicit operator T2(Entity<T1, T2> ent)
|
||||
{
|
||||
return ent.Comp2;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T1 comp1, out T2 comp2)
|
||||
{
|
||||
owner = Owner;
|
||||
comp1 = Comp1;
|
||||
comp2 = Comp2;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Entity<T1, T2, T3>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T1 Comp1;
|
||||
public T2 Comp2;
|
||||
public T3 Comp3;
|
||||
|
||||
public Entity(EntityUid owner, T1 comp1, T2 comp2, T3 comp3)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp1);
|
||||
DebugTools.AssertOwner(owner, comp2);
|
||||
DebugTools.AssertOwner(owner, comp3);
|
||||
|
||||
Owner = owner;
|
||||
Comp1 = comp1;
|
||||
Comp2 = comp2;
|
||||
Comp3 = comp3;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1, T2, T3>((EntityUid Owner, T1 Comp1, T2 Comp2, T3 Comp3) tuple)
|
||||
{
|
||||
return new Entity<T1, T2, T3>(tuple.Owner, tuple.Comp1, tuple.Comp2, tuple.Comp3);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1?, T2?, T3?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T1?, T2?, T3?>(owner, default, default, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T1, T2, T3> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T1(Entity<T1, T2, T3> ent)
|
||||
{
|
||||
return ent.Comp1;
|
||||
}
|
||||
|
||||
public static implicit operator T2(Entity<T1, T2, T3> ent)
|
||||
{
|
||||
return ent.Comp2;
|
||||
}
|
||||
|
||||
public static implicit operator T3(Entity<T1, T2, T3> ent)
|
||||
{
|
||||
return ent.Comp3;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T1 comp1, out T2 comp2, out T3 comp3)
|
||||
{
|
||||
owner = Owner;
|
||||
comp1 = Comp1;
|
||||
comp2 = Comp2;
|
||||
comp3 = Comp3;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Entity<T1, T2, T3, T4>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T1 Comp1;
|
||||
public T2 Comp2;
|
||||
public T3 Comp3;
|
||||
public T4 Comp4;
|
||||
|
||||
public Entity(EntityUid owner, T1 comp1, T2 comp2, T3 comp3, T4 comp4)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp1);
|
||||
DebugTools.AssertOwner(owner, comp2);
|
||||
DebugTools.AssertOwner(owner, comp3);
|
||||
DebugTools.AssertOwner(owner, comp4);
|
||||
|
||||
Owner = owner;
|
||||
Comp1 = comp1;
|
||||
Comp2 = comp2;
|
||||
Comp3 = comp3;
|
||||
Comp4 = comp4;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1, T2, T3, T4>((EntityUid Owner, T1 Comp1, T2 Comp2, T3 Comp3, T4 Comp4) tuple)
|
||||
{
|
||||
return new Entity<T1, T2, T3, T4>(tuple.Owner, tuple.Comp1, tuple.Comp2, tuple.Comp3, tuple.Comp4);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1?, T2?, T3?, T4?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T1?, T2?, T3?, T4?>(owner, default, default, default, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T1, T2, T3, T4> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T1(Entity<T1, T2, T3, T4> ent)
|
||||
{
|
||||
return ent.Comp1;
|
||||
}
|
||||
|
||||
public static implicit operator T2(Entity<T1, T2, T3, T4> ent)
|
||||
{
|
||||
return ent.Comp2;
|
||||
}
|
||||
|
||||
public static implicit operator T3(Entity<T1, T2, T3, T4> ent)
|
||||
{
|
||||
return ent.Comp3;
|
||||
}
|
||||
|
||||
public static implicit operator T4(Entity<T1, T2, T3, T4> ent)
|
||||
{
|
||||
return ent.Comp4;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T1 comp1, out T2 comp2, out T3 comp3, out T4 comp4)
|
||||
{
|
||||
owner = Owner;
|
||||
comp1 = Comp1;
|
||||
comp2 = Comp2;
|
||||
comp3 = Comp3;
|
||||
comp4 = Comp4;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Entity<T1, T2, T3, T4, T5>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T1 Comp1;
|
||||
public T2 Comp2;
|
||||
public T3 Comp3;
|
||||
public T4 Comp4;
|
||||
public T5 Comp5;
|
||||
|
||||
public Entity(EntityUid owner, T1 comp1, T2 comp2, T3 comp3, T4 comp4, T5 comp5)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp1);
|
||||
DebugTools.AssertOwner(owner, comp2);
|
||||
DebugTools.AssertOwner(owner, comp3);
|
||||
DebugTools.AssertOwner(owner, comp4);
|
||||
DebugTools.AssertOwner(owner, comp5);
|
||||
|
||||
Owner = owner;
|
||||
Comp1 = comp1;
|
||||
Comp2 = comp2;
|
||||
Comp3 = comp3;
|
||||
Comp4 = comp4;
|
||||
Comp5 = comp5;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1, T2, T3, T4, T5>((EntityUid Owner, T1 Comp1, T2 Comp2, T3 Comp3, T4 Comp4, T5 Comp5) tuple)
|
||||
{
|
||||
return new Entity<T1, T2, T3, T4, T5>(tuple.Owner, tuple.Comp1, tuple.Comp2, tuple.Comp3, tuple.Comp4, tuple.Comp5);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1?, T2?, T3?, T4?, T5?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T1?, T2?, T3?, T4?, T5?>(owner, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T1, T2, T3, T4, T5> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T1(Entity<T1, T2, T3, T4, T5> ent)
|
||||
{
|
||||
return ent.Comp1;
|
||||
}
|
||||
|
||||
public static implicit operator T2(Entity<T1, T2, T3, T4, T5> ent)
|
||||
{
|
||||
return ent.Comp2;
|
||||
}
|
||||
|
||||
public static implicit operator T3(Entity<T1, T2, T3, T4, T5> ent)
|
||||
{
|
||||
return ent.Comp3;
|
||||
}
|
||||
|
||||
public static implicit operator T4(Entity<T1, T2, T3, T4, T5> ent)
|
||||
{
|
||||
return ent.Comp4;
|
||||
}
|
||||
|
||||
public static implicit operator T5(Entity<T1, T2, T3, T4, T5> ent)
|
||||
{
|
||||
return ent.Comp5;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T1 comp1, out T2 comp2, out T3 comp3, out T4 comp4, out T5 comp5)
|
||||
{
|
||||
owner = Owner;
|
||||
comp1 = Comp1;
|
||||
comp2 = Comp2;
|
||||
comp3 = Comp3;
|
||||
comp4 = Comp4;
|
||||
comp5 = Comp5;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Entity<T1, T2, T3, T4, T5, T6>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent? where T6 : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T1 Comp1;
|
||||
public T2 Comp2;
|
||||
public T3 Comp3;
|
||||
public T4 Comp4;
|
||||
public T5 Comp5;
|
||||
public T6 Comp6;
|
||||
|
||||
public Entity(EntityUid owner, T1 comp1, T2 comp2, T3 comp3, T4 comp4, T5 comp5, T6 comp6)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp1);
|
||||
DebugTools.AssertOwner(owner, comp2);
|
||||
DebugTools.AssertOwner(owner, comp3);
|
||||
DebugTools.AssertOwner(owner, comp4);
|
||||
DebugTools.AssertOwner(owner, comp5);
|
||||
DebugTools.AssertOwner(owner, comp6);
|
||||
|
||||
Owner = owner;
|
||||
Comp1 = comp1;
|
||||
Comp2 = comp2;
|
||||
Comp3 = comp3;
|
||||
Comp4 = comp4;
|
||||
Comp5 = comp5;
|
||||
Comp6 = comp6;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1, T2, T3, T4, T5, T6>((EntityUid Owner, T1 Comp1, T2 Comp2, T3 Comp3, T4 Comp4, T5 Comp5, T6 Comp6) tuple)
|
||||
{
|
||||
return new Entity<T1, T2, T3, T4, T5, T6>(tuple.Owner, tuple.Comp1, tuple.Comp2, tuple.Comp3, tuple.Comp4, tuple.Comp5, tuple.Comp6);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1?, T2?, T3?, T4?, T5?, T6?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T1?, T2?, T3?, T4?, T5?, T6?>(owner, default, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T1, T2, T3, T4, T5, T6> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T1(Entity<T1, T2, T3, T4, T5, T6> ent)
|
||||
{
|
||||
return ent.Comp1;
|
||||
}
|
||||
|
||||
public static implicit operator T2(Entity<T1, T2, T3, T4, T5, T6> ent)
|
||||
{
|
||||
return ent.Comp2;
|
||||
}
|
||||
|
||||
public static implicit operator T3(Entity<T1, T2, T3, T4, T5, T6> ent)
|
||||
{
|
||||
return ent.Comp3;
|
||||
}
|
||||
|
||||
public static implicit operator T4(Entity<T1, T2, T3, T4, T5, T6> ent)
|
||||
{
|
||||
return ent.Comp4;
|
||||
}
|
||||
|
||||
public static implicit operator T5(Entity<T1, T2, T3, T4, T5, T6> ent)
|
||||
{
|
||||
return ent.Comp5;
|
||||
}
|
||||
|
||||
public static implicit operator T6(Entity<T1, T2, T3, T4, T5, T6> ent)
|
||||
{
|
||||
return ent.Comp6;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T1 comp1, out T2 comp2, out T3 comp3, out T4 comp4, out T5 comp5, out T6 comp6)
|
||||
{
|
||||
owner = Owner;
|
||||
comp1 = Comp1;
|
||||
comp2 = Comp2;
|
||||
comp3 = Comp3;
|
||||
comp4 = Comp4;
|
||||
comp5 = Comp5;
|
||||
comp6 = Comp6;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Entity<T1, T2, T3, T4, T5, T6, T7>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent? where T6 : IComponent? where T7 : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T1 Comp1;
|
||||
public T2 Comp2;
|
||||
public T3 Comp3;
|
||||
public T4 Comp4;
|
||||
public T5 Comp5;
|
||||
public T6 Comp6;
|
||||
public T7 Comp7;
|
||||
|
||||
public Entity(EntityUid owner, T1 comp1, T2 comp2, T3 comp3, T4 comp4, T5 comp5, T6 comp6, T7 comp7)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp1);
|
||||
DebugTools.AssertOwner(owner, comp2);
|
||||
DebugTools.AssertOwner(owner, comp3);
|
||||
DebugTools.AssertOwner(owner, comp4);
|
||||
DebugTools.AssertOwner(owner, comp5);
|
||||
DebugTools.AssertOwner(owner, comp6);
|
||||
DebugTools.AssertOwner(owner, comp7);
|
||||
|
||||
Owner = owner;
|
||||
Comp1 = comp1;
|
||||
Comp2 = comp2;
|
||||
Comp3 = comp3;
|
||||
Comp4 = comp4;
|
||||
Comp5 = comp5;
|
||||
Comp6 = comp6;
|
||||
Comp7 = comp7;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1, T2, T3, T4, T5, T6, T7>((EntityUid Owner, T1 Comp1, T2 Comp2, T3 Comp3, T4 Comp4, T5 Comp5, T6 Comp6, T7 Comp7) tuple)
|
||||
{
|
||||
return new Entity<T1, T2, T3, T4, T5, T6, T7>(tuple.Owner, tuple.Comp1, tuple.Comp2, tuple.Comp3, tuple.Comp4, tuple.Comp5, tuple.Comp6, tuple.Comp7);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1?, T2?, T3?, T4?, T5?, T6?, T7?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T1?, T2?, T3?, T4?, T5?, T6?, T7?>(owner, default, default, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T1(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Comp1;
|
||||
}
|
||||
|
||||
public static implicit operator T2(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Comp2;
|
||||
}
|
||||
|
||||
public static implicit operator T3(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Comp3;
|
||||
}
|
||||
|
||||
public static implicit operator T4(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Comp4;
|
||||
}
|
||||
|
||||
public static implicit operator T5(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Comp5;
|
||||
}
|
||||
|
||||
public static implicit operator T6(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Comp6;
|
||||
}
|
||||
|
||||
public static implicit operator T7(Entity<T1, T2, T3, T4, T5, T6, T7> ent)
|
||||
{
|
||||
return ent.Comp7;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T1 comp1, out T2 comp2, out T3 comp3, out T4 comp4, out T5 comp5, out T6 comp6, out T7 comp7)
|
||||
{
|
||||
owner = Owner;
|
||||
comp1 = Comp1;
|
||||
comp2 = Comp2;
|
||||
comp3 = Comp3;
|
||||
comp4 = Comp4;
|
||||
comp5 = Comp5;
|
||||
comp6 = Comp6;
|
||||
comp7 = Comp7;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Entity<T1, T2, T3, T4, T5, T6, T7, T8>
|
||||
where T1 : IComponent? where T2 : IComponent? where T3 : IComponent? where T4 : IComponent? where T5 : IComponent? where T6 : IComponent? where T7 : IComponent? where T8 : IComponent?
|
||||
{
|
||||
public EntityUid Owner;
|
||||
public T1 Comp1;
|
||||
public T2 Comp2;
|
||||
public T3 Comp3;
|
||||
public T4 Comp4;
|
||||
public T5 Comp5;
|
||||
public T6 Comp6;
|
||||
public T7 Comp7;
|
||||
public T8 Comp8;
|
||||
|
||||
public Entity(EntityUid owner, T1 comp1, T2 comp2, T3 comp3, T4 comp4, T5 comp5, T6 comp6, T7 comp7, T8 comp8)
|
||||
{
|
||||
DebugTools.AssertOwner(owner, comp1);
|
||||
DebugTools.AssertOwner(owner, comp2);
|
||||
DebugTools.AssertOwner(owner, comp3);
|
||||
DebugTools.AssertOwner(owner, comp4);
|
||||
DebugTools.AssertOwner(owner, comp5);
|
||||
DebugTools.AssertOwner(owner, comp6);
|
||||
DebugTools.AssertOwner(owner, comp7);
|
||||
DebugTools.AssertOwner(owner, comp8);
|
||||
|
||||
Owner = owner;
|
||||
Comp1 = comp1;
|
||||
Comp2 = comp2;
|
||||
Comp3 = comp3;
|
||||
Comp4 = comp4;
|
||||
Comp5 = comp5;
|
||||
Comp6 = comp6;
|
||||
Comp7 = comp7;
|
||||
Comp8 = comp8;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1, T2, T3, T4, T5, T6, T7, T8>((EntityUid Owner, T1 Comp1, T2 Comp2, T3 Comp3, T4 Comp4, T5 Comp5, T6 Comp6, T7 Comp7, T8 Comp8) tuple)
|
||||
{
|
||||
return new Entity<T1, T2, T3, T4, T5, T6, T7, T8>(tuple.Owner, tuple.Comp1, tuple.Comp2, tuple.Comp3, tuple.Comp4, tuple.Comp5, tuple.Comp6, tuple.Comp7, tuple.Comp8);
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T1?, T2?, T3?, T4?, T5?, T6?, T7?, T8?>(EntityUid owner)
|
||||
{
|
||||
return new Entity<T1?, T2?, T3?, T4?, T5?, T6?, T7?, T8?>(owner, default, default, default, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static implicit operator EntityUid(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Owner;
|
||||
}
|
||||
|
||||
public static implicit operator T1(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp1;
|
||||
}
|
||||
|
||||
public static implicit operator T2(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp2;
|
||||
}
|
||||
|
||||
public static implicit operator T3(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp3;
|
||||
}
|
||||
|
||||
public static implicit operator T4(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp4;
|
||||
}
|
||||
|
||||
public static implicit operator T5(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp5;
|
||||
}
|
||||
|
||||
public static implicit operator T6(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp6;
|
||||
}
|
||||
|
||||
public static implicit operator T7(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp7;
|
||||
}
|
||||
|
||||
public static implicit operator T8(Entity<T1, T2, T3, T4, T5, T6, T7, T8> ent)
|
||||
{
|
||||
return ent.Comp8;
|
||||
}
|
||||
|
||||
public readonly void Deconstruct(out EntityUid owner, out T1 comp1, out T2 comp2, out T3 comp3, out T4 comp4, out T5 comp5, out T6 comp6, out T7 comp7, out T8 comp8)
|
||||
{
|
||||
owner = Owner;
|
||||
comp1 = Comp1;
|
||||
comp2 = Comp2;
|
||||
comp3 = Comp3;
|
||||
comp4 = Comp4;
|
||||
comp5 = Comp5;
|
||||
comp6 = Comp6;
|
||||
comp7 = Comp7;
|
||||
comp8 = Comp8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,13 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="source"></param>
|
||||
/// <param name="subscriber">Subscriber that owns the handler.</param>
|
||||
/// <param name="eventHandler">Delegate that handles the event.</param>
|
||||
/// <seealso cref="SubscribeEvent{T}(EventSource, IEntityEventSubscriber, EntityEventRefHandler{T})"/>
|
||||
// [Obsolete("Subscribe to the event by ref instead (EntityEventRefHandler)")]
|
||||
void SubscribeEvent<T>(EventSource source, IEntityEventSubscriber subscriber,
|
||||
EntityEventHandler<T> eventHandler) where T : notnull;
|
||||
|
||||
/// <seealso cref="SubscribeEvent{T}(EventSource, IEntityEventSubscriber, EntityEventRefHandler{T})"/>
|
||||
// [Obsolete("Subscribe to the event by ref instead (EntityEventRefHandler)")]
|
||||
void SubscribeEvent<T>(
|
||||
EventSource source,
|
||||
IEntityEventSubscriber subscriber,
|
||||
@@ -133,7 +137,7 @@ namespace Robust.Shared.GameObjects
|
||||
var type = args.GetType();
|
||||
ref var unitRef = ref ExtractUnitRef(ref args, type);
|
||||
|
||||
ProcessSingleEvent(source, ref unitRef, type, false);
|
||||
ProcessSingleEvent(source, ref unitRef, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +264,7 @@ namespace Robust.Shared.GameObjects
|
||||
var eventType = toRaise.GetType();
|
||||
ref var unitRef = ref ExtractUnitRef(ref toRaise, eventType);
|
||||
|
||||
ProcessSingleEvent(source, ref unitRef, eventType, false);
|
||||
ProcessSingleEvent(source, ref unitRef, eventType);
|
||||
}
|
||||
|
||||
public void RaiseEvent<T>(EventSource source, T toRaise) where T : notnull
|
||||
@@ -268,7 +272,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (source == EventSource.None)
|
||||
throw new ArgumentOutOfRangeException(nameof(source));
|
||||
|
||||
ProcessSingleEvent(source, ref Unsafe.As<T, Unit>(ref toRaise), typeof(T), false);
|
||||
ProcessSingleEvent(source, ref Unsafe.As<T, Unit>(ref toRaise), typeof(T));
|
||||
}
|
||||
|
||||
public void RaiseEvent<T>(EventSource source, ref T toRaise) where T : notnull
|
||||
@@ -276,7 +280,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (source == EventSource.None)
|
||||
throw new ArgumentOutOfRangeException(nameof(source));
|
||||
|
||||
ProcessSingleEvent(source, ref Unsafe.As<T, Unit>(ref toRaise), typeof(T), true);
|
||||
ProcessSingleEvent(source, ref Unsafe.As<T, Unit>(ref toRaise), typeof(T));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -301,7 +305,7 @@ namespace Robust.Shared.GameObjects
|
||||
inverse.Remove(eventType);
|
||||
}
|
||||
|
||||
private void ProcessSingleEvent(EventSource source, ref Unit unitRef, Type eventType, bool byRef)
|
||||
private void ProcessSingleEvent(EventSource source, ref Unit unitRef, Type eventType)
|
||||
{
|
||||
if (!_eventData.TryGetValue(eventType, out var subs))
|
||||
return;
|
||||
@@ -314,20 +318,16 @@ namespace Robust.Shared.GameObjects
|
||||
// This means ordered broadcast events have no overhead over non-ordered ones.
|
||||
}
|
||||
|
||||
ProcessSingleEventCore(source, ref unitRef, subs, byRef);
|
||||
ProcessSingleEventCore(source, ref unitRef, subs);
|
||||
}
|
||||
|
||||
private static void ProcessSingleEventCore(
|
||||
EventSource source,
|
||||
ref Unit unitRef,
|
||||
EventData subs,
|
||||
bool byRef)
|
||||
EventData subs)
|
||||
{
|
||||
foreach (var handler in subs.BroadcastRegistrations)
|
||||
{
|
||||
if (handler.ReferenceEvent != byRef)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
if ((handler.Mask & source) != 0)
|
||||
handler.Handler(ref unitRef);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,6 @@ internal sealed partial class EntityEventBus : IEventBus
|
||||
|
||||
public bool IgnoreUnregisteredComponents;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowByRefMisMatch() =>
|
||||
throw new InvalidOperationException("Mismatching by-ref ness on event!");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ref Unit ExtractUnitRef(ref object obj, Type objType)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,12 @@ namespace Robust.Shared.GameObjects
|
||||
where TComp : IComponent
|
||||
where TEvent : notnull;
|
||||
|
||||
void SubscribeLocalEvent<TComp, TEvent>(
|
||||
EntityEventRefHandler<TComp, TEvent> handler,
|
||||
Type orderType, Type[]? before = null, Type[]? after = null)
|
||||
where TComp : IComponent
|
||||
where TEvent : notnull;
|
||||
|
||||
#endregion
|
||||
|
||||
void UnsubscribeLocalEvent<TComp, TEvent>()
|
||||
@@ -137,8 +143,7 @@ namespace Robust.Shared.GameObjects
|
||||
component.Owner,
|
||||
component,
|
||||
CompIdx.Index(component.GetType()),
|
||||
ref unitRef,
|
||||
false);
|
||||
ref unitRef);
|
||||
}
|
||||
|
||||
void IDirectedEventBus.RaiseComponentEvent<TEvent>(IComponent component, CompIdx type, TEvent args)
|
||||
@@ -149,8 +154,7 @@ namespace Robust.Shared.GameObjects
|
||||
component.Owner,
|
||||
component,
|
||||
type,
|
||||
ref unitRef,
|
||||
false);
|
||||
ref unitRef);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -162,8 +166,7 @@ namespace Robust.Shared.GameObjects
|
||||
component.Owner,
|
||||
component,
|
||||
CompIdx.Index(component.GetType()),
|
||||
ref unitRef,
|
||||
true);
|
||||
ref unitRef);
|
||||
}
|
||||
|
||||
public void OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare()
|
||||
@@ -178,7 +181,7 @@ namespace Robust.Shared.GameObjects
|
||||
var type = typeof(TEvent);
|
||||
ref var unitRef = ref Unsafe.As<TEvent, Unit>(ref args);
|
||||
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast, false);
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -187,7 +190,7 @@ namespace Robust.Shared.GameObjects
|
||||
var type = args.GetType();
|
||||
ref var unitRef = ref Unsafe.As<object, Unit>(ref args);
|
||||
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast, false);
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast);
|
||||
}
|
||||
|
||||
public void RaiseLocalEvent<TEvent>(EntityUid uid, ref TEvent args, bool broadcast = false)
|
||||
@@ -196,7 +199,7 @@ namespace Robust.Shared.GameObjects
|
||||
var type = typeof(TEvent);
|
||||
ref var unitRef = ref Unsafe.As<TEvent, Unit>(ref args);
|
||||
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast, true);
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast);
|
||||
}
|
||||
|
||||
public void RaiseLocalEvent(EntityUid uid, ref object args, bool broadcast = false)
|
||||
@@ -204,25 +207,25 @@ namespace Robust.Shared.GameObjects
|
||||
var type = args.GetType();
|
||||
ref var unitRef = ref Unsafe.As<object, Unit>(ref args);
|
||||
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast, true);
|
||||
RaiseLocalEventCore(uid, ref unitRef, type, broadcast);
|
||||
}
|
||||
|
||||
private void RaiseLocalEventCore(EntityUid uid, ref Unit unitRef, Type type, bool broadcast, bool byRef)
|
||||
private void RaiseLocalEventCore(EntityUid uid, ref Unit unitRef, Type type, bool broadcast)
|
||||
{
|
||||
if (!_eventData.TryGetValue(type, out var subs))
|
||||
return;
|
||||
|
||||
if (subs.IsOrdered)
|
||||
{
|
||||
RaiseLocalOrdered(uid, type, subs, ref unitRef, broadcast, byRef);
|
||||
RaiseLocalOrdered(uid, type, subs, ref unitRef, broadcast);
|
||||
return;
|
||||
}
|
||||
|
||||
EntDispatch(uid, type, ref unitRef, byRef);
|
||||
EntDispatch(uid, type, ref unitRef);
|
||||
|
||||
// we also broadcast it so the call site does not have to.
|
||||
if (broadcast)
|
||||
ProcessSingleEventCore(EventSource.Local, ref unitRef, subs, byRef);
|
||||
ProcessSingleEventCore(EventSource.Local, ref unitRef, subs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -238,8 +241,7 @@ namespace Robust.Shared.GameObjects
|
||||
typeof(TComp),
|
||||
typeof(TEvent),
|
||||
EventHandler,
|
||||
null,
|
||||
false);
|
||||
null);
|
||||
}
|
||||
|
||||
public void SubscribeLocalEvent<TComp, TEvent>(
|
||||
@@ -260,8 +262,7 @@ namespace Robust.Shared.GameObjects
|
||||
typeof(TComp),
|
||||
typeof(TEvent),
|
||||
EventHandler,
|
||||
orderData,
|
||||
false);
|
||||
orderData);
|
||||
|
||||
RegisterCommon(typeof(TEvent), orderData, out _);
|
||||
}
|
||||
@@ -277,8 +278,7 @@ namespace Robust.Shared.GameObjects
|
||||
typeof(TComp),
|
||||
typeof(TEvent),
|
||||
EventHandler,
|
||||
null,
|
||||
true);
|
||||
null);
|
||||
}
|
||||
|
||||
public void SubscribeLocalEvent<TComp, TEvent>(ComponentEventRefHandler<TComp, TEvent> handler, Type orderType,
|
||||
@@ -295,8 +295,26 @@ namespace Robust.Shared.GameObjects
|
||||
typeof(TComp),
|
||||
typeof(TEvent),
|
||||
EventHandler,
|
||||
orderData,
|
||||
true);
|
||||
orderData);
|
||||
|
||||
RegisterCommon(typeof(TEvent), orderData, out _);
|
||||
}
|
||||
|
||||
public void SubscribeLocalEvent<TComp, TEvent>(EntityEventRefHandler<TComp, TEvent> handler, Type orderType,
|
||||
Type[]? before = null,
|
||||
Type[]? after = null) where TComp : IComponent where TEvent : notnull
|
||||
{
|
||||
void EventHandler(EntityUid uid, IComponent comp, ref TEvent args)
|
||||
=> handler(new Entity<TComp>(uid, (TComp) comp), ref args);
|
||||
|
||||
var orderData = new OrderingData(orderType, before ?? Array.Empty<Type>(), after ?? Array.Empty<Type>());
|
||||
|
||||
EntSubscribe<TEvent>(
|
||||
CompIdx.Index<TComp>(),
|
||||
typeof(TComp),
|
||||
typeof(TEvent),
|
||||
EventHandler,
|
||||
orderData);
|
||||
|
||||
RegisterCommon(typeof(TEvent), orderData, out _);
|
||||
}
|
||||
@@ -345,12 +363,6 @@ namespace Robust.Shared.GameObjects
|
||||
if (_subscriptionLock)
|
||||
throw new InvalidOperationException("Subscription locked.");
|
||||
|
||||
var referenceEvent = eventType.HasCustomAttribute<ByRefEventAttribute>();
|
||||
|
||||
if (referenceEvent != registration.ReferenceEvent)
|
||||
throw new InvalidOperationException(
|
||||
$"Attempted to subscribe by-ref and by-value to the same directed event! comp={compTypeObj.Name}, event={eventType.Name} eventIsByRef={referenceEvent} subscriptionIsByRef={registration.ReferenceEvent}");
|
||||
|
||||
if (compType.Value >= _entSubscriptions.Length || _entSubscriptions[compType.Value] is not { } compSubs)
|
||||
{
|
||||
if (IgnoreUnregisteredComponents)
|
||||
@@ -377,7 +389,7 @@ namespace Robust.Shared.GameObjects
|
||||
Type compTypeObj,
|
||||
Type eventType,
|
||||
DirectedEventHandler<TEvent> handler,
|
||||
OrderingData? order, bool byReference)
|
||||
OrderingData? order)
|
||||
where TEvent : notnull
|
||||
{
|
||||
EntAddSubscription(compType, compTypeObj, eventType, new DirectedRegistration(handler, order,
|
||||
@@ -385,7 +397,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
ref var tev = ref Unsafe.As<Unit, TEvent>(ref ev);
|
||||
handler(uid, comp, ref tev);
|
||||
}, byReference));
|
||||
}));
|
||||
}
|
||||
|
||||
private void EntUnsubscribe(CompIdx compType, Type eventType)
|
||||
@@ -525,7 +537,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private void EntDispatch(EntityUid euid, Type eventType, ref Unit args, bool dispatchByReference)
|
||||
private void EntDispatch(EntityUid euid, Type eventType, ref Unit args)
|
||||
{
|
||||
if (!EntTryGetSubscriptions(eventType, euid, out var enumerator))
|
||||
return;
|
||||
@@ -535,9 +547,6 @@ namespace Robust.Shared.GameObjects
|
||||
if (component.Deleted)
|
||||
continue;
|
||||
|
||||
if (reg.ReferenceEvent != dispatchByReference)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
reg.Handler(euid, component, ref args);
|
||||
}
|
||||
}
|
||||
@@ -545,22 +554,18 @@ namespace Robust.Shared.GameObjects
|
||||
private void EntCollectOrdered(
|
||||
EntityUid euid,
|
||||
Type eventType,
|
||||
ref ValueList<OrderedEventDispatch> found,
|
||||
bool byRef)
|
||||
ref ValueList<OrderedEventDispatch> found)
|
||||
{
|
||||
if (!EntTryGetSubscriptions(eventType, euid, out var enumerator))
|
||||
return;
|
||||
|
||||
while (enumerator.MoveNext(out var component, out var reg))
|
||||
{
|
||||
if (reg.ReferenceEvent != byRef)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
found.Add(new OrderedEventDispatch((ref Unit ev) =>
|
||||
{
|
||||
if (!component.Deleted)
|
||||
reg.Handler(euid, component, ref ev);
|
||||
}, reg.Order));
|
||||
}, reg.Order));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,8 +573,7 @@ namespace Robust.Shared.GameObjects
|
||||
EntityUid euid,
|
||||
IComponent component,
|
||||
CompIdx baseType,
|
||||
ref Unit args,
|
||||
bool dispatchByReference)
|
||||
ref Unit args)
|
||||
where TEvent : notnull
|
||||
{
|
||||
var compSubs = _entSubscriptions[baseType.Value]!;
|
||||
@@ -577,9 +581,6 @@ namespace Robust.Shared.GameObjects
|
||||
if (!compSubs.TryGetValue(typeof(TEvent), out var reg))
|
||||
return;
|
||||
|
||||
if (reg.ReferenceEvent != dispatchByReference)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
reg.Handler(euid, component, ref args);
|
||||
}
|
||||
|
||||
@@ -690,17 +691,14 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public readonly Delegate Original;
|
||||
public readonly DirectedEventHandler Handler;
|
||||
public readonly bool ReferenceEvent;
|
||||
|
||||
public DirectedRegistration(
|
||||
Delegate original,
|
||||
OrderingData? ordering,
|
||||
DirectedEventHandler handler,
|
||||
bool referenceEvent) : base(ordering)
|
||||
DirectedEventHandler handler) : base(ordering)
|
||||
{
|
||||
Original = original;
|
||||
Handler = handler;
|
||||
ReferenceEvent = referenceEvent;
|
||||
}
|
||||
|
||||
public void SetOrder(int order)
|
||||
@@ -736,6 +734,8 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <seealso cref="ComponentEventRefHandler{TComp, TEvent}"/>
|
||||
// [Obsolete("Use ComponentEventRefHandler instead")]
|
||||
public delegate void ComponentEventHandler<in TComp, in TEvent>(EntityUid uid, TComp component, TEvent args)
|
||||
where TComp : IComponent
|
||||
where TEvent : notnull;
|
||||
@@ -743,4 +743,8 @@ namespace Robust.Shared.GameObjects
|
||||
public delegate void ComponentEventRefHandler<in TComp, TEvent>(EntityUid uid, TComp component, ref TEvent args)
|
||||
where TComp : IComponent
|
||||
where TEvent : notnull;
|
||||
|
||||
public delegate void EntityEventRefHandler<TComp, TEvent>(Entity<TComp> ent, ref TEvent args)
|
||||
where TComp : IComponent
|
||||
where TEvent : notnull;
|
||||
}
|
||||
|
||||
@@ -11,14 +11,10 @@ namespace Robust.Shared.GameObjects
|
||||
private static void CollectBroadcastOrdered(
|
||||
EventSource source,
|
||||
EventData sub,
|
||||
ref ValueList<OrderedEventDispatch> found,
|
||||
bool byRef)
|
||||
ref ValueList<OrderedEventDispatch> found)
|
||||
{
|
||||
foreach (var handler in sub.BroadcastRegistrations)
|
||||
{
|
||||
if (handler.ReferenceEvent != byRef)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
if ((handler.Mask & source) != 0)
|
||||
found.Add(new OrderedEventDispatch(handler.Handler, handler.Order));
|
||||
}
|
||||
@@ -29,8 +25,7 @@ namespace Robust.Shared.GameObjects
|
||||
Type eventType,
|
||||
EventData subs,
|
||||
ref Unit unitRef,
|
||||
bool broadcast,
|
||||
bool byRef)
|
||||
bool broadcast)
|
||||
{
|
||||
if (!subs.OrderingUpToDate)
|
||||
UpdateOrderSeq(eventType, subs);
|
||||
@@ -38,9 +33,9 @@ namespace Robust.Shared.GameObjects
|
||||
var found = new ValueList<OrderedEventDispatch>();
|
||||
|
||||
if (broadcast)
|
||||
CollectBroadcastOrdered(EventSource.Local, subs, ref found, byRef);
|
||||
CollectBroadcastOrdered(EventSource.Local, subs, ref found);
|
||||
|
||||
EntCollectOrdered(uid, eventType, ref found, byRef);
|
||||
EntCollectOrdered(uid, eventType, ref found);
|
||||
|
||||
DispatchOrderedEvents(ref unitRef, ref found);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
93
Robust.Shared/GameObjects/EntityManager.LifeCycle.cs
Normal file
93
Robust.Shared/GameObjects/EntityManager.LifeCycle.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial class EntityManager
|
||||
{
|
||||
private static readonly ComponentAdd CompAddInstance = new();
|
||||
private static readonly ComponentInit CompInitInstance = new();
|
||||
private static readonly ComponentStartup CompStartupInstance = new();
|
||||
private static readonly ComponentShutdown CompShutdownInstance = new();
|
||||
private static readonly ComponentRemove CompRemoveInstance = new();
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.PreAdd" /> to <see cref="ComponentLifeStage.Added" />,
|
||||
/// after raising a <see cref="ComponentAdd"/> event.
|
||||
/// </summary>
|
||||
internal void LifeAddToEntity(IComponent component, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.PreAdd);
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
component.LifeStage = ComponentLifeStage.Adding;
|
||||
component.CreationTick = CurrentTick;
|
||||
// networked components are assumed to be dirty when added to entities. See also: ClearTicks()
|
||||
component.LastModifiedTick = CurrentTick;
|
||||
EventBus.RaiseComponentEvent(component, type, CompAddInstance);
|
||||
component.LifeStage = ComponentLifeStage.Added;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Added" /> to <see cref="ComponentLifeStage.Initialized" />,
|
||||
/// calling <see cref="Initialize" />.
|
||||
/// </summary>
|
||||
internal void LifeInitialize(IComponent component, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Added);
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Initializing;
|
||||
EventBus.RaiseComponentEvent(component, type, CompInitInstance);
|
||||
component.LifeStage = ComponentLifeStage.Initialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Initialized" /> to
|
||||
/// <see cref="ComponentLifeStage.Running" />, calling <see cref="Startup" />.
|
||||
/// </summary>
|
||||
internal void LifeStartup(IComponent component)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Initialized);
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Starting;
|
||||
EventBus.RaiseComponentEvent(component, CompStartupInstance);
|
||||
component.LifeStage = ComponentLifeStage.Running;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Running" /> to <see cref="ComponentLifeStage.Stopped" />,
|
||||
/// calling <see cref="Shutdown" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components are allowed to remove themselves in their own Startup function.
|
||||
/// </remarks>
|
||||
internal void LifeShutdown(IComponent component)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
|
||||
|
||||
if (component.LifeStage <= ComponentLifeStage.Initialized)
|
||||
{
|
||||
// Component was never started, no shutdown logic necessary. Simply mark it as stopped.
|
||||
component.LifeStage = ComponentLifeStage.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Stopping;
|
||||
EventBus.RaiseComponentEvent(component, CompShutdownInstance);
|
||||
component.LifeStage = ComponentLifeStage.Stopped;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Stopped" /> to <see cref="ComponentLifeStage.Deleted" />,
|
||||
/// calling <see cref="Component.OnRemove" />.
|
||||
/// </summary>
|
||||
internal void LifeRemoveFromEntity(IComponent component)
|
||||
{
|
||||
// can be called at any time after PreAdd, including inside other life stage events.
|
||||
DebugTools.Assert(component.LifeStage != ComponentLifeStage.PreAdd);
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Removing;
|
||||
EventBus.RaiseComponentEvent(component, CompRemoveInstance);
|
||||
component.LifeStage = ComponentLifeStage.Deleted;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public partial class EntityManager
|
||||
return true;
|
||||
}
|
||||
|
||||
entity = EntityUid.Invalid;
|
||||
entity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public partial class EntityManager
|
||||
{
|
||||
if (nEntity == null)
|
||||
{
|
||||
entity = EntityUid.Invalid;
|
||||
entity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public partial class EntityManager
|
||||
{
|
||||
if (uid == EntityUid.Invalid)
|
||||
{
|
||||
netEntity = NetEntity.Invalid;
|
||||
netEntity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ public partial class EntityManager
|
||||
{
|
||||
if (uid == null)
|
||||
{
|
||||
netEntity = NetEntity.Invalid;
|
||||
netEntity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ public partial class EntityManager
|
||||
return Spawn(protoName);
|
||||
|
||||
var uid = Spawn(protoName, overrides);
|
||||
_xforms.PlaceNextToOrDrop(uid, target);
|
||||
_xforms.DropNextTo(uid, target);
|
||||
return uid;
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ public partial class EntityManager
|
||||
|
||||
xform ??= TransformQuery.GetComponent(containerUid);
|
||||
if (xform.ParentUid.IsValid())
|
||||
_xforms.PlaceNextToOrDrop(uid, containerUid, targetXform: xform);
|
||||
_xforms.DropNextTo(uid, (containerUid, xform));
|
||||
}
|
||||
|
||||
return uid;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public delegate void EntityUidQueryCallback(EntityUid uid);
|
||||
|
||||
public delegate void ComponentQueryCallback<T>(EntityUid uid, T component) where T : Component;
|
||||
public delegate void ComponentQueryCallback<T>(EntityUid uid, T component) where T : IComponent;
|
||||
|
||||
/// <inheritdoc />
|
||||
[Virtual]
|
||||
@@ -311,19 +311,19 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, overrides);
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, overrides);
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, overrides);
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
_xforms.SetCoordinates(newEntity, TransformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
return newEntity;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, overrides);
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
var transform = TransformQuery.GetComponent(newEntity);
|
||||
|
||||
if (coordinates.MapId == MapId.Nullspace)
|
||||
@@ -387,19 +387,28 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("use override with an EntityUid")]
|
||||
public void Dirty(Component component, MetaDataComponent? meta = null)
|
||||
public void Dirty(IComponent component, MetaDataComponent? meta = null)
|
||||
{
|
||||
Dirty(component.Owner, component, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dirty(EntityUid uid, Component component, MetaDataComponent? meta = null)
|
||||
public virtual void Dirty(EntityUid uid, IComponent component, MetaDataComponent? meta = null)
|
||||
{
|
||||
if (component.LifeStage >= ComponentLifeStage.Removing || !component.NetSyncEnabled)
|
||||
Dirty(new Entity<IComponent>(uid, component), meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dirty<T>(Entity<T> ent, MetaDataComponent? meta = null) where T : IComponent
|
||||
{
|
||||
if (ent.Comp.LifeStage >= ComponentLifeStage.Removing || !ent.Comp.NetSyncEnabled)
|
||||
return;
|
||||
|
||||
DirtyEntity(uid, meta);
|
||||
component.LastModifiedTick = CurrentTick;
|
||||
DebugTools.AssertOwner(ent, ent.Comp);
|
||||
DirtyEntity(ent, meta);
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
ent.Comp.LastModifiedTick = CurrentTick;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -435,8 +444,13 @@ namespace Robust.Shared.GameObjects
|
||||
// Notify all entities they are being terminated prior to detaching & deleting
|
||||
RecursiveFlagEntityTermination(e, meta);
|
||||
|
||||
var xform = TransformQuery.GetComponent(e);
|
||||
TransformComponent? parentXform = null;
|
||||
if (xform.ParentUid.IsValid())
|
||||
TransformQuery.Resolve(xform.ParentUid, ref parentXform);
|
||||
|
||||
// Then actually delete them
|
||||
RecursiveDeleteEntity(e, meta);
|
||||
RecursiveDeleteEntity(e, meta, xform, parentXform);
|
||||
}
|
||||
|
||||
private void RecursiveFlagEntityTermination(
|
||||
@@ -471,11 +485,14 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private void RecursiveDeleteEntity(
|
||||
EntityUid uid,
|
||||
MetaDataComponent metadata)
|
||||
MetaDataComponent metadata,
|
||||
TransformComponent transform,
|
||||
TransformComponent? parentXform)
|
||||
{
|
||||
DebugTools.Assert(transform.ParentUid.IsValid() == (parentXform != null));
|
||||
DebugTools.Assert(parentXform == null || parentXform.ChildEntities.Contains(uid));
|
||||
|
||||
// Note about this method: #if EXCEPTION_TOLERANCE is not used here because we're gonna it in the future...
|
||||
var netEntity = GetNetEntity(uid, metadata);
|
||||
var transform = TransformQuery.GetComponent(uid);
|
||||
|
||||
// Detach the base entity to null before iterating over children
|
||||
// This also ensures that the entity-lookup updates don't have to be re-run for every child (which recurses up the transform hierarchy).
|
||||
@@ -483,7 +500,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
_xforms.DetachParentToNull(uid, transform);
|
||||
_xforms.DetachParentToNull(uid, transform, parentXform);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -495,7 +512,10 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
RecursiveDeleteEntity(child, MetaQuery.GetComponent(child));
|
||||
var childMeta = MetaQuery.GetComponent(child);
|
||||
var childXform = TransformQuery.GetComponent(child);
|
||||
DebugTools.AssertEqual(childXform.ParentUid, uid);
|
||||
RecursiveDeleteEntity(child, childMeta, childXform, transform);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@@ -513,7 +533,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
component.LifeShutdown(this);
|
||||
LifeShutdown(component);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -538,7 +558,7 @@ namespace Robust.Shared.GameObjects
|
||||
_eventBus.OnEntityDeleted(uid);
|
||||
Entities.Remove(uid);
|
||||
// Need to get the ID above before MetadataComponent shutdown but only remove it after everything else is done.
|
||||
NetEntityLookup.Remove(netEntity);
|
||||
NetEntityLookup.Remove(metadata.NetEntity);
|
||||
}
|
||||
|
||||
public virtual void QueueDeleteEntity(EntityUid? uid)
|
||||
@@ -637,6 +657,7 @@ namespace Robust.Shared.GameObjects
|
||||
#pragma warning disable CS0618
|
||||
Owner = uid,
|
||||
#pragma warning restore CS0618
|
||||
EntityLastModifiedTick = _gameTiming.CurTick
|
||||
};
|
||||
|
||||
SetNetEntity(uid, netEntity, metadata);
|
||||
@@ -647,7 +668,9 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// allocate the required TransformComponent
|
||||
var xformComp = Unsafe.As<TransformComponent>(_componentFactory.GetComponent(_xformReg));
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
xformComp.Owner = uid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
AddComponentInternal(uid, xformComp, false, true, metadata);
|
||||
|
||||
return uid;
|
||||
@@ -656,23 +679,23 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(out _);
|
||||
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, 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, IEntityLoadContext? context = null)
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = AllocEntity(prototype, out var metadata);
|
||||
var entity = AllocEntity(prototype, out metadata);
|
||||
try
|
||||
{
|
||||
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
@@ -740,36 +763,34 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
public virtual EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
public EntityStringRepresentation? ToPrettyString(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
// We want to retrieve the MetaData component even if it is deleted.
|
||||
if (uid == null)
|
||||
return null;
|
||||
return uid == null ? null : ToPrettyString(uid.Value, metadata);
|
||||
}
|
||||
|
||||
if (!_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid.Value, out var component))
|
||||
return new EntityStringRepresentation(uid.Value, true);
|
||||
/// <inheritdoc />
|
||||
public virtual EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (!MetaQuery.Resolve(uid, ref metadata, false))
|
||||
return new EntityStringRepresentation(uid, true);
|
||||
|
||||
var metadata = (MetaDataComponent) component;
|
||||
|
||||
return ToPrettyString(uid.Value, metadata);
|
||||
return new EntityStringRepresentation(uid, metadata.EntityDeleted, metadata.EntityName, metadata.EntityPrototype?.ID);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[return: NotNullIfNotNull("netEntity")]
|
||||
public EntityStringRepresentation? ToPrettyString(NetEntity? netEntity)
|
||||
{
|
||||
return ToPrettyString(GetEntity(netEntity));
|
||||
return netEntity == null ? null : ToPrettyString(netEntity.Value);
|
||||
}
|
||||
|
||||
public EntityStringRepresentation ToPrettyString(EntityUid uid)
|
||||
=> ToPrettyString((EntityUid?) uid).Value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityStringRepresentation ToPrettyString(NetEntity netEntity)
|
||||
=> ToPrettyString((NetEntity?) netEntity).Value;
|
||||
|
||||
private EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent metadata)
|
||||
{
|
||||
return new EntityStringRepresentation(uid, metadata.EntityDeleted, metadata.EntityName, metadata.EntityPrototype?.ID);
|
||||
if (!TryGetEntityData(netEntity, out var uid, out var meta))
|
||||
return new EntityStringRepresentation(EntityUid.Invalid, true);
|
||||
|
||||
return ToPrettyString(uid.Value, meta);
|
||||
}
|
||||
|
||||
#endregion Entity Management
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
public static class EntityManagerExt
|
||||
{
|
||||
public static T? GetComponentOrNull<T>(this IEntityManager entityManager, EntityUid entityUid)
|
||||
where T : class, IComponent
|
||||
where T : IComponent
|
||||
{
|
||||
if (entityManager.TryGetComponent(entityUid, out T? component))
|
||||
return component;
|
||||
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
|
||||
public static T? GetComponentOrNull<T>(this IEntityManager entityManager, EntityUid? entityUid)
|
||||
where T : class, IComponent
|
||||
where T : IComponent
|
||||
{
|
||||
if (entityUid.HasValue && entityManager.TryGetComponent(entityUid.Value, out T? component))
|
||||
return component;
|
||||
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,20 +206,29 @@ public partial class EntitySystem
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Obsolete("Use Dirty(EntityUid, Component, MetaDataComponent?")]
|
||||
protected void Dirty(Component component, MetaDataComponent? meta = null)
|
||||
protected void Dirty(IComponent component, MetaDataComponent? meta = null)
|
||||
{
|
||||
EntityManager.Dirty(component, meta);
|
||||
EntityManager.Dirty(component.Owner, component, meta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void Dirty(EntityUid uid, Component component, MetaDataComponent? meta = null)
|
||||
protected void Dirty(EntityUid uid, IComponent component, MetaDataComponent? meta = null)
|
||||
{
|
||||
EntityManager.Dirty(uid, component, 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> ent, MetaDataComponent? meta = null) where T : IComponent
|
||||
{
|
||||
EntityManager.Dirty(ent.Owner, ent.Comp, meta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the name of an entity.
|
||||
/// </summary>
|
||||
@@ -396,15 +405,15 @@ public partial class EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString(EntityUid, MetaDataComponent?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
protected EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
protected EntityStringRepresentation? ToPrettyString(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.ToPrettyString(uid);
|
||||
return EntityManager.ToPrettyString(uid, metadata);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString(EntityUid, MetaDataComponent?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: NotNullIfNotNull("netEntity")]
|
||||
protected EntityStringRepresentation? ToPrettyString(NetEntity? netEntity)
|
||||
@@ -412,15 +421,20 @@ public partial class EntitySystem
|
||||
return EntityManager.ToPrettyString(netEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString(EntityUid, MetaDataComponent?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata)
|
||||
=> EntityManager.ToPrettyString(uid, metadata);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString(EntityUid, MetaDataComponent?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityStringRepresentation ToPrettyString(EntityUid uid)
|
||||
=> ToPrettyString((EntityUid?) uid).Value;
|
||||
=> EntityManager.ToPrettyString(uid);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString(EntityUid, MetaDataComponent?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityStringRepresentation ToPrettyString(NetEntity netEntity)
|
||||
=> ToPrettyString((NetEntity?) netEntity).Value;
|
||||
=> EntityManager.ToPrettyString(netEntity);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -428,7 +442,7 @@ public partial class EntitySystem
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.GetComponent<T>"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected T Comp<T>(EntityUid uid) where T : Component
|
||||
protected T Comp<T>(EntityUid uid) where T : IComponent
|
||||
{
|
||||
return EntityManager.GetComponent<T>(uid);
|
||||
}
|
||||
@@ -437,7 +451,7 @@ public partial class EntitySystem
|
||||
/// Returns the component of a specific type, or null when it's missing or the entity does not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected T? CompOrNull<T>(EntityUid uid) where T : class, IComponent
|
||||
protected T? CompOrNull<T>(EntityUid uid) where T : IComponent
|
||||
{
|
||||
return EntityManager.GetComponentOrNull<T>(uid);
|
||||
}
|
||||
@@ -446,14 +460,14 @@ public partial class EntitySystem
|
||||
/// Returns the component of a specific type, or null when it's missing or the entity does not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected T? CompOrNull<T>(EntityUid? uid) where T : class, IComponent
|
||||
protected T? CompOrNull<T>(EntityUid? uid) where T : IComponent
|
||||
{
|
||||
return uid.HasValue ? EntityManager.GetComponentOrNull<T>(uid.Value) : null;
|
||||
return uid.HasValue ? EntityManager.GetComponentOrNull<T>(uid.Value) : default;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TryGetComponent<T>(EntityUid, out T)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool TryComp<T>(EntityUid uid, [NotNullWhen(true)] out T? comp)
|
||||
protected bool TryComp<T>(EntityUid uid, [NotNullWhen(true)] out T? comp) where T : IComponent
|
||||
{
|
||||
return EntityManager.TryGetComponent(uid, out comp);
|
||||
}
|
||||
@@ -474,7 +488,7 @@ public partial class EntitySystem
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TryGetComponent<T>(EntityUid?, out T)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool TryComp<T>([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out T? comp)
|
||||
protected bool TryComp<T>([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out T? comp) where T : IComponent
|
||||
{
|
||||
if (!uid.HasValue)
|
||||
{
|
||||
@@ -609,14 +623,14 @@ public partial class EntitySystem
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.AddComponent<T>(EntityUid, T, bool)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void AddComp<T>(EntityUid uid, T component, bool overwrite = false) where T : Component
|
||||
protected void AddComp<T>(EntityUid uid, T component, bool overwrite = false) where T : IComponent
|
||||
{
|
||||
EntityManager.AddComponent(uid, component, overwrite);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.EnsureComponent<T>(EntityUid)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected T EnsureComp<T>(EntityUid uid) where T : Component, new()
|
||||
protected T EnsureComp<T>(EntityUid uid) where T : IComponent, new()
|
||||
{
|
||||
return EntityManager.EnsureComponent<T>(uid);
|
||||
}
|
||||
@@ -627,7 +641,7 @@ public partial class EntitySystem
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.RemoveComponentDeferred<T>(EntityUid)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool RemCompDeferred<T>(EntityUid uid) where T : class, IComponent
|
||||
protected bool RemCompDeferred<T>(EntityUid uid) where T : IComponent
|
||||
{
|
||||
return EntityManager.RemoveComponentDeferred<T>(uid);
|
||||
}
|
||||
@@ -658,7 +672,7 @@ public partial class EntitySystem
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.Count" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected int Count<T>() where T : Component
|
||||
protected int Count<T>() where T : IComponent
|
||||
{
|
||||
return EntityManager.Count<T>();
|
||||
}
|
||||
@@ -676,7 +690,7 @@ public partial class EntitySystem
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.RemoveComponent<T>(EntityUid)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool RemComp<T>(EntityUid uid) where T : class, IComponent
|
||||
protected bool RemComp<T>(EntityUid uid) where T : IComponent
|
||||
{
|
||||
return EntityManager.RemoveComponent<T>(uid);
|
||||
}
|
||||
@@ -814,34 +828,34 @@ public partial class EntitySystem
|
||||
#region All Entity Query
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected AllEntityQueryEnumerator<TComp1> AllEntityQuery<TComp1>() where TComp1 : Component
|
||||
protected AllEntityQueryEnumerator<TComp1> AllEntityQuery<TComp1>() where TComp1 : IComponent
|
||||
{
|
||||
return EntityManager.AllEntityQueryEnumerator<TComp1>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected AllEntityQueryEnumerator<TComp1, TComp2> AllEntityQuery<TComp1, TComp2>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
{
|
||||
return EntityManager.AllEntityQueryEnumerator<TComp1, TComp2>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected AllEntityQueryEnumerator<TComp1, TComp2, TComp3> AllEntityQuery<TComp1, TComp2, TComp3>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
{
|
||||
return EntityManager.AllEntityQueryEnumerator<TComp1, TComp2, TComp3>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected AllEntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> AllEntityQuery<TComp1, TComp2, TComp3, TComp4>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp4 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
where TComp4 : IComponent
|
||||
{
|
||||
return EntityManager.AllEntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>();
|
||||
}
|
||||
@@ -851,34 +865,34 @@ public partial class EntitySystem
|
||||
#region Get Entity Query
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityQueryEnumerator<TComp1> EntityQueryEnumerator<TComp1>() where TComp1 : Component
|
||||
protected EntityQueryEnumerator<TComp1> EntityQueryEnumerator<TComp1>() where TComp1 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQueryEnumerator<TComp1>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityQueryEnumerator<TComp1, TComp2> EntityQueryEnumerator<TComp1, TComp2>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQueryEnumerator<TComp1, TComp2>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityQueryEnumerator<TComp1, TComp2, TComp3> EntityQueryEnumerator<TComp1, TComp2, TComp3>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQueryEnumerator<TComp1, TComp2, TComp3>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp4 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
where TComp4 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>();
|
||||
}
|
||||
@@ -892,7 +906,7 @@ public partial class EntitySystem
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
protected EntityQuery<T> GetEntityQuery<T>() where T : Component
|
||||
protected EntityQuery<T> GetEntityQuery<T>() where T : IComponent
|
||||
{
|
||||
return EntityManager.GetEntityQuery<T>();
|
||||
}
|
||||
@@ -901,7 +915,7 @@ public partial class EntitySystem
|
||||
/// If you need the EntityUid, use <see cref="EntityQueryEnumerator{TComp1}"/>
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected IEnumerable<TComp1> EntityQuery<TComp1>(bool includePaused = false) where TComp1 : Component
|
||||
protected IEnumerable<TComp1> EntityQuery<TComp1>(bool includePaused = false) where TComp1 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQuery<TComp1>(includePaused);
|
||||
}
|
||||
@@ -911,8 +925,8 @@ public partial class EntitySystem
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected IEnumerable<(TComp1, TComp2)> EntityQuery<TComp1, TComp2>(bool includePaused = false)
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQuery<TComp1, TComp2>(includePaused);
|
||||
}
|
||||
@@ -922,9 +936,9 @@ public partial class EntitySystem
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected IEnumerable<(TComp1, TComp2, TComp3)> EntityQuery<TComp1, TComp2, TComp3>(bool includePaused = false)
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQuery<TComp1, TComp2, TComp3>(includePaused);
|
||||
}
|
||||
@@ -934,10 +948,10 @@ public partial class EntitySystem
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected IEnumerable<(TComp1, TComp2, TComp3, TComp4)> EntityQuery<TComp1, TComp2, TComp3, TComp4>(bool includePaused = false)
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp4 : Component
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
where TComp4 : IComponent
|
||||
{
|
||||
return EntityManager.EntityQuery<TComp1, TComp2, TComp3, TComp4>(includePaused);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Shared.GameObjects
|
||||
protected bool Resolve<TComp>(EntityUid uid, [NotNullWhen(true)] ref TComp? component, bool logMissing = true)
|
||||
where TComp : IComponent
|
||||
{
|
||||
DebugTools.Assert(component == null || uid == component.Owner, "Specified Entity is not the component's Owner!");
|
||||
DebugTools.AssertOwner(uid, component);
|
||||
|
||||
if (component != null && !component.Deleted)
|
||||
return true;
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Shared.GameObjects
|
||||
var found = EntityManager.TryGetComponent(uid, out component);
|
||||
|
||||
if(logMissing && !found)
|
||||
Log.Error($"Can't resolve \"{typeof(TComp)}\" on entity {uid}!\n{new StackTrace(1, true)}");
|
||||
Log.Error($"Can't resolve \"{typeof(TComp)}\" on entity {ToPrettyString(uid)}!\n{new StackTrace(1, true)}");
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Robust.Shared.GameObjects
|
||||
SubEvent(EventSource.Network, handler, before, after);
|
||||
}
|
||||
|
||||
/// <seealso cref="SubscribeLocalEvent{T}(EntityEventRefHandler{T}, Type[], Type[])"/>
|
||||
// [Obsolete("Subscribe to the event by ref instead (EntityEventRefHandler)")]
|
||||
protected void SubscribeLocalEvent<T>(
|
||||
EntityEventHandler<T> handler,
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
@@ -72,6 +74,8 @@ namespace Robust.Shared.GameObjects
|
||||
SubSessionEvent(EventSource.All, handler, before, after);
|
||||
}
|
||||
|
||||
/// <seealso cref="SubEvent{T}(EventSource, EntityEventRefHandler{T}, Type[], Type[])"/>
|
||||
// [Obsolete("Subscribe to the event by ref instead (EntityEventRefHandler)")]
|
||||
private void SubEvent<T>(
|
||||
EventSource src,
|
||||
EntityEventHandler<T> handler,
|
||||
@@ -108,6 +112,8 @@ namespace Robust.Shared.GameObjects
|
||||
_subscriptions.Add(new SubBroadcast<EntitySessionMessage<T>>(src));
|
||||
}
|
||||
|
||||
/// <seealso cref="SubscribeLocalEvent{TComp, TEvent}(ComponentEventRefHandler{TComp, TEvent}, Type[], Type[])"/>
|
||||
// [Obsolete("Subscribe to the event by ref instead (ComponentEventRefHandler)")]
|
||||
protected void SubscribeLocalEvent<TComp, TEvent>(
|
||||
ComponentEventHandler<TComp, TEvent> handler,
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
@@ -132,6 +138,18 @@ namespace Robust.Shared.GameObjects
|
||||
_subscriptions.Add(new SubLocal<TComp, TEvent>());
|
||||
}
|
||||
|
||||
protected void SubscribeLocalEvent<TComp, TEvent>(
|
||||
EntityEventRefHandler<TComp, TEvent> handler,
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where TComp : IComponent
|
||||
where TEvent : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeLocalEvent(handler, GetType(), before, after);
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubLocal<TComp, TEvent>());
|
||||
}
|
||||
|
||||
private void ShutdownSubscriptions()
|
||||
{
|
||||
if (_subscriptions == null)
|
||||
|
||||
@@ -4,7 +4,6 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -153,7 +152,7 @@ namespace Robust.Shared.GameObjects
|
||||
set
|
||||
{
|
||||
if (MetaData is {} metaData)
|
||||
metaData.EntityName = value;
|
||||
IoCManager.Resolve<IEntityManager>().System<MetaDataSystem>().SetEntityName(this, value, metaData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,8 +162,11 @@ namespace Robust.Shared.GameObjects
|
||||
get => MetaData?.EntityDescription ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (MetaData is {} metaData)
|
||||
metaData.EntityDescription = value;
|
||||
if (MetaData is { } metaData)
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
entManager.System<MetaDataSystem>().SetEntityDescription(this, value, metaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <remarks>
|
||||
/// Base component for the ECS system.
|
||||
/// All discoverable implementations of IComponent must override the <see cref="Name" />.
|
||||
/// Instances are dynamically instantiated by a <c>ComponentFactory</c>, and will have their IoC Dependencies resolved.
|
||||
/// </remarks>
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
@@ -17,7 +16,10 @@ namespace Robust.Shared.GameObjects
|
||||
/// The current lifetime stage of this component. You can use this to check
|
||||
/// if the component is initialized or being deleted.
|
||||
/// </summary>
|
||||
ComponentLifeStage LifeStage { get; }
|
||||
ComponentLifeStage LifeStage { get; [Obsolete("Do not use from content")] set; }
|
||||
|
||||
[Obsolete("Do not use from content")]
|
||||
bool Networked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this component should be synchronized with clients when modified.
|
||||
@@ -28,11 +30,25 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
bool NetSyncEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, and if this is a networked component, then component data will only be sent to players if their
|
||||
/// controlled entity is the owner of this component. This is less performance intensive than <see cref="SessionSpecific"/>.
|
||||
/// </summary>
|
||||
bool SendOnlyToOwner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, and if this is a networked component, then this component will cause <see
|
||||
/// cref="ComponentGetStateAttemptEvent"/> events to be raised to check whether a given player should
|
||||
/// receive this component's state.
|
||||
/// </summary>
|
||||
bool SessionSpecific { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity that this component is attached to.
|
||||
/// </summary>
|
||||
/// <seealso cref="EntityQueryEnumerator{TComp1}"/>
|
||||
EntityUid Owner { get; }
|
||||
[Obsolete("Update your API to allow accessing Owner through other means")]
|
||||
EntityUid Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Component has been (or is currently being) initialized.
|
||||
@@ -57,11 +73,17 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// This is the tick the component was created.
|
||||
/// </summary>
|
||||
GameTick CreationTick { get; }
|
||||
GameTick CreationTick { get; [Obsolete("Do not use from content")] set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is the last game tick Dirty() was called.
|
||||
/// </summary>
|
||||
GameTick LastModifiedTick { get; }
|
||||
GameTick LastModifiedTick { get; [Obsolete("Do not use from content")] set; }
|
||||
|
||||
[Obsolete("Do not use from content")]
|
||||
void ClearTicks();
|
||||
|
||||
[Obsolete("Do not use from content")]
|
||||
void ClearCreationTick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Gets the number of a specific component.
|
||||
/// </summary>
|
||||
public int Count<T>() where T : Component;
|
||||
public int Count<T>() where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of a specific component.
|
||||
@@ -44,12 +44,12 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Concrete component type to add.</typeparam>
|
||||
/// <returns>The newly added component.</returns>
|
||||
T AddComponent<T>(EntityUid uid) where T : Component, new();
|
||||
T AddComponent<T>(EntityUid uid) where T : IComponent, new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Component with a given network id to an entity.
|
||||
/// </summary>
|
||||
Component AddComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null);
|
||||
IComponent AddComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds an uninitialized Component type to an entity.
|
||||
@@ -61,7 +61,8 @@ namespace Robust.Shared.GameObjects
|
||||
/// <typeparam name="T">Concrete component type to add.</typeparam>
|
||||
/// <param name="uid">Entity being modified.</param>
|
||||
/// <returns>Component initialization handle. When you are done setting up the component, make sure to dispose this.</returns>
|
||||
EntityManager.CompInitializeHandle<T> AddComponentUninitialized<T>(EntityUid uid) where T : Component, new();
|
||||
[Obsolete]
|
||||
EntityManager.CompInitializeHandle<T> AddComponentUninitialized<T>(EntityUid uid) where T : IComponent, new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Component to an entity. If the entity is already Initialized, the component will
|
||||
@@ -70,7 +71,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="uid">Entity being modified.</param>
|
||||
/// <param name="component">Component to add.</param>
|
||||
/// <param name="overwrite">Should it overwrite existing components?</param>
|
||||
void AddComponent<T>(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : Component;
|
||||
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,
|
||||
@@ -213,7 +214,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="uid">Entity to modify.</param>
|
||||
/// <typeparam name="T">Component to add.</typeparam>
|
||||
/// <returns>The component in question</returns>
|
||||
T EnsureComponent<T>(EntityUid uid) where T : Component, new();
|
||||
T EnsureComponent<T>(EntityUid uid) where T : IComponent, new();
|
||||
|
||||
/// <summary>
|
||||
/// This method will always return a component for a certain entity, adding it if it's not there already.
|
||||
@@ -221,8 +222,8 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="uid">Entity to modify.</param>
|
||||
/// <param name="component">The output component after being ensured.</param>
|
||||
/// <typeparam name="T">Component to add.</typeparam>
|
||||
/// <returns>The component in question</returns>
|
||||
bool EnsureComponent<T>(EntityUid uid, out T component) where T : Component, new();
|
||||
/// <returns>True if the component already existed</returns>
|
||||
bool EnsureComponent<T>(EntityUid uid, out T component) where T : IComponent, new();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
@@ -333,9 +334,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Returns a cached struct enumerator with the specified component.
|
||||
/// </summary>
|
||||
EntityQuery<TComp1> GetEntityQuery<TComp1>() where TComp1 : Component;
|
||||
EntityQuery<TComp1> GetEntityQuery<TComp1>() where TComp1 : IComponent;
|
||||
|
||||
EntityQuery<Component> GetEntityQuery(Type type);
|
||||
EntityQuery<IComponent> GetEntityQuery(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Returns ALL component type instances on an entity. A single component instance
|
||||
@@ -397,49 +398,49 @@ namespace Robust.Shared.GameObjects
|
||||
/// Returns all instances of a component in an array.
|
||||
/// Use sparingly.
|
||||
/// </summary>
|
||||
(EntityUid Uid, T Component)[] AllComponents<T>() where T : Component;
|
||||
(EntityUid Uid, T Component)[] AllComponents<T>() where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Returns all instances of a component in a List.
|
||||
/// Use sparingly.
|
||||
/// </summary>
|
||||
List<(EntityUid Uid, T Component)> AllComponentsList<T>() where T : Component;
|
||||
List<(EntityUid Uid, T Component)> AllComponentsList<T>() where T : IComponent;
|
||||
|
||||
AllEntityQueryEnumerator<TComp1> AllEntityQueryEnumerator<TComp1>()
|
||||
where TComp1 : Component;
|
||||
where TComp1 : IComponent;
|
||||
|
||||
AllEntityQueryEnumerator<TComp1, TComp2> AllEntityQueryEnumerator<TComp1, TComp2>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component;
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent;
|
||||
|
||||
AllEntityQueryEnumerator<TComp1, TComp2, TComp3> AllEntityQueryEnumerator<TComp1, TComp2, TComp3>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component;
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent;
|
||||
|
||||
AllEntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> AllEntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp4 : Component;
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
where TComp4 : IComponent;
|
||||
|
||||
EntityQueryEnumerator<TComp1> EntityQueryEnumerator<TComp1>()
|
||||
where TComp1 : Component;
|
||||
where TComp1 : IComponent;
|
||||
|
||||
EntityQueryEnumerator<TComp1, TComp2> EntityQueryEnumerator<TComp1, TComp2>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component;
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent;
|
||||
|
||||
EntityQueryEnumerator<TComp1, TComp2, TComp3> EntityQueryEnumerator<TComp1, TComp2, TComp3>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component;
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent;
|
||||
|
||||
EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>()
|
||||
where TComp1 : Component
|
||||
where TComp2 : Component
|
||||
where TComp3 : Component
|
||||
where TComp4 : Component;
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
where TComp3 : IComponent
|
||||
where TComp4 : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Returns ALL component instances of a specified type.
|
||||
@@ -490,7 +491,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="type">A trait or component type to check for.</param>
|
||||
/// <param name="includePaused"></param>
|
||||
/// <returns>All components that are the specified type.</returns>
|
||||
IEnumerable<(EntityUid Uid, Component Component)> GetAllComponents(Type type, bool includePaused = false);
|
||||
IEnumerable<(EntityUid Uid, IComponent Component)> GetAllComponents(Type type, bool includePaused = false);
|
||||
|
||||
/// <summary>
|
||||
/// Culls all components from the collection that are marked as deleted. This needs to be called often.
|
||||
|
||||
@@ -83,9 +83,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public void DirtyEntity(EntityUid uid, MetaDataComponent? metadata = null);
|
||||
|
||||
public void Dirty(Component component, MetaDataComponent? metadata = null);
|
||||
[Obsolete("use override with an EntityUid")]
|
||||
public void Dirty(IComponent component, MetaDataComponent? metadata = null);
|
||||
|
||||
public void Dirty(EntityUid uid, Component component, MetaDataComponent? meta = null);
|
||||
public void Dirty(EntityUid uid, IComponent component, MetaDataComponent? meta = null);
|
||||
|
||||
public void Dirty<T>(Entity<T> ent, MetaDataComponent? meta = null) where T : IComponent;
|
||||
|
||||
public void QueueDeleteEntity(EntityUid? uid);
|
||||
|
||||
@@ -127,7 +130,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
/// </summary>
|
||||
EntityStringRepresentation ToPrettyString(EntityUid uid);
|
||||
EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
@@ -138,7 +141,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
/// </summary>
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
EntityStringRepresentation? ToPrettyString(EntityUid? uid);
|
||||
EntityStringRepresentation? ToPrettyString(EntityUid? uid, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
|
||||
@@ -184,8 +184,11 @@ public readonly struct NetEntity : IEquatable<NetEntity>, IComparable<NetEntity>
|
||||
get => MetaData?.EntityName ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (MetaData is {} metaData)
|
||||
metaData.EntityName = value;
|
||||
if (MetaData is { } metaData)
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
entManager.System<MetaDataSystem>().SetEntityName(entManager.GetEntity(this), value, metaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +198,11 @@ public readonly struct NetEntity : IEquatable<NetEntity>, IComparable<NetEntity>
|
||||
get => MetaData?.EntityDescription ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (MetaData is {} metaData)
|
||||
metaData.EntityDescription = value;
|
||||
if (MetaData is { } metaData)
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
entManager.System<MetaDataSystem>().SetEntityDescription(entManager.GetEntity(this), value, metaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -273,9 +272,14 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return new HashSet<EntityUid>();
|
||||
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
GetEntitiesIntersecting(mapId, worldAABB, intersecting, flags);
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
public void GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, HashSet<EntityUid> intersecting, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
// Get grid entities
|
||||
var state = (this, _map, intersecting, worldAABB, _transform, flags);
|
||||
@@ -309,8 +313,6 @@ public sealed partial class EntityLookupSystem
|
||||
var localAABB = _transform.GetInvWorldMatrix(mapUid).TransformBox(worldAABB);
|
||||
AddEntitiesIntersecting(mapUid, intersecting, localAABB, flags);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -546,16 +548,23 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesInRange(MapId mapId, Vector2 worldPos, float range,
|
||||
LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var entities = new HashSet<EntityUid>();
|
||||
GetEntitiesInRange(mapId, worldPos, range, entities, flags);
|
||||
return entities;
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange(MapId mapId, Vector2 worldPos, float range, HashSet<EntityUid> entities, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace)
|
||||
return new HashSet<EntityUid>();
|
||||
return;
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
return GetEntitiesIntersecting(mapId, worldAABB, flags);
|
||||
GetEntitiesIntersecting(mapId, worldAABB, entities, flags);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -21,7 +20,19 @@ public sealed partial class EntityLookupSystem
|
||||
HashSet<T> intersecting,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query) where T : Component
|
||||
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,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query) where T : IComponent
|
||||
{
|
||||
var lookup = _broadQuery.GetComponent(lookupUid);
|
||||
var invMatrix = _transform.GetInvWorldMatrix(lookupUid);
|
||||
@@ -30,48 +41,48 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
if ((flags & LookupFlags.Dynamic) != 0x0)
|
||||
{
|
||||
lookup.DynamicTree.QueryAabb(ref state, static (ref (HashSet<T> intersecting, EntityQuery<T> query) tuple, in FixtureProxy value) =>
|
||||
lookup.DynamicTree.QueryAabb(ref state, static (ref (HashSet<Entity<T>> intersecting, EntityQuery<T> query) tuple, in FixtureProxy value) =>
|
||||
{
|
||||
if (!tuple.query.TryGetComponent(value.Entity, out var comp))
|
||||
return true;
|
||||
|
||||
tuple.intersecting.Add(comp);
|
||||
tuple.intersecting.Add((value.Entity, comp));
|
||||
return true;
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
|
||||
if ((flags & (LookupFlags.Static)) != 0x0)
|
||||
{
|
||||
lookup.StaticTree.QueryAabb(ref state, static (ref (HashSet<T> intersecting, EntityQuery<T> query) tuple, in FixtureProxy value) =>
|
||||
lookup.StaticTree.QueryAabb(ref state, static (ref (HashSet<Entity<T>> intersecting, EntityQuery<T> query) tuple, in FixtureProxy value) =>
|
||||
{
|
||||
if (!tuple.query.TryGetComponent(value.Entity, out var comp))
|
||||
return true;
|
||||
|
||||
tuple.intersecting.Add(comp);
|
||||
tuple.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 (HashSet<T> intersecting, EntityQuery<T> query) tuple, in EntityUid value) =>
|
||||
lookup.StaticSundriesTree.QueryAabb(ref state, static (ref (HashSet<Entity<T>> intersecting, EntityQuery<T> query) tuple, in EntityUid value) =>
|
||||
{
|
||||
if (!tuple.query.TryGetComponent(value, out var comp))
|
||||
return true;
|
||||
|
||||
tuple.intersecting.Add(comp);
|
||||
tuple.intersecting.Add((value, comp));
|
||||
return true;
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
|
||||
if ((flags & LookupFlags.Sundries) != 0x0)
|
||||
{
|
||||
lookup.SundriesTree.QueryAabb(ref state, static (ref (HashSet<T> intersecting, EntityQuery<T> query) tuple, in EntityUid value) =>
|
||||
lookup.SundriesTree.QueryAabb(ref state, static (ref (HashSet<Entity<T>> intersecting, EntityQuery<T> query) tuple, in EntityUid value) =>
|
||||
{
|
||||
if (!tuple.query.TryGetComponent(value, out var comp))
|
||||
return true;
|
||||
|
||||
tuple.intersecting.Add(comp);
|
||||
tuple.intersecting.Add((value, comp));
|
||||
return true;
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
@@ -82,7 +93,7 @@ public sealed partial class EntityLookupSystem
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query,
|
||||
EntityUid? ignored = null) where T : Component
|
||||
EntityUid? ignored = null) where T : IComponent
|
||||
{
|
||||
var lookup = _broadQuery.GetComponent(lookupUid);
|
||||
var invMatrix = _transform.GetInvWorldMatrix(lookupUid);
|
||||
@@ -153,7 +164,7 @@ public sealed partial class EntityLookupSystem
|
||||
return state.found;
|
||||
}
|
||||
|
||||
private void RecursiveAdd<T>(EntityUid uid, ref ValueList<T> toAdd, EntityQuery<T> query) where T : Component
|
||||
private void RecursiveAdd<T>(EntityUid uid, ref ValueList<T> toAdd, EntityQuery<T> query) where T : IComponent
|
||||
{
|
||||
var childEnumerator = _xformQuery.GetComponent(uid).ChildEnumerator;
|
||||
|
||||
@@ -168,15 +179,38 @@ public sealed partial class EntityLookupSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void AddContained<T>(HashSet<T> intersecting, LookupFlags flags, EntityQuery<T> query) where T : Component
|
||||
private void RecursiveAdd<T>(EntityUid uid, ref ValueList<Entity<T>> toAdd, EntityQuery<T> query) where T : IComponent
|
||||
{
|
||||
var childEnumerator = _xformQuery.GetComponent(uid).ChildEnumerator;
|
||||
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
if (query.TryGetComponent(child.Value, out var compies))
|
||||
{
|
||||
toAdd.Add((child.Value, compies));
|
||||
}
|
||||
|
||||
RecursiveAdd(child.Value, ref toAdd, query);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private void AddContained<T>(HashSet<T> intersecting, LookupFlags flags, EntityQuery<T> query) where T : IComponent
|
||||
{
|
||||
var intersectingEntities = new HashSet<Entity<T>>();
|
||||
AddContained(intersectingEntities, flags, query);
|
||||
intersecting.UnionWith(intersectingEntities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
private void AddContained<T>(HashSet<Entity<T>> intersecting, LookupFlags flags, EntityQuery<T> query) where T : IComponent
|
||||
{
|
||||
if ((flags & LookupFlags.Contained) == 0x0) return;
|
||||
|
||||
var toAdd = new ValueList<T>();
|
||||
var toAdd = new ValueList<Entity<T>>();
|
||||
|
||||
foreach (var comp in intersecting)
|
||||
{
|
||||
if (!_containerQuery.TryGetComponent(comp.Owner, out var conManager)) continue;
|
||||
if (!_containerQuery.TryGetComponent(comp, out var conManager)) continue;
|
||||
|
||||
foreach (var con in conManager.GetAllContainers())
|
||||
{
|
||||
@@ -184,7 +218,7 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
if (query.TryGetComponent(contained, out var compies))
|
||||
{
|
||||
toAdd.Add(compies);
|
||||
toAdd.Add((contained, compies));
|
||||
}
|
||||
|
||||
RecursiveAdd(contained, ref toAdd, query);
|
||||
@@ -209,7 +243,7 @@ public sealed partial class EntityLookupSystem
|
||||
/// <summary>
|
||||
/// Should we just iterate every component and check position or do bounds checks.
|
||||
/// </summary>
|
||||
private bool UseBoundsQuery<T>(float area) where T : Component
|
||||
private bool UseBoundsQuery<T>(float area) where T : IComponent
|
||||
{
|
||||
// If the component has a low count we'll just do an estimate if it's faster to iterate every comp directly
|
||||
// Might be useful to have some way to expose this to content?
|
||||
@@ -224,7 +258,7 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, EntityUid? ignored = null, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
DebugTools.Assert(typeof(Component).IsAssignableFrom(type));
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
if (mapId == MapId.Nullspace) return false;
|
||||
|
||||
if (!UseBoundsQuery(type, worldAABB.Height * worldAABB.Width))
|
||||
@@ -256,7 +290,7 @@ public sealed partial class EntityLookupSystem
|
||||
(EntityLookupSystem system,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<Component> query,
|
||||
EntityQuery<IComponent> query,
|
||||
EntityUid? ignored,
|
||||
bool found) tuple) =>
|
||||
{
|
||||
@@ -274,13 +308,20 @@ public sealed partial class EntityLookupSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
public HashSet<Component> GetComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags)
|
||||
[Obsolete]
|
||||
public HashSet<IComponent> GetComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
DebugTools.Assert(typeof(Component).IsAssignableFrom(type));
|
||||
if (mapId == MapId.Nullspace)
|
||||
return new HashSet<Component>();
|
||||
var intersectingEntities = new HashSet<Entity<IComponent>>();
|
||||
GetEntitiesIntersecting(type, mapId, worldAABB, intersectingEntities, flags);
|
||||
var intersecting = new HashSet<IComponent>(intersectingEntities.Select(e => e.Comp));
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
var intersecting = new HashSet<Component>();
|
||||
public void GetEntitiesIntersecting(Type type, MapId mapId, Box2 worldAABB, HashSet<Entity<IComponent>> intersecting, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
if (!UseBoundsQuery(type, worldAABB.Height * worldAABB.Width))
|
||||
{
|
||||
@@ -296,7 +337,7 @@ public sealed partial class EntityLookupSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
intersecting.Add(comp);
|
||||
intersecting.Add((uid, comp));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -311,36 +352,40 @@ public sealed partial class EntityLookupSystem
|
||||
ref (EntityLookupSystem system,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<Component> query,
|
||||
HashSet<Component> intersecting) tuple) =>
|
||||
EntityQuery<IComponent> query,
|
||||
HashSet<Entity<IComponent>> intersecting) tuple) =>
|
||||
{
|
||||
tuple.system.AddComponentsIntersecting(uid, tuple.intersecting, tuple.worldAABB, tuple.flags, tuple.query);
|
||||
tuple.system.AddEntitiesIntersecting(uid, tuple.intersecting, tuple.worldAABB, tuple.flags, tuple.query);
|
||||
return true;
|
||||
}, (flags & LookupFlags.Approximate) != 0x0);
|
||||
|
||||
// Get map entities
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
AddComponentsIntersecting(mapUid, intersecting, worldAABB, flags, query);
|
||||
AddEntitiesIntersecting(mapUid, intersecting, worldAABB, flags, query);
|
||||
AddContained(intersecting, flags, query);
|
||||
}
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
public HashSet<T> GetComponentsIntersecting<T>(MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) where T : Component
|
||||
[Obsolete]
|
||||
public HashSet<T> GetComponentsIntersecting<T>(MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return new HashSet<T>();
|
||||
var intersectingEntities = new HashSet<Entity<T>>();
|
||||
GetEntitiesIntersecting(mapId, worldAABB, intersectingEntities, flags);
|
||||
return new HashSet<T>(intersectingEntities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
var intersecting = new HashSet<T>();
|
||||
public void GetEntitiesIntersecting<T>(MapId mapId, Box2 worldAABB, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
if (!UseBoundsQuery<T>(worldAABB.Height * worldAABB.Width))
|
||||
{
|
||||
var query = AllEntityQuery<T, TransformComponent>();
|
||||
|
||||
while (query.MoveNext(out var comp, out var xform))
|
||||
while (query.MoveNext(out var uid, out var comp, out var xform))
|
||||
{
|
||||
if (xform.MapID != mapId || !worldAABB.Contains(_transform.GetWorldPosition(xform))) continue;
|
||||
intersecting.Add(comp);
|
||||
entities.Add((uid, comp));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -348,60 +393,93 @@ public sealed partial class EntityLookupSystem
|
||||
var query = GetEntityQuery<T>();
|
||||
|
||||
// Get grid entities
|
||||
var state = (this, worldAABB, flags, query, intersecting);
|
||||
var state = (this, worldAABB, flags, query, entities);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref state,
|
||||
static (EntityUid uid, MapGridComponent grid,
|
||||
ref (EntityLookupSystem system,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query,
|
||||
HashSet<T> intersecting) tuple) =>
|
||||
{
|
||||
tuple.system.AddComponentsIntersecting(uid, tuple.intersecting, tuple.worldAABB, tuple.flags, tuple.query);
|
||||
return true;
|
||||
}, (flags & LookupFlags.Approximate) != 0x0);
|
||||
ref (EntityLookupSystem system,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<T> query,
|
||||
HashSet<Entity<T>> intersecting) tuple) =>
|
||||
{
|
||||
tuple.system.AddEntitiesIntersecting(uid, tuple.intersecting, tuple.worldAABB, tuple.flags, tuple.query);
|
||||
return true;
|
||||
}, (flags & LookupFlags.Approximate) != 0x0);
|
||||
|
||||
// Get map entities
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
AddComponentsIntersecting(mapUid, intersecting, worldAABB, flags, query);
|
||||
AddContained(intersecting, flags, query);
|
||||
AddEntitiesIntersecting(mapUid, entities, worldAABB, flags, query);
|
||||
AddContained(entities, flags, query);
|
||||
}
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EntityCoordinates
|
||||
|
||||
public HashSet<T> GetComponentsInRange<T>(EntityCoordinates coordinates, float range) where T : Component
|
||||
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);
|
||||
GetEntitiesInRange(mapPos, range, entities);
|
||||
}
|
||||
|
||||
public HashSet<Entity<T>> GetEntitiesInRange<T>(EntityCoordinates coordinates, float range) where T : IComponent
|
||||
{
|
||||
var entities = new HashSet<Entity<T>>();
|
||||
GetEntitiesInRange(coordinates, range, entities);
|
||||
return entities;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MapCoordinates
|
||||
|
||||
public HashSet<Component> GetComponentsInRange(Type type, MapCoordinates coordinates, float range)
|
||||
[Obsolete]
|
||||
public HashSet<IComponent> GetComponentsInRange(Type type, MapCoordinates coordinates, float range)
|
||||
{
|
||||
DebugTools.Assert(typeof(Component).IsAssignableFrom(type));
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
return GetComponentsInRange(type, coordinates.MapId, coordinates.Position, range);
|
||||
}
|
||||
|
||||
public HashSet<T> GetComponentsInRange<T>(MapCoordinates coordinates, float range) where T : Component
|
||||
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.MapId, coordinates.Position, range, entities);
|
||||
return entities;
|
||||
}
|
||||
|
||||
public HashSet<T> GetComponentsInRange<T>(MapCoordinates coordinates, float range) where T : IComponent
|
||||
{
|
||||
return GetComponentsInRange<T>(coordinates.MapId, coordinates.Position, range);
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange<T>(MapCoordinates coordinates, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
{
|
||||
GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities);
|
||||
}
|
||||
|
||||
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);
|
||||
return entities;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MapId
|
||||
|
||||
public bool AnyComponentsInRange(Type type, MapId mapId, Vector2 worldPos, float range)
|
||||
{
|
||||
DebugTools.Assert(typeof(Component).IsAssignableFrom(type));
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type));
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return false;
|
||||
@@ -413,31 +491,47 @@ public sealed partial class EntityLookupSystem
|
||||
return AnyComponentsIntersecting(type, mapId, worldAABB);
|
||||
}
|
||||
|
||||
public HashSet<Component> GetComponentsInRange(Type type, MapId mapId, Vector2 worldPos, float range)
|
||||
[Obsolete]
|
||||
public HashSet<IComponent> GetComponentsInRange(Type type, MapId mapId, Vector2 worldPos, float range)
|
||||
{
|
||||
DebugTools.Assert(typeof(Component).IsAssignableFrom(type));
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return new HashSet<Component>();
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
return GetComponentsIntersecting(type, mapId, worldAABB);
|
||||
var entities = new HashSet<Entity<IComponent>>();
|
||||
GetEntitiesInRange(type, mapId, worldPos, range, entities);
|
||||
return new HashSet<IComponent>(entities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
public HashSet<T> GetComponentsInRange<T>(MapId mapId, Vector2 worldPos, float range) where T : Component
|
||||
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 new HashSet<T>();
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
return GetComponentsIntersecting<T>(mapId, worldAABB);
|
||||
GetEntitiesIntersecting(type, mapId, worldAABB, entities);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public HashSet<T> GetComponentsInRange<T>(MapId mapId, Vector2 worldPos, float range) where T : IComponent
|
||||
{
|
||||
var entities = new HashSet<Entity<T>>();
|
||||
GetEntitiesInRange(mapId, worldPos, range, entities);
|
||||
return new HashSet<T>(entities.Select(e => e.Comp));
|
||||
}
|
||||
|
||||
public void GetEntitiesInRange<T>(MapId mapId, Vector2 worldPos, float range, HashSet<Entity<T>> entities) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec);
|
||||
GetEntitiesIntersecting(mapId, worldAABB, entities);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -31,14 +31,15 @@ internal sealed class PrototypeReloadSystem : EntitySystem
|
||||
if (!eventArgs.ByType.TryGetValue(typeof(EntityPrototype), out var set))
|
||||
return;
|
||||
|
||||
foreach (var metadata in EntityQuery<MetaDataComponent>())
|
||||
var query = EntityQueryEnumerator<MetaDataComponent>();
|
||||
while (query.MoveNext(out var uid, out var metadata))
|
||||
{
|
||||
var id = metadata.EntityPrototype?.ID;
|
||||
if (id == null || !set.Modified.ContainsKey(id))
|
||||
continue;
|
||||
|
||||
var proto = _prototypes.Index<EntityPrototype>(id);
|
||||
UpdateEntity(metadata.Owner, metadata, proto);
|
||||
UpdateEntity(uid, metadata, proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +78,7 @@ internal sealed class PrototypeReloadSystem : EntitySystem
|
||||
.Except(oldPrototypeComponents))
|
||||
{
|
||||
var data = newPrototype.Components[name];
|
||||
var component = (Component)_componentFactory.GetComponent(name);
|
||||
component.Owner = entity;
|
||||
var component = _componentFactory.GetComponent(name);
|
||||
EntityManager.AddComponent(entity, component);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Enumerators;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -296,7 +296,7 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
MapManager.SuppressOnTileChanged = false;
|
||||
if (modified.Count != 0)
|
||||
RaiseLocalEvent(uid, new GridModifiedEvent(component, modified), true);
|
||||
RaiseLocalEvent(uid, new GridModifiedEvent(uid, component, modified), true);
|
||||
}
|
||||
|
||||
private void OnGridGetState(EntityUid uid, MapGridComponent component, ref ComponentGetState args)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user