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

InputMover delta state #33979

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion Content.Shared/ActionBlocker/ActionBlockerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ public bool UpdateCanMove(EntityUid uid, InputMoverComponent? component = null)
RaiseLocalEvent(uid, ev);

if (component.CanMove == ev.Cancelled)
Dirty(uid, component);
{
DirtyField(uid, component, nameof(InputMoverComponent.CanMove));
}

component.CanMove = !ev.Cancelled;
return !ev.Cancelled;
Expand Down
32 changes: 31 additions & 1 deletion Content.Shared/Movement/Components/InputMoverComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@
namespace Content.Shared.Movement.Components
{
[RegisterComponent, NetworkedComponent]
public sealed partial class InputMoverComponent : Component
public sealed partial class InputMoverComponent : Component, IComponentDelta
{
/// <inheritdoc/>
public GameTick LastFieldUpdate { get; set; }

/// <inheritdoc/>
public GameTick[] LastModifiedFields { get; set; }

// This class has to be able to handle server TPS being lower than client FPS.
// While still having perfectly responsive movement client side.
// We do this by keeping track of the exact sub-tick values that inputs are pressed on the client,
Expand Down Expand Up @@ -76,6 +82,30 @@ public sealed partial class InputMoverComponent : Component
public bool CanMove = true;
}

[Serializable, NetSerializable]
public record struct InputHeldDeltaState : IComponentDeltaState<InputMoverComponentState>
{
public MoveButtons HeldMoveButtons;

public void ApplyToFullState(InputMoverComponentState fullState)
{
fullState.HeldMoveButtons = HeldMoveButtons;
}

public InputMoverComponentState CreateNewFullState(InputMoverComponentState fullState)
{
return new InputMoverComponentState()
{
CanMove = fullState.CanMove,
HeldMoveButtons = HeldMoveButtons,
LerpTarget = fullState.LerpTarget,
RelativeEntity = fullState.RelativeEntity,
RelativeRotation = fullState.RelativeRotation,
TargetRelativeRotation = fullState.TargetRelativeRotation,
};
}
}

[Serializable, NetSerializable]
public sealed class InputMoverComponentState : ComponentState
{
Expand Down
101 changes: 69 additions & 32 deletions Content.Shared/Movement/Systems/SharedMoverController.Input.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ public abstract partial class SharedMoverController

private void InitializeInput()
{
EntityManager.ComponentFactory.RegisterNetworkedFields(
EntityManager.ComponentFactory.GetRegistration(typeof(InputMoverComponent)),
nameof(InputMoverComponent.HeldMoveButtons),
nameof(InputMoverComponent.CanMove),
nameof(InputMoverComponent.RelativeEntity),
nameof(InputMoverComponent.LerpTarget),
nameof(InputMoverComponent.RelativeRotation),
nameof(InputMoverComponent.TargetRelativeRotation));

var moveUpCmdHandler = new MoverDirInputCmdHandler(this, Direction.North);
var moveLeftCmdHandler = new MoverDirInputCmdHandler(this, Direction.West);
var moveRightCmdHandler = new MoverDirInputCmdHandler(this, Direction.East);
Expand Down Expand Up @@ -95,41 +104,62 @@ protected void SetMoveInput(Entity<InputMoverComponent> entity, MoveButtons butt
var moveEvent = new MoveInputEvent(entity, entity.Comp.HeldMoveButtons);
entity.Comp.HeldMoveButtons = buttons;
RaiseLocalEvent(entity, ref moveEvent);
Dirty(entity, entity.Comp);
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.HeldMoveButtons));

var ev = new SpriteMoveEvent(entity.Comp.HeldMoveButtons != MoveButtons.None);
RaiseLocalEvent(entity, ref ev);
}

private void OnMoverHandleState(Entity<InputMoverComponent> entity, ref ComponentHandleState args)
{
if (args.Current is not InputMoverComponentState state)
return;

// Handle state
entity.Comp.LerpTarget = state.LerpTarget;
entity.Comp.RelativeRotation = state.RelativeRotation;
entity.Comp.TargetRelativeRotation = state.TargetRelativeRotation;
entity.Comp.CanMove = state.CanMove;
entity.Comp.RelativeEntity = EnsureEntity<InputMoverComponent>(state.RelativeEntity, entity.Owner);

// Reset
entity.Comp.LastInputTick = GameTick.Zero;
entity.Comp.LastInputSubTick = 0;

if (entity.Comp.HeldMoveButtons != state.HeldMoveButtons)
if (args.Current is InputHeldDeltaState held && held.HeldMoveButtons != entity.Comp.HeldMoveButtons)
{
RaiseMoveEvent(entity, held.HeldMoveButtons);
}
else if (args.Current is InputMoverComponentState state)
{
var moveEvent = new MoveInputEvent(entity, entity.Comp.HeldMoveButtons);
entity.Comp.HeldMoveButtons = state.HeldMoveButtons;
RaiseLocalEvent(entity.Owner, ref moveEvent);
// Handle state
entity.Comp.LerpTarget = state.LerpTarget;
entity.Comp.RelativeRotation = state.RelativeRotation;
entity.Comp.TargetRelativeRotation = state.TargetRelativeRotation;
entity.Comp.CanMove = state.CanMove;
entity.Comp.RelativeEntity = EnsureEntity<InputMoverComponent>(state.RelativeEntity, entity.Owner);

var ev = new SpriteMoveEvent(entity.Comp.HeldMoveButtons != MoveButtons.None);
RaiseLocalEvent(entity, ref ev);
RaiseMoveEvent(entity, state.HeldMoveButtons);
}
}

private void RaiseMoveEvent(Entity<InputMoverComponent> entity, MoveButtons buttons)
{
if (entity.Comp.HeldMoveButtons == buttons)
return;

var moveEvent = new MoveInputEvent(entity, entity.Comp.HeldMoveButtons);
entity.Comp.HeldMoveButtons = buttons;
RaiseLocalEvent(entity.Owner, ref moveEvent);

var ev = new SpriteMoveEvent(entity.Comp.HeldMoveButtons != MoveButtons.None);
RaiseLocalEvent(entity, ref ev);
}

private void OnMoverGetState(Entity<InputMoverComponent> entity, ref ComponentGetState args)
{
var fields = EntityManager.GetModifiedFields(entity.Comp, args.FromTick);

switch (fields)
{
case 1 << 0:
args.State = new InputHeldDeltaState()
{
HeldMoveButtons = entity.Comp.HeldMoveButtons,
};
return;
}

args.State = new InputMoverComponentState()
{
CanMove = entity.Comp.CanMove,
Expand All @@ -156,7 +186,7 @@ public void RotateCamera(EntityUid uid, Angle angle)
return;

mover.TargetRelativeRotation += angle;
Dirty(uid, mover);
DirtyField(uid, mover, nameof(InputMoverComponent.TargetRelativeRotation));
}

public void ResetCamera(EntityUid uid)
Expand All @@ -168,15 +198,16 @@ public void ResetCamera(EntityUid uid)
}

// If we updated parent then cancel the accumulator and force it now.
if (!TryUpdateRelative(mover, XformQuery.GetComponent(uid)) && mover.TargetRelativeRotation.Equals(Angle.Zero))
if (!TryUpdateRelative((uid, mover), XformQuery.GetComponent(uid)) && mover.TargetRelativeRotation.Equals(Angle.Zero))
return;

mover.LerpTarget = TimeSpan.Zero;
mover.TargetRelativeRotation = Angle.Zero;
Dirty(uid, mover);
DirtyField(uid, mover, nameof(InputMoverComponent.LerpTarget));
DirtyField(uid, mover, nameof(InputMoverComponent.TargetRelativeRotation));
}

private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xform)
private bool TryUpdateRelative(Entity<InputMoverComponent> mover, TransformComponent xform)
{
var relative = xform.GridUid;
relative ??= xform.MapUid;
Expand All @@ -186,7 +217,7 @@ private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xfo
// 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))
if (mover.Comp.RelativeEntity.Equals(relative))
return false;

// Okay need to get our old relative rotation with respect to our new relative rotation
Expand All @@ -195,15 +226,16 @@ private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xfo
var targetRotation = Angle.Zero;

// Get our current relative rotation
if (XformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
if (XformQuery.TryGetComponent(mover.Comp.RelativeEntity, out var oldRelativeXform))
{
currentRotation = _transform.GetWorldRotation(oldRelativeXform, XformQuery) + mover.RelativeRotation;
currentRotation = _transform.GetWorldRotation(oldRelativeXform, XformQuery) + mover.Comp.RelativeRotation;
}

if (XformQuery.TryGetComponent(relative, out var relativeXform))
{
// This is our current rotation relative to our new parent.
mover.RelativeRotation = (currentRotation - _transform.GetWorldRotation(relativeXform)).FlipPositive();
mover.Comp.RelativeRotation = (currentRotation - _transform.GetWorldRotation(relativeXform)).FlipPositive();
DirtyField(mover.Owner, mover.Comp, nameof(InputMoverComponent.RelativeRotation));
}

// If we went from grid -> map we'll preserve our worldrotation
Expand All @@ -218,11 +250,13 @@ private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xfo
if (CameraRotationLocked)
targetRotation = Angle.Zero;
else
targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
targetRotation = mover.Comp.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
}

mover.RelativeEntity = relative;
mover.TargetRelativeRotation = targetRotation;
mover.Comp.RelativeEntity = relative;
mover.Comp.TargetRelativeRotation = targetRotation;
DirtyField(mover.Owner, mover.Comp, nameof(InputMoverComponent.RelativeEntity));
DirtyField(mover.Owner, mover.Comp, nameof(InputMoverComponent.TargetRelativeRotation));
return true;
}

Expand Down Expand Up @@ -257,7 +291,7 @@ private void OnInputParentChange(Entity<InputMoverComponent> entity, ref EntPare
if (entity.Comp.LifeStage < ComponentLifeStage.Running)
{
entity.Comp.RelativeEntity = relative;
Dirty(entity.Owner, entity.Comp);
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.RelativeEntity));
return;
}

Expand All @@ -271,7 +305,10 @@ private void OnInputParentChange(Entity<InputMoverComponent> entity, ref EntPare
entity.Comp.TargetRelativeRotation = Angle.Zero;
entity.Comp.RelativeRotation = Angle.Zero;
entity.Comp.LerpTarget = TimeSpan.Zero;
Dirty(entity.Owner, entity.Comp);
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.RelativeEntity));
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.TargetRelativeRotation));
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.RelativeRotation));
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.LerpTarget));
return;
}

Expand All @@ -281,14 +318,14 @@ private void OnInputParentChange(Entity<InputMoverComponent> entity, ref EntPare
if (entity.Comp.LerpTarget >= Timing.CurTime)
{
entity.Comp.LerpTarget = TimeSpan.Zero;
Dirty(entity.Owner, entity.Comp);
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.LerpTarget));
}

return;
}

entity.Comp.LerpTarget = TimeSpan.FromSeconds(InputMoverComponent.LerpTime) + Timing.CurTime;
Dirty(entity.Owner, entity.Comp);
DirtyField(entity.Owner, entity.Comp, nameof(InputMoverComponent.LerpTarget));
}

private void HandleDirChange(EntityUid entity, Direction dir, ushort subTick, bool state)
Expand Down
5 changes: 1 addition & 4 deletions Content.Shared/Movement/Systems/SharedMoverController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,7 @@ protected void HandleMobMovement(
// Update relative movement
if (mover.LerpTarget < Timing.CurTime)
{
if (TryUpdateRelative(mover, xform))
{
Dirty(uid, mover);
}
TryUpdateRelative((uid, mover), xform);
}

LerpRotation(uid, mover, frameTime);
Expand Down
Loading