Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix conveyor mispredicts #28157

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Content.Server/Physics/Controllers/ConveyorController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ private void OnConveyorShutdown(EntityUid uid, ConveyorComponent component, Comp
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
return;

RemComp<ActiveConveyorComponent>(uid);

if (!TryComp<PhysicsComponent>(uid, out var physics))
return;

Expand Down
12 changes: 0 additions & 12 deletions Content.Shared/Conveyor/ActiveConveyorComponent.cs

This file was deleted.

13 changes: 13 additions & 0 deletions Content.Shared/Conveyor/ConveyedComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Robust.Shared.GameStates;

namespace Content.Shared.Conveyor;

/// <summary>
/// Indicates this entity is currently being conveyed.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ConveyedComponent : Component
{
[ViewVariables, AutoNetworkedField]
public List<EntityUid> Colliding = new();
}
3 changes: 0 additions & 3 deletions Content.Shared/Conveyor/ConveyorComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ public sealed partial class ConveyorComponent : Component

[DataField]
public ProtoId<SinkPortPrototype> OffPort = "Off";

[ViewVariables]
public readonly HashSet<EntityUid> Intersecting = new();
}

[Serializable, NetSerializable]
Expand Down
168 changes: 100 additions & 68 deletions Content.Shared/Physics/Controllers/SharedConveyorController.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
using System.Numerics;
using Content.Shared.Conveyor;
using Content.Shared.Gravity;
using Content.Shared.Magic;
using Content.Shared.Movement.Systems;
using Robust.Shared.Collections;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Utility;

namespace Content.Shared.Physics.Controllers;

public abstract class SharedConveyorController : VirtualController
{
[Dependency] protected readonly IMapManager MapManager = default!;
[Dependency] protected readonly EntityLookupSystem Lookup = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] protected readonly SharedPhysicsSystem Physics = default!;
[Dependency] private readonly SharedGravitySystem _gravity = default!;

protected const string ConveyorFixture = "conveyor";

private static readonly Vector2 _expansion = new Vector2(0.1f, 0.1f);
private EntityQuery<MapGridComponent> _gridQuery;
private EntityQuery<TransformComponent> _xformQuery;

private ValueList<EntityUid> _ents = new();
private HashSet<Entity<ConveyorComponent>> _conveyors = new();

public override void Initialize()
{
_gridQuery = GetEntityQuery<MapGridComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();

UpdatesAfter.Add(typeof(SharedMoverController));

SubscribeLocalEvent<ConveyorComponent, StartCollideEvent>(OnConveyorStartCollide);
Expand All @@ -37,74 +48,125 @@ private void OnConveyorStartCollide(EntityUid uid, ConveyorComponent component,
{
var otherUid = args.OtherEntity;

if (args.OtherBody.BodyType == BodyType.Static || component.State == ConveyorState.Off)
if (!args.OtherFixture.Hard || args.OtherBody.BodyType == BodyType.Static || component.State == ConveyorState.Off)
return;

var conveyed = EnsureComp<ConveyedComponent>(otherUid);

if (conveyed.Colliding.Contains(uid))
return;

component.Intersecting.Add(otherUid);
EnsureComp<ActiveConveyorComponent>(uid);
conveyed.Colliding.Add(uid);
Dirty(otherUid, conveyed);
}

private void OnConveyorEndCollide(EntityUid uid, ConveyorComponent component, ref EndCollideEvent args)
private void OnConveyorEndCollide(Entity<ConveyorComponent> ent, ref EndCollideEvent args)
{
component.Intersecting.Remove(args.OtherEntity);
if (!TryComp(args.OtherEntity, out ConveyedComponent? conveyed))
return;

if (component.Intersecting.Count == 0)
RemComp<ActiveConveyorComponent>(uid);
if (!conveyed.Colliding.Remove(ent.Owner))
return;

Dirty(args.OtherEntity, conveyed);
}

public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);

var conveyed = new HashSet<EntityUid>();
// Don't use it directly in EntityQuery because we may be able to save getcomponents.
var xformQuery = GetEntityQuery<TransformComponent>();
var bodyQuery = GetEntityQuery<PhysicsComponent>();
var query = EntityQueryEnumerator<ActiveConveyorComponent, ConveyorComponent>();
var query = EntityQueryEnumerator<ConveyedComponent, TransformComponent, PhysicsComponent>();
_ents.Clear();

while (query.MoveNext(out var uid, out var _, out var comp))
while (query.MoveNext(out var uid, out var comp, out var xform, out var physics))
{
Convey(uid, comp, xformQuery, bodyQuery, conveyed, frameTime, prediction);
if (TryConvey((uid, comp, physics, xform), prediction, frameTime))
continue;

_ents.Add(uid);
}

foreach (var ent in _ents)
{
RemComp<ConveyedComponent>(ent);
}
}

private void Convey(EntityUid uid, ConveyorComponent comp, EntityQuery<TransformComponent> xformQuery, EntityQuery<PhysicsComponent> bodyQuery, HashSet<EntityUid> conveyed, float frameTime, bool prediction)
private bool TryConvey(Entity<ConveyedComponent, PhysicsComponent, TransformComponent> entity, bool prediction, float frameTime)
{
// Use an event for conveyors to know what needs to run
if (!CanRun(comp))
return;
var physics = entity.Comp2;
var xform = entity.Comp3;
var contacting = entity.Comp1.Colliding.Count > 0;

var speed = comp.Speed;
if (!contacting)
return false;

if (speed <= 0f || !xformQuery.TryGetComponent(uid, out var xform) || xform.GridUid == null)
return;
// Client moment
if (!physics.Predict && prediction)
return true;

if (physics.BodyType == BodyType.Static)
return false;

if (!_gridQuery.TryComp(xform.GridUid, out var grid))
return true;

var gridTile = _maps.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates);
_conveyors.Clear();

var conveyorPos = xform.LocalPosition;
var conveyorRot = xform.LocalRotation;
// Check for any conveyors on the attached tile.
Lookup.GetLocalEntitiesIntersecting(xform.GridUid.Value, gridTile, _conveyors);
DebugTools.Assert(_conveyors.Count <= 1);

conveyorRot += comp.Angle;
// No more conveyors.
if (_conveyors.Count == 0)
return true;

if (physics.BodyStatus == BodyStatus.InAir ||
_gravity.IsWeightless(entity, physics, xform))
{
return true;
}

Entity<ConveyorComponent> bestConveyor = default;
var bestSpeed = 0f;

foreach (var conveyor in _conveyors)
{
if (conveyor.Comp.Speed > bestSpeed && CanRun(conveyor))
{
bestSpeed = conveyor.Comp.Speed;
bestConveyor = conveyor;
}
}

if (bestSpeed == 0f || bestConveyor == default)
return true;

var comp = bestConveyor.Comp!;
var conveyorXform = _xformQuery.GetComponent(bestConveyor.Owner);
var conveyorPos = conveyorXform.LocalPosition;
var conveyorRot = conveyorXform.LocalRotation;

conveyorRot += bestConveyor.Comp!.Angle;

if (comp.State == ConveyorState.Reverse)
conveyorRot += MathF.PI;

var direction = conveyorRot.ToWorldVec();

foreach (var (entity, transform, body) in GetEntitiesToMove(comp, xform, xformQuery, bodyQuery))
{
if (!conveyed.Add(entity) || prediction && !body.Predict)
continue;
var localPos = xform.LocalPosition;
var itemRelative = conveyorPos - localPos;

var localPos = transform.LocalPosition;
var itemRelative = conveyorPos - localPos;
localPos += Convey(direction, bestSpeed, frameTime, itemRelative);

localPos += Convey(direction, speed, frameTime, itemRelative);
transform.LocalPosition = localPos;
TransformSystem.SetLocalPosition(entity, localPos, xform);

// Force it awake for collisionwake reasons.
Physics.SetAwake((entity, body), true);
Physics.SetSleepTime(body, 0f);
}
Dirty(uid, comp);
// Force it awake for collisionwake reasons.
Physics.SetAwake((entity, physics), true);
Physics.SetSleepTime(physics, 0f);

return true;
}

private static Vector2 Convey(Vector2 direction, float speed, float frameTime, Vector2 itemRelative)
Expand Down Expand Up @@ -140,36 +202,6 @@ private static Vector2 Convey(Vector2 direction, float speed, float frameTime, V
}
}

private IEnumerable<(EntityUid, TransformComponent, PhysicsComponent)> GetEntitiesToMove(
ConveyorComponent comp,
TransformComponent xform,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<PhysicsComponent> bodyQuery)
{
// Check if the thing's centre overlaps the grid tile.
var grid = Comp<MapGridComponent>(xform.GridUid!.Value);
var tile = grid.GetTileRef(xform.Coordinates);
var conveyorBounds = Lookup.GetLocalBounds(tile, grid.TileSize);

foreach (var entity in comp.Intersecting)
{
if (!xformQuery.TryGetComponent(entity, out var entityXform) || entityXform.ParentUid != xform.GridUid!.Value)
continue;

if (!bodyQuery.TryGetComponent(entity, out var physics) || physics.BodyType == BodyType.Static || physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(entity, physics, entityXform))
continue;

// Yes there's still going to be the occasional rounding issue where it stops getting conveyed
// When you fix the corner issue that will fix this anyway.
var gridAABB = new Box2(entityXform.LocalPosition - _expansion, entityXform.LocalPosition + _expansion);

if (!conveyorBounds.Intersects(gridAABB))
continue;

yield return (entity, entityXform, physics);
}
}

public bool CanRun(ConveyorComponent component)
{
return component.State != ConveyorState.Off && component.Powered;
Expand Down
1 change: 0 additions & 1 deletion Resources/Maps/origin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81416,7 +81416,6 @@ entities:
- type: DeviceLinkSink
links:
- 26152
- type: ActiveConveyor
- uid: 12943
components:
- type: Transform
Expand Down
8 changes: 4 additions & 4 deletions Resources/Prototypes/Entities/Structures/conveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
conveyor:
shape: !type:PolygonShape
vertices:
- -0.55,-0.55
- 0.55,-0.55
- 0.55,0.55
- -0.55,0.55
- -0.49,-0.49
- 0.49,-0.49
- 0.49,0.49
- -0.49,0.49
layer:
- Impassable
- MidImpassable
Expand Down
Loading