From fcca27fce3790275878df9dc165ac5eb1931334d Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sat, 21 Dec 2024 16:42:36 +1100 Subject: [PATCH] InputMover delta state Probably last one on content unless something big comes up. --- .../ActionBlocker/ActionBlockerSystem.cs | 4 +- .../Components/InputMoverComponent.cs | 32 +++++- .../Systems/SharedMoverController.Input.cs | 101 ++++++++++++------ .../Movement/Systems/SharedMoverController.cs | 5 +- 4 files changed, 104 insertions(+), 38 deletions(-) diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index 8a4b5baffd34d2..b4efd7dd815e80 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -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; diff --git a/Content.Shared/Movement/Components/InputMoverComponent.cs b/Content.Shared/Movement/Components/InputMoverComponent.cs index f1e34c90df47e5..ac541e214b4423 100644 --- a/Content.Shared/Movement/Components/InputMoverComponent.cs +++ b/Content.Shared/Movement/Components/InputMoverComponent.cs @@ -8,8 +8,14 @@ namespace Content.Shared.Movement.Components { [RegisterComponent, NetworkedComponent] - public sealed partial class InputMoverComponent : Component + public sealed partial class InputMoverComponent : Component, IComponentDelta { + /// + public GameTick LastFieldUpdate { get; set; } + + /// + 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, @@ -76,6 +82,30 @@ public sealed partial class InputMoverComponent : Component public bool CanMove = true; } + [Serializable, NetSerializable] + public record struct InputHeldDeltaState : IComponentDeltaState + { + 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 { diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs index 6f508d9038c0fe..4a4409931b9b90 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs @@ -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); @@ -95,7 +104,7 @@ protected void SetMoveInput(Entity 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); @@ -103,33 +112,54 @@ protected void SetMoveInput(Entity entity, MoveButtons butt private void OnMoverHandleState(Entity 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(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(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 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 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, @@ -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) @@ -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 mover, TransformComponent xform) { var relative = xform.GridUid; relative ??= xform.MapUid; @@ -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 @@ -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 @@ -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; } @@ -257,7 +291,7 @@ private void OnInputParentChange(Entity 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; } @@ -271,7 +305,10 @@ private void OnInputParentChange(Entity 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; } @@ -281,14 +318,14 @@ private void OnInputParentChange(Entity 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) diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 472d56b1d692d7..cf2f3bdafa565a 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -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);