Fix some tabletop prediction jank (#12758)

This commit is contained in:
Leon Friedrich
2022-11-27 23:25:54 +13:00
committed by GitHub
parent 2dc7663d1a
commit 8467d2373c
4 changed files with 91 additions and 96 deletions

View File

@@ -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<TabletopSystem>();
SubscribeNetworkEvent<TabletopPlayEvent>(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
/// <param name="viewport">The viewport in which we are dragging.</param>
private void StartDragging(EntityUid draggedEntity, ScalingViewport viewport)
{
RaiseNetworkEvent(new TabletopDraggingPlayerChangedEvent(draggedEntity, true));
if (EntityManager.TryGetComponent<AppearanceComponent>(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<TabletopDraggableComponent>(_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;

View File

@@ -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<TabletopMoveEvent>(OnTabletopMove);
SubscribeNetworkEvent<TabletopDraggingPlayerChangedEvent>(OnDraggingPlayerChanged);
SubscribeLocalEvent<TabletopDraggableComponent, ComponentGetState>(GetDraggableState);
}
/// <summary>
/// Move an entity which is dragged by the user, but check if they are allowed to do so and to these coordinates
/// </summary>
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<TransformComponent>(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<TabletopDraggableComponent?>(dragged, out var draggableComponent)) return;
draggableComponent.DraggingPlayer = msg.IsDragging ? args.SenderSession.UserId : null;
Dirty(draggableComponent);
if (!EntityManager.TryGetComponent<AppearanceComponent?>(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);
}
}
}

View File

@@ -19,6 +19,7 @@ namespace Content.Server.Tabletop
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<TabletopStopPlayingEvent>(OnStopPlaying);
SubscribeLocalEvent<TabletopGameComponent, ActivateInWorldEvent>(OnTabletopActivate);
SubscribeLocalEvent<TabletopGameComponent, ComponentShutdown>(OnGameShutdown);
@@ -27,7 +28,21 @@ namespace Content.Server.Tabletop
SubscribeLocalEvent<TabletopGameComponent, GetVerbsEvent<ActivationVerb>>(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);
}
/// <summary>

View File

@@ -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<TabletopDraggableComponent, ComponentGetState>(GetDraggableState);
SubscribeAllEvent<TabletopDraggingPlayerChangedEvent>(OnDraggingPlayerChanged);
SubscribeAllEvent<TabletopMoveEvent>(OnTabletopMove);
}
/// <summary>
/// Move an entity which is dragged by the user, but check if they are allowed to do so and to these coordinates
/// </summary>
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<TransformComponent>(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)