From 8467d2373cc19cadfa88399659b0c8dda59b2271 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 27 Nov 2022 23:25:54 +1300 Subject: [PATCH] Fix some tabletop prediction jank (#12758) --- Content.Client/Tabletop/TabletopSystem.cs | 27 +++---- .../Tabletop/TabletopSystem.Draggable.cs | 75 ------------------- Content.Server/Tabletop/TabletopSystem.cs | 17 ++++- .../Tabletop/SharedTabletopSystem.cs | 68 ++++++++++++++++- 4 files changed, 91 insertions(+), 96 deletions(-) delete mode 100644 Content.Server/Tabletop/TabletopSystem.Draggable.cs diff --git a/Content.Client/Tabletop/TabletopSystem.cs b/Content.Client/Tabletop/TabletopSystem.cs index 654d2b48ac..c97e6460fa 100644 --- a/Content.Client/Tabletop/TabletopSystem.cs +++ b/Content.Client/Tabletop/TabletopSystem.cs @@ -16,7 +16,6 @@ using Robust.Shared.Input.Binding; using Robust.Shared.Map; using Robust.Shared.Timing; using static Robust.Shared.Input.Binding.PointerInputCmdHandler; -using DrawDepth = Content.Shared.DrawDepth.DrawDepth; namespace Content.Client.Tabletop { @@ -27,7 +26,6 @@ namespace Content.Client.Tabletop [Dependency] private readonly IUserInterfaceManager _uiManger = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; // Time in seconds to wait until sending the location of a dragged entity to the server again private const float Delay = 1f / 10; // 10 Hz @@ -40,10 +38,11 @@ namespace Content.Client.Tabletop public override void Initialize() { + base.Initialize(); UpdatesOutsidePrediction = true; CommandBinds.Builder - .Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false)) + .Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false, true)) .Register(); SubscribeNetworkEvent(OnTabletopPlay); @@ -57,12 +56,8 @@ namespace Content.Client.Tabletop StopDragging(false); } - public override void Update(float frameTime) + public override void FrameUpdate(float frameTime) { - // don't send network messages when doing prediction. - if (!_gameTiming.IsFirstTimePredicted) - return; - if (_window == null) return; @@ -110,7 +105,7 @@ namespace Content.Client.Tabletop // Only send new position to server when Delay is reached if (_timePassed >= Delay && _table != null) { - RaiseNetworkEvent(new TabletopMoveEvent(_draggedEntity.Value, clampedCoords, _table.Value)); + RaisePredictiveEvent(new TabletopMoveEvent(_draggedEntity.Value, clampedCoords, _table.Value)); _timePassed -= Delay; } } @@ -169,6 +164,9 @@ namespace Content.Client.Tabletop private bool OnUse(in PointerInputCmdArgs args) { + if (!_gameTiming.IsFirstTimePredicted) + return false; + return args.State switch { BoundKeyState.Down => OnMouseDown(args), @@ -216,13 +214,7 @@ namespace Content.Client.Tabletop /// The viewport in which we are dragging. private void StartDragging(EntityUid draggedEntity, ScalingViewport viewport) { - RaiseNetworkEvent(new TabletopDraggingPlayerChangedEvent(draggedEntity, true)); - - if (EntityManager.TryGetComponent(draggedEntity, out var appearance)) - { - _appearance.SetData(draggedEntity, TabletopItemVisuals.Scale, new Vector2(1.25f, 1.25f), appearance); - _appearance.SetData(draggedEntity, TabletopItemVisuals.DrawDepth, (int) DrawDepth.Items + 1, appearance); - } + RaisePredictiveEvent(new TabletopDraggingPlayerChangedEvent(draggedEntity, true)); _draggedEntity = draggedEntity; _viewport = viewport; @@ -237,7 +229,8 @@ namespace Content.Client.Tabletop // Set the dragging player on the component to noone if (broadcast && _draggedEntity != null && EntityManager.HasComponent(_draggedEntity.Value)) { - RaiseNetworkEvent(new TabletopDraggingPlayerChangedEvent(_draggedEntity.Value, false)); + RaisePredictiveEvent(new TabletopMoveEvent(_draggedEntity.Value, Transform(_draggedEntity.Value).MapPosition, _table!.Value)); + RaisePredictiveEvent(new TabletopDraggingPlayerChangedEvent(_draggedEntity.Value, false)); } _draggedEntity = null; diff --git a/Content.Server/Tabletop/TabletopSystem.Draggable.cs b/Content.Server/Tabletop/TabletopSystem.Draggable.cs deleted file mode 100644 index 7db264a841..0000000000 --- a/Content.Server/Tabletop/TabletopSystem.Draggable.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Content.Server.Tabletop.Components; -using Content.Shared.Tabletop; -using Content.Shared.Tabletop.Components; -using Content.Shared.Tabletop.Events; -using Robust.Server.Player; -using Robust.Shared.GameStates; -using Robust.Shared.Map; -using DrawDepth = Content.Shared.DrawDepth.DrawDepth; - -namespace Content.Server.Tabletop -{ - public sealed partial class TabletopSystem - { - public void InitializeDraggable() - { - SubscribeNetworkEvent(OnTabletopMove); - SubscribeNetworkEvent(OnDraggingPlayerChanged); - SubscribeLocalEvent(GetDraggableState); - } - - /// - /// Move an entity which is dragged by the user, but check if they are allowed to do so and to these coordinates - /// - private void OnTabletopMove(TabletopMoveEvent msg, EntitySessionEventArgs args) - { - if (args.SenderSession as IPlayerSession is not { AttachedEntity: { } playerEntity } playerSession) - return; - - if (!EntityManager.TryGetComponent(msg.TableUid, out TabletopGameComponent? tabletop) || tabletop.Session is not {} session) - return; - - // Check if player is actually playing at this table - if (!session.Players.ContainsKey(playerSession)) - return; - - if (!CanSeeTable(playerEntity, msg.TableUid) || !CanDrag(playerEntity, msg.MovedEntityUid, out _)) - return; - - // TODO: some permission system, disallow movement if you're not permitted to move the item - - // Move the entity and dirty it (we use the map ID from the entity so noone can try to be funny and move the item to another map) - var transform = EntityManager.GetComponent(msg.MovedEntityUid); - var entityCoordinates = new EntityCoordinates(_mapManager.GetMapEntityId(transform.MapID), msg.Coordinates.Position); - transform.Coordinates = entityCoordinates; - } - - private void OnDraggingPlayerChanged(TabletopDraggingPlayerChangedEvent msg, EntitySessionEventArgs args) - { - var dragged = msg.DraggedEntityUid; - - if (!EntityManager.TryGetComponent(dragged, out var draggableComponent)) return; - - draggableComponent.DraggingPlayer = msg.IsDragging ? args.SenderSession.UserId : null; - Dirty(draggableComponent); - - if (!EntityManager.TryGetComponent(dragged, out var appearance)) return; - - if (draggableComponent.DraggingPlayer != null) - { - appearance.SetData(TabletopItemVisuals.Scale, new Vector2(1.25f, 1.25f)); - appearance.SetData(TabletopItemVisuals.DrawDepth, (int) DrawDepth.Items + 1); - } - else - { - appearance.SetData(TabletopItemVisuals.Scale, Vector2.One); - appearance.SetData(TabletopItemVisuals.DrawDepth, (int) DrawDepth.Items); - } - } - - private void GetDraggableState(EntityUid uid, TabletopDraggableComponent component, ref ComponentGetState args) - { - args.State = new TabletopDraggableComponentState(component.DraggingPlayer); - } - } -} diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index 29488f4c1d..72d470f224 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -19,6 +19,7 @@ namespace Content.Server.Tabletop public override void Initialize() { + base.Initialize(); SubscribeNetworkEvent(OnStopPlaying); SubscribeLocalEvent(OnTabletopActivate); SubscribeLocalEvent(OnGameShutdown); @@ -27,7 +28,21 @@ namespace Content.Server.Tabletop SubscribeLocalEvent>(AddPlayGameVerb); InitializeMap(); - InitializeDraggable(); + } + + protected override void OnTabletopMove(TabletopMoveEvent msg, EntitySessionEventArgs args) + { + if (args.SenderSession is not IPlayerSession playerSession) + return; + + if (!TryComp(msg.TableUid, out TabletopGameComponent? tabletop) || tabletop.Session is not { } session) + return; + + // Check if player is actually playing at this table + if (!session.Players.ContainsKey(playerSession)) + return; + + base.OnTabletopMove(msg, args); } /// diff --git a/Content.Shared/Tabletop/SharedTabletopSystem.cs b/Content.Shared/Tabletop/SharedTabletopSystem.cs index b3a713902b..59ec7897ce 100644 --- a/Content.Shared/Tabletop/SharedTabletopSystem.cs +++ b/Content.Shared/Tabletop/SharedTabletopSystem.cs @@ -2,7 +2,11 @@ using Content.Shared.ActionBlocker; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Tabletop.Components; +using Content.Shared.Tabletop.Events; +using Robust.Shared.GameStates; +using Robust.Shared.Map; using Robust.Shared.Network; +using Robust.Shared.Players; using Robust.Shared.Serialization; using System.Diagnostics.CodeAnalysis; @@ -10,8 +14,66 @@ namespace Content.Shared.Tabletop { public abstract class SharedTabletopSystem : EntitySystem { - [Dependency] protected readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] protected readonly ActionBlockerSystem ActionBlockerSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedTransformSystem _transforms = default!; + [Dependency] private readonly IMapManager _mapMan = default!; + + public override void Initialize() + { + SubscribeLocalEvent(GetDraggableState); + SubscribeAllEvent(OnDraggingPlayerChanged); + SubscribeAllEvent(OnTabletopMove); + } + + /// + /// Move an entity which is dragged by the user, but check if they are allowed to do so and to these coordinates + /// + protected virtual void OnTabletopMove(TabletopMoveEvent msg, EntitySessionEventArgs args) + { + if (args.SenderSession is not { AttachedEntity: { } playerEntity } playerSession) + return; + + if (!CanSeeTable(playerEntity, msg.TableUid) || !CanDrag(playerEntity, msg.MovedEntityUid, out _)) + return; + + // Move the entity and dirty it (we use the map ID from the entity so noone can try to be funny and move the item to another map) + var transform = EntityManager.GetComponent(msg.MovedEntityUid); + _transforms.SetParent(transform, _mapMan.GetMapEntityId(transform.MapID)); + _transforms.SetLocalPositionNoLerp(transform, msg.Coordinates.Position); + } + + private void GetDraggableState(EntityUid uid, TabletopDraggableComponent component, ref ComponentGetState args) + { + args.State = new TabletopDraggableComponentState(component.DraggingPlayer); + } + + private void OnDraggingPlayerChanged(TabletopDraggingPlayerChangedEvent msg, EntitySessionEventArgs args) + { + var dragged = msg.DraggedEntityUid; + + if (!TryComp(dragged, out TabletopDraggableComponent? draggableComponent)) + return; + + draggableComponent.DraggingPlayer = msg.IsDragging ? args.SenderSession.UserId : null; + Dirty(draggableComponent); + + if (!TryComp(dragged, out AppearanceComponent? appearance)) + return; + + if (draggableComponent.DraggingPlayer != null) + { + _appearance.SetData(dragged, TabletopItemVisuals.Scale, new Vector2(1.25f, 1.25f), appearance); + _appearance.SetData(dragged, TabletopItemVisuals.DrawDepth, (int) DrawDepth.DrawDepth.Items + 1, appearance); + } + else + { + _appearance.SetData(dragged, TabletopItemVisuals.Scale, Vector2.One, appearance); + _appearance.SetData(dragged, TabletopItemVisuals.DrawDepth, (int) DrawDepth.DrawDepth.Items, appearance); + } + } + [Serializable, NetSerializable] public sealed class TabletopDraggableComponentState : ComponentState @@ -41,7 +103,7 @@ namespace Content.Shared.Tabletop return false; } - return _interactionSystem.InRangeUnobstructed(playerEntity, table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table); + return _interactionSystem.InRangeUnobstructed(playerEntity, table.Value) && ActionBlockerSystem.CanInteract(playerEntity, table); } protected bool CanDrag(EntityUid playerEntity, EntityUid target, [NotNullWhen(true)] out TabletopDraggableComponent? draggable) @@ -51,7 +113,7 @@ namespace Content.Shared.Tabletop // CanSeeTable checks interaction action blockers. So no need to check them here. // If this ever changes, so that ghosts can spectate games, then the check needs to be moved here. - + return TryComp(playerEntity, out SharedHandsComponent? hands) && hands.Hands.Count > 0; } #endregion