Revert "Multi-threaded mob movement" (#12809)

This commit is contained in:
metalgearsloth
2022-11-30 12:39:02 +11:00
committed by GitHub
parent c86170303d
commit 4f6ead57bf
5 changed files with 105 additions and 242 deletions

View File

@@ -1,14 +1,11 @@
using Content.Shared.Inventory;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Pulling.Components;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Containers;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Physics.Controllers
{
@@ -137,30 +134,8 @@ namespace Content.Client.Physics.Controllers
}
}
var mobQuery = GetEntityQuery<MobMoverComponent>();
var inventoryQuery = GetEntityQuery<InventoryComponent>();
var containerQuery = GetEntityQuery<ContainerManagerComponent>();
var footQuery = GetEntityQuery<FootstepModifierComponent>();
DebugTools.Assert(!UsedMobMovement.ContainsKey(mover.Owner));
// Server-side should just be handled on its own so we'll just do this shizznit
HandleMobMovement(mover, body, xformMover, frameTime, xformQuery, mobQuery, inventoryQuery, containerQuery, footQuery, out var dirtyMover, out var linearVelocity, out var sound, out var audio);
MetaDataComponent? metadata = null;
if (dirtyMover)
{
Dirty(mover, metadata);
}
if (linearVelocity != null)
{
PhysicsSystem.SetLinearVelocity(body, linearVelocity.Value, false);
PhysicsSystem.SetAngularVelocity(body, 0f, false);
Dirty(body, metadata);
}
Audio.PlayPredicted(sound, mover.Owner, mover.Owner, audio);
HandleMobMovement(mover, body, xformMover, frameTime, xformQuery);
}
protected override bool CanSound()

View File

@@ -1,28 +1,20 @@
using System.Buffers;
using System.Threading.Tasks;
using Content.Server.Cargo.Components;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems;
using Content.Shared.Inventory;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Physics.Components;
using Robust.Shared.Player;
using Robust.Shared.Threading;
using Robust.Shared.Utility;
namespace Content.Server.Physics.Controllers
{
public sealed class MoverController : SharedMoverController
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IParallelManager _parallel = default!;
[Dependency] private readonly ThrusterSystem _thruster = default!;
private Dictionary<ShuttleComponent, List<(PilotComponent, InputMoverComponent, TransformComponent)>> _shuttlePilots = new();
@@ -72,12 +64,7 @@ namespace Content.Server.Physics.Controllers
var xformQuery = GetEntityQuery<TransformComponent>();
var moverQuery = GetEntityQuery<InputMoverComponent>();
var movers = AllEntityQuery<InputMoverComponent>();
var totalCount = EntityManager.Count<InputMoverComponent>();
var moveInput = ArrayPool<(InputMoverComponent Mover, TransformComponent Transform, PhysicsComponent Physics)>.Shared.Rent(totalCount);
var count = 0;
while (movers.MoveNext(out var mover))
foreach (var mover in EntityQuery<InputMoverComponent>(true))
{
if (relayQuery.TryGetComponent(mover.Owner, out var relayed) && relayed.RelayEntity != null)
{
@@ -96,11 +83,13 @@ namespace Content.Server.Physics.Controllers
continue;
}
PhysicsComponent? body;
PhysicsComponent? body = null;
TransformComponent? xformMover = xform;
if (mover.ToParent && relayQuery.HasComponent(xform.ParentUid))
{
if (!bodyQuery.TryGetComponent(xform.ParentUid, out body) ||
!xformQuery.HasComponent(xform.ParentUid))
!TryComp(xform.ParentUid, out xformMover))
{
continue;
}
@@ -110,64 +99,9 @@ namespace Content.Server.Physics.Controllers
continue;
}
DebugTools.Assert(!UsedMobMovement.ContainsKey(mover.Owner));
// To avoid threading issues on adding dictionary entries later.
UsedMobMovement[mover.Owner] = false;
moveInput[count++] = (mover, xform, body);
HandleMobMovement(mover, body, xformMover, frameTime, xformQuery);
}
var moveResults = ArrayPool<(bool DirtyMover, Vector2? LinearVelocity, SoundSpecifier? sound, AudioParams audio)>.Shared.Rent(count);
var mobQuery = GetEntityQuery<MobMoverComponent>();
var inventoryQuery = GetEntityQuery<InventoryComponent>();
var containerQuery = GetEntityQuery<ContainerManagerComponent>();
var footQuery = GetEntityQuery<FootstepModifierComponent>();
var options = new ParallelOptions()
{
MaxDegreeOfParallelism = _parallel.ParallelProcessCount,
};
Parallel.For(0, count, options, i =>
{
var (mover, xform, body) = moveInput[i];
HandleMobMovement(mover, body, xform, frameTime, xformQuery, mobQuery, inventoryQuery, containerQuery, footQuery, out var dirtyMover, out var linearVelocity, out var sound, out var audio);
moveResults[i] = (dirtyMover, linearVelocity, sound, audio);
});
var metaQuery = GetEntityQuery<MetaDataComponent>();
for (var i = 0; i < count; i++)
{
var results = moveResults[i];
var input = moveInput[i];
MetaDataComponent? metadata = null;
// Calling dirty isn't thread-safe sadly.
if (results.DirtyMover)
{
metadata ??= metaQuery.GetComponent(input.Mover.Owner);
Dirty(input.Mover, metadata);
}
if (results.LinearVelocity != null)
{
metadata ??= metaQuery.GetComponent(input.Physics.Owner);
PhysicsSystem.SetLinearVelocity(input.Physics, results.LinearVelocity.Value, false);
PhysicsSystem.SetAngularVelocity(input.Physics, 0f, false);
Dirty(input.Physics, metadata);
}
if (results.sound != null)
{
Audio.PlayPredicted(results.sound, input.Mover.Owner, input.Mover.Owner, results.audio);
}
moveInput[i] = default;
}
ArrayPool<(bool DirtyMover, Vector2? LinearVelocity, SoundSpecifier? Sound, AudioParams Audio)>.Shared.Return(moveResults);
ArrayPool<(InputMoverComponent, TransformComponent, PhysicsComponent)>.Shared.Return(moveInput);
HandleShuttleMovement(frameTime);
}

View File

@@ -1,6 +1,5 @@
using Content.Shared.Movement.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Timing;
namespace Content.Shared.Movement.Components
@@ -62,8 +61,8 @@ namespace Content.Shared.Movement.Components
/// <summary>
/// If we traverse on / off a grid then set a timer to update our relative inputs.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("lerpTarget", customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan LerpTarget;
[ViewVariables(VVAccess.ReadWrite)]
public float LerpAccumulator;
public const float LerpTime = 1.0f;

View File

@@ -80,7 +80,7 @@ namespace Content.Shared.Movement.Systems
component.RelativeRotation = state.RelativeRotation;
component.TargetRelativeRotation = state.TargetRelativeRotation;
component.RelativeEntity = state.RelativeEntity;
component.LerpTarget = state.LerpTarget;
component.LerpAccumulator = state.LerpAccumulator;
}
private void OnInputGetState(EntityUid uid, InputMoverComponent component, ref ComponentGetState args)
@@ -91,7 +91,7 @@ namespace Content.Shared.Movement.Systems
component.RelativeRotation,
component.TargetRelativeRotation,
component.RelativeEntity,
component.LerpTarget);
component.LerpAccumulator);
}
private void ShutdownInput()
@@ -118,73 +118,13 @@ namespace Content.Shared.Movement.Systems
public void ResetCamera(EntityUid uid)
{
if (CameraRotationLocked ||
!TryComp<InputMoverComponent>(uid, out var mover))
{
return;
}
// If we updated parent then cancel the accumulator and force it now.
var xformQuery = GetEntityQuery<TransformComponent>();
if (!TryUpdateRelative(mover, xformQuery.GetComponent(uid), xformQuery) && mover.TargetRelativeRotation.Equals(Angle.Zero))
if (CameraRotationLocked || !TryComp<InputMoverComponent>(uid, out var mover) || mover.TargetRelativeRotation.Equals(Angle.Zero))
return;
mover.LerpTarget = TimeSpan.Zero;
mover.TargetRelativeRotation = Angle.Zero;
Dirty(mover);
}
private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xform, EntityQuery<TransformComponent> xformQuery)
{
var relative = xform.GridUid;
relative ??= xform.MapUid;
// So essentially what we want:
// 1. If we go from grid to map then preserve our rotation and continue as usual
// 2. If we go from grid -> grid then (after lerp time) snap to nearest cardinal (probably imperceptible)
// 3. If we go from map -> grid then (after lerp time) snap to nearest cardinal
if (mover.RelativeEntity.Equals(relative))
return false;
// Okay need to get our old relative rotation with respect to our new relative rotation
// e.g. if we were right side up on our current grid need to get what that is on our new grid.
var currentRotation = Angle.Zero;
var targetRotation = Angle.Zero;
// Get our current relative rotation
if (xformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
{
currentRotation = _transform.GetWorldRotation(oldRelativeXform, xformQuery) + mover.RelativeRotation;
}
if (xformQuery.TryGetComponent(relative, out var relativeXform))
{
// This is our current rotation relative to our new parent.
mover.RelativeRotation = (currentRotation - _transform.GetWorldRotation(relativeXform, xformQuery)).FlipPositive();
}
// If we went from grid -> map we'll preserve our worldrotation
if (relative != null && _mapManager.IsMap(relative.Value))
{
targetRotation = currentRotation.FlipPositive().Reduced();
}
// If we went from grid -> grid OR grid -> map then snap the target to cardinal and lerp there.
// OR just rotate to zero (depending on cvar)
else if (relative != null && _mapManager.IsGrid(relative.Value))
{
if (CameraRotationLocked)
targetRotation = Angle.Zero;
else
targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
}
mover.RelativeEntity = relative;
mover.TargetRelativeRotation = targetRotation;
return true;
}
public Angle GetParentGridAngle(InputMoverComponent mover, EntityQuery<TransformComponent> xformQuery)
{
var rotation = mover.RelativeRotation;
@@ -221,16 +161,16 @@ namespace Content.Shared.Movement.Systems
// If we go on a grid and back off then just reset the accumulator.
if (relative == component.RelativeEntity)
{
if (component.LerpTarget >= Timing.CurTime)
if (component.LerpAccumulator != 0f)
{
component.LerpTarget = TimeSpan.Zero;
component.LerpAccumulator = 0f;
Dirty(component);
}
return;
}
component.LerpTarget = TimeSpan.FromSeconds(InputMoverComponent.LerpTime) + Timing.CurTime;
component.LerpAccumulator = InputMoverComponent.LerpTime;
Dirty(component);
}
@@ -559,16 +499,16 @@ namespace Content.Shared.Movement.Systems
/// </summary>
public Angle TargetRelativeRotation;
public EntityUid? RelativeEntity;
public TimeSpan LerpTarget;
public float LerpAccumulator = 0f;
public InputMoverComponentState(MoveButtons buttons, bool canMove, Angle relativeRotation, Angle targetRelativeRotation, EntityUid? relativeEntity, TimeSpan lerpTarget)
public InputMoverComponentState(MoveButtons buttons, bool canMove, Angle relativeRotation, Angle targetRelativeRotation, EntityUid? relativeEntity, float lerpAccumulator)
{
Buttons = buttons;
CanMove = canMove;
RelativeRotation = relativeRotation;
TargetRelativeRotation = targetRelativeRotation;
RelativeEntity = relativeEntity;
LerpTarget = lerpTarget;
LerpAccumulator = lerpAccumulator;
}
}

View File

@@ -36,7 +36,7 @@ namespace Content.Shared.Movement.Systems
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedGravitySystem _gravity = default!;
[Dependency] private readonly SharedMobStateSystem _mobState = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly TagSystem _tags = default!;
@@ -96,35 +96,69 @@ namespace Content.Shared.Movement.Systems
/// <summary>
/// Movement while considering actionblockers, weightlessness, etc.
/// </summary>
/// <remarks>
/// Yes this signature is massive but this is also called a lot.
/// </remarks>
protected void HandleMobMovement(
InputMoverComponent mover,
PhysicsComponent physicsComponent,
TransformComponent xform,
float frameTime,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<MobMoverComponent> mobQuery,
EntityQuery<InventoryComponent> inventoryQuery,
EntityQuery<ContainerManagerComponent> containerQuery,
EntityQuery<FootstepModifierComponent> footQuery,
out bool dirtyMover,
out Vector2? linearVelocity,
out SoundSpecifier? sound,
out AudioParams audio)
EntityQuery<TransformComponent> xformQuery)
{
dirtyMover = false;
linearVelocity = Vector2.Zero;
sound = null;
audio = default;
DebugTools.Assert(!UsedMobMovement.ContainsKey(mover.Owner));
// Update relative movement
if (mover.LerpTarget < Timing.CurTime)
if (mover.LerpAccumulator > 0f)
{
if (TryUpdateRelative(mover, xform, xformQuery))
Dirty(mover);
mover.LerpAccumulator -= frameTime;
if (mover.LerpAccumulator <= 0f)
{
dirtyMover = true;
mover.LerpAccumulator = 0f;
var relative = xform.GridUid;
relative ??= xform.MapUid;
// So essentially what we want:
// 1. If we go from grid to map then preserve our rotation and continue as usual
// 2. If we go from grid -> grid then (after lerp time) snap to nearest cardinal (probably imperceptible)
// 3. If we go from map -> grid then (after lerp time) snap to nearest cardinal
if (!mover.RelativeEntity.Equals(relative))
{
// Okay need to get our old relative rotation with respect to our new relative rotation
// e.g. if we were right side up on our current grid need to get what that is on our new grid.
var currentRotation = Angle.Zero;
var targetRotation = Angle.Zero;
// Get our current relative rotation
if (xformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
{
currentRotation = oldRelativeXform.WorldRotation + mover.RelativeRotation;
}
if (xformQuery.TryGetComponent(relative, out var relativeXform))
{
// This is our current rotation relative to our new parent.
mover.RelativeRotation = (currentRotation - relativeXform.WorldRotation).FlipPositive();
}
// If we went from grid -> map we'll preserve our worldrotation
if (relative != null && _mapManager.IsMap(relative.Value))
{
targetRotation = currentRotation.FlipPositive().Reduced();
}
// If we went from grid -> grid OR grid -> map then snap the target to cardinal and lerp there.
// OR just rotate to zero (depending on cvar)
else if (relative != null && _mapManager.IsGrid(relative.Value))
{
if (CameraRotationLocked)
targetRotation = Angle.Zero;
else
targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
}
mover.RelativeEntity = relative;
mover.TargetRelativeRotation = targetRotation;
}
}
}
@@ -149,13 +183,13 @@ namespace Content.Shared.Movement.Systems
mover.RelativeRotation += adjustment;
mover.RelativeRotation.FlipPositive();
dirtyMover = true;
Dirty(mover);
}
else if (!angleDiff.Equals(Angle.Zero))
{
mover.TargetRelativeRotation.FlipPositive();
mover.RelativeRotation = mover.TargetRelativeRotation;
dirtyMover = true;
Dirty(mover);
}
if (!UseMobMovement(mover, physicsComponent))
@@ -183,7 +217,7 @@ namespace Content.Shared.Movement.Systems
// No gravity: is our entity touching anything?
touching = ev.CanMove;
if (!touching && mobQuery.TryGetComponent(xform.Owner, out var mobMover))
if (!touching && TryComp<MobMoverComponent>(xform.Owner, out var mobMover))
touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsComponent);
}
}
@@ -240,20 +274,19 @@ namespace Content.Shared.Movement.Systems
{
// This should have its event run during island solver soooo
xform.DeferUpdates = true;
_transform.SetWorldRotation(xform, worldTotal.ToWorldAngle(), xformQuery);
xform.WorldRotation = worldTotal.ToWorldAngle();
xform.DeferUpdates = false;
if (!weightless && TryComp<MobMoverComponent>(mover.Owner, out var mobMover) &&
TryGetSound(weightless, mover, mobMover, xform, inventoryQuery, containerQuery, footQuery, out var soundSpec))
TryGetSound(weightless, mover, mobMover, xform, out var sound))
{
var soundModifier = mover.Sprinting ? 1.0f : FootstepWalkingAddedVolumeMultiplier;
var audioParams = soundSpec.Params
var audioParams = sound.Params
.WithVolume(FootstepVolume * soundModifier)
.WithVariation(soundSpec.Params.Variation ?? FootstepVariation);
.WithVariation(sound.Params.Variation ?? FootstepVariation);
sound = soundSpec;
audio = audioParams;
_audio.PlayPredicted(sound, mover.Owner, mover.Owner, audioParams);
}
}
@@ -262,13 +295,10 @@ namespace Content.Shared.Movement.Systems
if (!weightless || touching)
Accelerate(ref velocity, in worldTotal, accel, frameTime);
if (physicsComponent.LinearVelocity.EqualsApprox(velocity, 0.0001f) &&
MathHelper.CloseTo(physicsComponent.AngularVelocity, 0f))
{
return;
}
PhysicsSystem.SetLinearVelocity(physicsComponent, velocity);
linearVelocity = velocity;
// Ensures that players do not spiiiiiiin
PhysicsSystem.SetAngularVelocity(physicsComponent, 0);
}
private void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref Vector2 velocity)
@@ -349,16 +379,7 @@ namespace Content.Shared.Movement.Systems
protected abstract bool CanSound();
private bool TryGetSound(
bool weightless,
InputMoverComponent mover,
MobMoverComponent mobMover,
TransformComponent xform,
EntityQuery<InventoryComponent> inventoryQuery,
EntityQuery<ContainerManagerComponent> containerQuery,
EntityQuery<FootstepModifierComponent> footQuery,
[NotNullWhen(true)] out SoundSpecifier? sound)
private bool TryGetSound(bool weightless, InputMoverComponent mover, MobMoverComponent mobMover, TransformComponent xform, [NotNullWhen(true)] out SoundSpecifier? sound)
{
sound = null;
@@ -392,21 +413,18 @@ namespace Content.Shared.Movement.Systems
if (mobMover.StepSoundDistance < distanceNeeded) return false;
mobMover.StepSoundDistance -= distanceNeeded;
EntityUid? shoes = null;
if (inventoryQuery.TryGetComponent(mover.Owner, out var inventory) &&
containerQuery.TryGetComponent(mover.Owner, out var containerManager) &&
_inventory.TryGetSlotEntity(mover.Owner, "shoes", out shoes, inventory, containerManager) &&
footQuery.TryGetComponent(shoes, out var modifier))
if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) &&
EntityManager.TryGetComponent<FootstepModifierComponent>(shoes, out var modifier))
{
sound = modifier.Sound;
return true;
}
return TryGetFootstepSound(coordinates, shoes != null, footQuery, out sound);
return TryGetFootstepSound(coordinates, shoes != null, out sound);
}
private bool TryGetFootstepSound(EntityCoordinates coordinates, bool haveShoes, EntityQuery<FootstepModifierComponent> footQuery, [NotNullWhen(true)] out SoundSpecifier? sound)
private bool TryGetFootstepSound(EntityCoordinates coordinates, bool haveShoes, [NotNullWhen(true)] out SoundSpecifier? sound)
{
sound = null;
var gridUid = coordinates.GetGridUid(EntityManager);
@@ -414,7 +432,7 @@ namespace Content.Shared.Movement.Systems
// Fallback to the map
if (gridUid == null)
{
if (footQuery.TryGetComponent(coordinates.GetMapUid(EntityManager), out var modifier))
if (TryComp<FootstepModifierComponent>(coordinates.GetMapUid(EntityManager), out var modifier))
{
sound = modifier.Sound;
return true;
@@ -426,21 +444,18 @@ namespace Content.Shared.Movement.Systems
var grid = _mapManager.GetGrid(gridUid.Value);
var tile = grid.GetTileRef(coordinates);
if (tile.IsSpace(_tileDefinitionManager))
return false;
if (tile.IsSpace(_tileDefinitionManager)) return false;
// If the coordinates have a FootstepModifier component
// i.e. component that emit sound on footsteps emit that sound
var anchoredEnumerator = grid.GetAnchoredEntitiesEnumerator(tile.GridIndices);
while (anchoredEnumerator.MoveNext(out var maybeFootstep))
foreach (var maybeFootstep in grid.GetAnchoredEntities(tile.GridIndices))
{
if (EntityManager.TryGetComponent(maybeFootstep, out FootstepModifierComponent? footstep))
{
if (!footQuery.TryGetComponent(maybeFootstep, out var footstep))
continue;
sound = footstep.Sound;
return true;
}
}
// Walking on a tile.
var def = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];