ConstructionGL2 Part 1: ECSification, events and steps. (#5017)
- Completely rewrited the `ConstructionComponent` logic to be ECS, *without* looking too much at the original implementation.
- The original implementation was dirty and unmaintainable, whereas this new implementation is much cleaner, well-organized and maintainable. I've made sure to leave many comments around, explaining what everything does.
- Construction now has a framework for handling events other than `InteractUsing`.
- This means that you can now have CGL steps for things other than inserting items, using tools...
- Construction no longer uses `async` everywhere for `DoAfter`s. Instead it uses events.
- Construction event handling occurs in the `ConstructionSystem` update tick, instead of on event handlers.
- This ensures we can delete/modify entities without worrying about "collection modified while enumerating" exceptions.
- This also means the construction update tick is where all the fun happens, meaning it'll show up on our metrics and give us an idea of how expensive it is/how much tick time is spent in construction.
- `IGraphCondition` and `IGraphAction` have been refactored to take in `EntityUid`, `IEntityManager`, and to not be async.
- Removes nested steps, as they made maintainability significantly worse, and nothing used them yet.
- This fixes #4892 and fixes #4857
Please note, this leaves many things unchanged, as my idea is to split this into multiple PRs. Some unchanged things:
- Initial construction code is the same. In the future, it'll probably use dummy entities.
- Client-side guided steps are the same. In the future, the server will generate the guided steps and send them to clients as needed, caching these in both the server and client to save cycles and bandwidth.
- No new construction graph steps... Yet! 👀
This commit is contained in:
committed by
GitHub
parent
d70470b99b
commit
189a5c7847
@@ -17,9 +17,10 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataField("value")]
|
||||
public bool Value { get; private set; } = true;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entity.TryGetComponent(out AirlockComponent? airlock)) return true;
|
||||
if (!entityManager.TryGetComponent(uid, out AirlockComponent? airlock))
|
||||
return true;
|
||||
|
||||
return airlock.BoltsDown == Value;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataField("conditions")]
|
||||
public IGraphCondition[] Conditions { get; } = Array.Empty<IGraphCondition>();
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
foreach (var condition in Conditions)
|
||||
{
|
||||
if (!await condition.Condition(entity))
|
||||
if (!condition.Condition(uid, entityManager))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,9 @@ namespace Content.Server.Construction.Conditions
|
||||
{
|
||||
[DataField("value")] public bool Value { get; private set; } = true;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (entity.Deleted)
|
||||
return false;
|
||||
|
||||
if (!entity.TryGetComponent<WiresComponent>(out var wires))
|
||||
if (!entityManager.TryGetComponent(uid, out WiresComponent? wires))
|
||||
return true;
|
||||
|
||||
foreach (var wire in wires.WiresList)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -14,11 +13,11 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataField("conditions")]
|
||||
public IGraphCondition[] Conditions { get; } = Array.Empty<IGraphCondition>();
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
foreach (var condition in Conditions)
|
||||
{
|
||||
if (await condition.Condition(entity))
|
||||
if (condition.Condition(uid, entityManager))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,16 +15,8 @@ namespace Content.Server.Construction.Conditions
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public class ComponentInTile : IGraphCondition, ISerializationHooks
|
||||
public class ComponentInTile : IGraphCondition
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, any entity on the tile must have the component.
|
||||
/// If false, no entity on the tile must have the component.
|
||||
@@ -38,14 +30,15 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataField("component")]
|
||||
public string Component { get; private set; } = string.Empty;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Component)) return false;
|
||||
|
||||
var type = _componentFactory.GetRegistration(Component).Type;
|
||||
var type = IoCManager.Resolve<IComponentFactory>().GetRegistration(Component).Type;
|
||||
|
||||
var indices = entity.Transform.Coordinates.ToVector2i(entity.EntityManager, _mapManager);
|
||||
var entities = indices.GetEntitiesInTile(entity.Transform.GridID, LookupFlags.Approximate | LookupFlags.IncludeAnchored, IoCManager.Resolve<IEntityLookup>());
|
||||
var transform = entityManager.GetComponent<ITransformComponent>(uid);
|
||||
var indices = transform.Coordinates.ToVector2i(entityManager, IoCManager.Resolve<IMapManager>());
|
||||
var entities = indices.GetEntitiesInTile(transform.GridID, LookupFlags.Approximate | LookupFlags.IncludeAnchored, IoCManager.Resolve<IEntityLookup>());
|
||||
|
||||
foreach (var ent in entities)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.Examine;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -16,10 +17,11 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataField("container")] public string Container { get; private set; } = string.Empty;
|
||||
[DataField("text")] public string Text { get; private set; } = string.Empty;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager) ||
|
||||
!containerManager.TryGetContainer(Container, out var container)) return true;
|
||||
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<ContainerSystem>();
|
||||
if (!containerSystem.TryGetContainer(uid, Container, out var container))
|
||||
return false;
|
||||
|
||||
return container.ContainedEntities.Count == 0;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Threading.Tasks;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.Examine;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -16,10 +17,11 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataField("container")] public string Container { get; private set; } = string.Empty;
|
||||
[DataField("text")] public string Text { get; private set; } = string.Empty;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager) ||
|
||||
!containerManager.TryGetContainer(Container, out var container)) return false;
|
||||
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<ContainerSystem>();
|
||||
if (!containerSystem.TryGetContainer(uid, Container, out var container))
|
||||
return false;
|
||||
|
||||
return container.ContainedEntities.Count != 0;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Doors.Components;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.Examine;
|
||||
@@ -6,8 +5,6 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using static Content.Shared.Doors.SharedDoorComponent;
|
||||
|
||||
namespace Content.Server.Construction.Conditions
|
||||
{
|
||||
@@ -18,9 +15,10 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataField("welded")]
|
||||
public bool Welded { get; private set; } = true;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ServerDoorComponent? doorComponent)) return false;
|
||||
if (!entityManager.TryGetComponent(uid, out ServerDoorComponent? doorComponent))
|
||||
return false;
|
||||
|
||||
return doorComponent.IsWeldedShut == Welded;
|
||||
}
|
||||
|
||||
@@ -14,11 +14,10 @@ namespace Content.Server.Construction.Conditions
|
||||
{
|
||||
[DataField("anchored")] public bool Anchored { get; private set; } = true;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entity.TryGetComponent(out IPhysBody? physics)) return false;
|
||||
|
||||
return (physics.BodyType == BodyType.Static && Anchored) || (physics.BodyType != BodyType.Static && !Anchored);
|
||||
var transform = entityManager.GetComponent<ITransformComponent>(uid);
|
||||
return transform.Anchored && Anchored || !transform.Anchored && !Anchored;
|
||||
}
|
||||
|
||||
public bool DoExamine(ExaminedEvent args)
|
||||
|
||||
@@ -17,9 +17,9 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataDefinition]
|
||||
public class MachineFrameComplete : IGraphCondition
|
||||
{
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (entity.Deleted || !entity.TryGetComponent<MachineFrameComponent>(out var machineFrame))
|
||||
if (!entityManager.TryGetComponent(uid, out MachineFrameComponent? machineFrame))
|
||||
return false;
|
||||
|
||||
return machineFrame.IsComplete;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Toilet;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.Examine;
|
||||
@@ -6,7 +5,6 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Construction.Conditions
|
||||
{
|
||||
@@ -14,9 +12,11 @@ namespace Content.Server.Construction.Conditions
|
||||
[DataDefinition]
|
||||
public class ToiletLidClosed : IGraphCondition
|
||||
{
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ToiletComponent? toilet)) return false;
|
||||
if (!entityManager.TryGetComponent(uid, out ToiletComponent? toilet))
|
||||
return false;
|
||||
|
||||
return !toilet.LidOpen;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@ namespace Content.Server.Construction.Conditions
|
||||
{
|
||||
[DataField("open")] public bool Open { get; private set; } = true;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entity.TryGetComponent(out WiresComponent? wires)) return false;
|
||||
if (!entityManager.TryGetComponent(uid, out WiresComponent? wires))
|
||||
return false;
|
||||
|
||||
return wires.IsPanelOpen == Open;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user