From d228f971b0404a025062bd106ab66a11ef3cf971 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 13 May 2023 02:02:50 +1200 Subject: [PATCH] Try fix collision mispredicts (#16298) --- .../Physics/Controllers/MoverController.cs | 83 +++++----- .../Weapons/Ranged/Systems/TetherGunSystem.cs | 44 +++--- .../CardboardBox/CardboardBoxSystem.cs | 3 +- .../Mech/EntitySystems/SharedMechSystem.cs | 3 +- .../MovementRelayTargetComponent.cs | 16 +- .../Components/RelayInputMoverComponent.cs | 8 +- .../Movement/Systems/SharedJetpackSystem.cs | 3 +- .../Systems/SharedMoverController.Input.cs | 11 +- .../Systems/SharedMoverController.Relay.cs | 148 ++++++------------ .../Movement/Systems/SharedMoverController.cs | 26 ++- Content.Shared/Vehicle/SharedVehicleSystem.cs | 3 +- 11 files changed, 134 insertions(+), 214 deletions(-) diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index 32c6247f05..0a0d22e68f 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -2,11 +2,10 @@ using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; using Content.Shared.Pulling.Components; using Robust.Client.GameObjects; +using Robust.Client.Physics; using Robust.Client.Player; -using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Timing; -using Robust.Shared.Utility; namespace Content.Client.Physics.Controllers { @@ -22,16 +21,51 @@ namespace Content.Client.Physics.Controllers SubscribeLocalEvent(OnRelayPlayerDetached); SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); + + SubscribeLocalEvent(OnUpdatePredicted); + SubscribeLocalEvent(OnUpdateRelayTargetPredicted); + SubscribeLocalEvent(OnUpdatePullablePredicted); + } + + private void OnUpdatePredicted(EntityUid uid, InputMoverComponent component, ref UpdateIsPredictedEvent args) + { + // Enable prediction if an entity is controlled by the player + if (uid == _playerManager.LocalPlayer?.ControlledEntity) + args.IsPredicted = true; + } + + private void OnUpdateRelayTargetPredicted(EntityUid uid, MovementRelayTargetComponent component, ref UpdateIsPredictedEvent args) + { + if (component.Source == _playerManager.LocalPlayer?.ControlledEntity) + args.IsPredicted = true; + } + + private void OnUpdatePullablePredicted(EntityUid uid, SharedPullableComponent component, ref UpdateIsPredictedEvent args) + { + // Enable prediction if an entity is being pulled by the player. + // Disable prediction if an entity is being pulled by some non-player entity. + + if (component.Puller == _playerManager.LocalPlayer?.ControlledEntity) + args.IsPredicted = true; + else if (component.Puller != null) + args.BlockPrediction = true; + + // TODO recursive pulling checks? + // What if the entity is being pulled by a vehicle controlled by the player? } private void OnRelayPlayerAttached(EntityUid uid, RelayInputMoverComponent component, PlayerAttachedEvent args) { + Physics.UpdateIsPredicted(uid); + Physics.UpdateIsPredicted(component.RelayEntity); if (TryComp(component.RelayEntity, out var inputMover)) SetMoveInput(inputMover, MoveButtons.None); } private void OnRelayPlayerDetached(EntityUid uid, RelayInputMoverComponent component, PlayerDetachedEvent args) { + Physics.UpdateIsPredicted(uid); + Physics.UpdateIsPredicted(component.RelayEntity); if (TryComp(component.RelayEntity, out var inputMover)) SetMoveInput(inputMover, MoveButtons.None); } @@ -53,12 +87,8 @@ namespace Content.Client.Physics.Controllers if (_playerManager.LocalPlayer?.ControlledEntity is not {Valid: true} player) return; - if (TryComp(player, out var relayMover) - && TryComp(relayMover.RelayEntity, out MovementRelayTargetComponent? targetComp)) - { - DebugTools.Assert(targetComp.Entities.Count <= 1, "Multiple relayed movers are not supported at the moment"); - HandleClientsideMovement(relayMover.RelayEntity.Value, frameTime); - } + if (TryComp(player, out var relayMover)) + HandleClientsideMovement(relayMover.RelayEntity, frameTime); HandleClientsideMovement(player, frameTime); } @@ -70,7 +100,6 @@ namespace Content.Client.Physics.Controllers var relayTargetQuery = GetEntityQuery(); var mobMoverQuery = GetEntityQuery(); var pullableQuery = GetEntityQuery(); - var physicsQuery = GetEntityQuery(); var modifierQuery = GetEntityQuery(); if (!moverQuery.TryGetComponent(player, out var mover) || @@ -98,42 +127,6 @@ namespace Content.Client.Physics.Controllers return; } - // Essentially we only want to set our mob to predicted so every other entity we just interpolate - // (i.e. only see what the server has sent us). - // The exception to this is joints. - body.Predict = true; - - // We set joints to predicted given these can affect how our mob moves. - // I would only recommend disabling this if you make pulling not use joints anymore (someday maybe?) - - if (TryComp(player, out JointComponent? jointComponent)) - { - foreach (var joint in jointComponent.GetJoints.Values) - { - if (physicsQuery.TryGetComponent(joint.BodyAUid, out var physics)) - physics.Predict = true; - - if (physicsQuery.TryGetComponent(joint.BodyBUid, out physics)) - physics.Predict = true; - } - } - - // If we're being pulled then we won't predict anything and will receive server lerps so it looks way smoother. - if (pullableQuery.TryGetComponent(player, out var pullableComp)) - { - if (pullableComp.Puller is {Valid: true} puller && TryComp(puller, out var pullerBody)) - { - pullerBody.Predict = false; - body.Predict = false; - - if (TryComp(player, out var playerPuller) && playerPuller.Pulling != null && - physicsQuery.TryGetComponent(playerPuller.Pulling, out var pulledBody)) - { - pulledBody.Predict = false; - } - } - } - // Server-side should just be handled on its own so we'll just do this shizznit HandleMobMovement( player, diff --git a/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs b/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs index 482643076b..3a66f07bc9 100644 --- a/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs @@ -1,9 +1,9 @@ -using Content.Client.Clickable; using Content.Client.Gameplay; using Content.Shared.Weapons.Ranged.Systems; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; +using Robust.Client.Physics; using Robust.Client.State; using Robust.Shared.Input; using Robust.Shared.Map; @@ -18,6 +18,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly InputSystem _inputSystem = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; public bool Enabled { get; set; } @@ -34,6 +35,13 @@ public sealed class TetherGunSystem : SharedTetherGunSystem base.Initialize(); SubscribeNetworkEvent(OnPredictTether); SubscribeNetworkEvent(OnTetherGun); + SubscribeLocalEvent(OnUpdatePrediction); + } + + private void OnUpdatePrediction(ref UpdateIsPredictedEvent ev) + { + if (ev.Uid == _dragging || ev.Uid == _tether) + ev.IsPredicted = true; } private void OnTetherGun(TetherGunToggleMessage ev) @@ -43,22 +51,13 @@ public sealed class TetherGunSystem : SharedTetherGunSystem private void OnPredictTether(PredictTetherEvent ev) { - if (_dragging != ev.Entity) return; + if (_dragging != ev.Entity || _tether == ev.Entity) + return; + var oldTether = _tether; _tether = ev.Entity; - } - - public override void FrameUpdate(float frameTime) - { - base.FrameUpdate(frameTime); - if (!TryComp(_dragging, out var body)) return; - - body.Predict = true; - - if (TryComp(_tether, out var tetherBody)) - { - tetherBody.Predict = true; - } + _physics.UpdateIsPredicted(oldTether); + _physics.UpdateIsPredicted(_tether); } public override void Update(float frameTime) @@ -102,13 +101,6 @@ public sealed class TetherGunSystem : SharedTetherGunSystem return; } - body.Predict = true; - - if (TryComp(_tether, out var tetherBody)) - { - tetherBody.Predict = true; - } - if (_lastMousePosition.Value.Position.EqualsApprox(mousePos.Position)) return; _lastMousePosition = mousePos; @@ -123,10 +115,15 @@ public sealed class TetherGunSystem : SharedTetherGunSystem { if (_dragging == null) return; + var oldDrag = _dragging; + var oldTether = _tether; RaiseNetworkEvent(new StopTetherEvent()); _dragging = null; _lastMousePosition = null; _tether = null; + + _physics.UpdateIsPredicted(oldDrag); + _physics.UpdateIsPredicted(oldTether); } private void StartDragging(EntityUid uid, MapCoordinates coordinates) @@ -138,5 +135,8 @@ public sealed class TetherGunSystem : SharedTetherGunSystem Entity = _dragging!.Value, Coordinates = coordinates, }); + + _physics.UpdateIsPredicted(uid); + } } diff --git a/Content.Server/CardboardBox/CardboardBoxSystem.cs b/Content.Server/CardboardBox/CardboardBoxSystem.cs index 0e0c171f02..e7a0c8f973 100644 --- a/Content.Server/CardboardBox/CardboardBoxSystem.cs +++ b/Content.Server/CardboardBox/CardboardBoxSystem.cs @@ -100,8 +100,7 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem RemComp(component.Mover.Value); } - var relay = EnsureComp(args.Entity); - _mover.SetRelay(args.Entity, uid, relay); + _mover.SetRelay(args.Entity, uid); component.Mover = args.Entity; } diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs index afed3e4fd8..0b5e63297b 100644 --- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs +++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs @@ -169,12 +169,11 @@ public abstract class SharedMechSystem : EntitySystem return; var rider = EnsureComp(pilot); - var relay = EnsureComp(pilot); // Warning: this bypasses most normal interaction blocking components on the user, like drone laws and the like. var irelay = EnsureComp(pilot); - _mover.SetRelay(pilot, mech, relay); + _mover.SetRelay(pilot, mech); _interaction.SetRelay(pilot, mech, irelay); rider.Mech = mech; Dirty(rider); diff --git a/Content.Shared/Movement/Components/MovementRelayTargetComponent.cs b/Content.Shared/Movement/Components/MovementRelayTargetComponent.cs index 80e5c27a5a..dbe5d23f1c 100644 --- a/Content.Shared/Movement/Components/MovementRelayTargetComponent.cs +++ b/Content.Shared/Movement/Components/MovementRelayTargetComponent.cs @@ -1,17 +1,15 @@ +using Content.Shared.Movement.Systems; using Robust.Shared.GameStates; namespace Content.Shared.Movement.Components; -[RegisterComponent, NetworkedComponent] -public sealed class MovementRelayTargetComponent : Component +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +[Access(typeof(SharedMoverController))] +public sealed partial class MovementRelayTargetComponent : Component { - // This really shouldn't be a list at the moment. Its just not supported. - // Neither movement updating, nor HandleDirChange() support more than one mover. - // Its currently possible for the direction to be set by one mover and the relative rotation to be set by a separate unrelated mover. - // AAAAA - /// - /// Entities that are relaying to us. + /// The entity that is relaying to this entity. /// - [ViewVariables] public readonly List Entities = new(); + [ViewVariables, AutoNetworkedField] + public EntityUid Source; } diff --git a/Content.Shared/Movement/Components/RelayInputMoverComponent.cs b/Content.Shared/Movement/Components/RelayInputMoverComponent.cs index aff2290264..c783165cbe 100644 --- a/Content.Shared/Movement/Components/RelayInputMoverComponent.cs +++ b/Content.Shared/Movement/Components/RelayInputMoverComponent.cs @@ -6,10 +6,10 @@ namespace Content.Shared.Movement.Components; /// /// Raises the engine movement inputs for a particular entity onto the designated entity /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] [Access(typeof(SharedMoverController))] -public sealed class RelayInputMoverComponent : Component +public sealed partial class RelayInputMoverComponent : Component { - [ViewVariables] - public EntityUid? RelayEntity; + [ViewVariables, AutoNetworkedField] + public EntityUid RelayEntity; } diff --git a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs index cb18fac036..0a82aa3576 100644 --- a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs +++ b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs @@ -101,8 +101,7 @@ public abstract class SharedJetpackSystem : EntitySystem private void SetupUser(EntityUid uid, JetpackComponent component) { var user = EnsureComp(uid); - var relay = EnsureComp(uid); - _mover.SetRelay(uid, component.Owner, relay); + _mover.SetRelay(uid, component.Owner); user.Jetpack = component.Owner; } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs index fbf0346d7b..087a3ab5eb 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs @@ -274,11 +274,8 @@ namespace Content.Shared.Movement.Systems if (TryComp(entity, out var mover)) SetMoveInput(mover, MoveButtons.None); - DebugTools.Assert(TryComp(relayMover.RelayEntity, out MovementRelayTargetComponent? targetComp) && targetComp.Entities.Count == 1, - "Multiple relayed movers are not supported at the moment"); - - if (relayMover.RelayEntity != null && !_mobState.IsIncapacitated(entity)) - HandleDirChange(relayMover.RelayEntity.Value, dir, subTick, state); + if (!_mobState.IsIncapacitated(entity)) + HandleDirChange(relayMover.RelayEntity, dir, subTick, state); return; } @@ -328,9 +325,7 @@ namespace Content.Shared.Movement.Systems SetMoveInput(moverComp, MoveButtons.None); } - if (relayMover.RelayEntity == null) return; - - HandleRunChange(relayMover.RelayEntity.Value, subTick, walking); + HandleRunChange(relayMover.RelayEntity, subTick, walking); return; } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs index 3f73e11431..320bea0de4 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs @@ -1,7 +1,4 @@ using Content.Shared.Movement.Components; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; -using Robust.Shared.Utility; namespace Content.Shared.Movement.Systems; @@ -9,136 +6,85 @@ public abstract partial class SharedMoverController { private void InitializeRelay() { - SubscribeLocalEvent(OnRelayGetState); - SubscribeLocalEvent(OnRelayHandleState); SubscribeLocalEvent(OnRelayShutdown); - - SubscribeLocalEvent(OnTargetRelayGetState); - SubscribeLocalEvent(OnTargetRelayHandleState); SubscribeLocalEvent(OnTargetRelayShutdown); + SubscribeLocalEvent(OnAfterRelayTargetState); + SubscribeLocalEvent(OnAfterRelayState); + } + + private void OnAfterRelayTargetState(EntityUid uid, MovementRelayTargetComponent component, ref AfterAutoHandleStateEvent args) + { + Physics.UpdateIsPredicted(uid); + } + + private void OnAfterRelayState(EntityUid uid, RelayInputMoverComponent component, ref AfterAutoHandleStateEvent args) + { + Physics.UpdateIsPredicted(uid); } /// /// Sets the relay entity and marks the component as dirty. This only exists because people have previously /// forgotten to Dirty(), so fuck you, you have to use this method now. /// - public void SetRelay(EntityUid uid, EntityUid relayEntity, RelayInputMoverComponent? component = null) + public void SetRelay(EntityUid uid, EntityUid relayEntity) { - if (!Resolve(uid, ref component) || component.RelayEntity == relayEntity) - return; - if (uid == relayEntity) { Logger.Error($"An entity attempted to relay movement to itself. Entity:{ToPrettyString(uid)}"); return; } - if (TryComp(relayEntity, out var targetComp)) - { - targetComp.Entities.Remove(uid); + var component = EnsureComp(uid); + if (component.RelayEntity == relayEntity) + return; - if (targetComp.Entities.Count == 0) - RemComp(relayEntity); + if (TryComp(component.RelayEntity, out MovementRelayTargetComponent? oldTarget)) + { + oldTarget.Source = EntityUid.Invalid; + RemComp(component.RelayEntity, oldTarget); + Physics.UpdateIsPredicted(component.RelayEntity); } + var targetComp = EnsureComp(relayEntity); + if (TryComp(targetComp.Source, out RelayInputMoverComponent? oldRelay)) + { + oldRelay.RelayEntity = EntityUid.Invalid; + RemComp(targetComp.Source, oldRelay); + Physics.UpdateIsPredicted(targetComp.Source); + } + + Physics.UpdateIsPredicted(uid); + Physics.UpdateIsPredicted(relayEntity); component.RelayEntity = relayEntity; - targetComp = EnsureComp(relayEntity); - targetComp.Entities.Add(uid); - DebugTools.Assert(targetComp.Entities.Count <= 1, "Multiple relayed movers are not supported at the moment"); + targetComp.Source = uid; Dirty(component); Dirty(targetComp); } private void OnRelayShutdown(EntityUid uid, RelayInputMoverComponent component, ComponentShutdown args) { - // If relay is removed then cancel all inputs. - if (!TryComp(component.RelayEntity, out var inputMover)) + Physics.UpdateIsPredicted(uid); + Physics.UpdateIsPredicted(component.RelayEntity); + + if (TryComp(component.RelayEntity, out var inputMover)) + SetMoveInput(inputMover, MoveButtons.None); + + if (Timing.ApplyingState) return; - if (TryComp(component.RelayEntity, out var targetComp) && - targetComp.LifeStage < ComponentLifeStage.Stopping) - { - targetComp.Entities.Remove(uid); - - if (targetComp.Entities.Count == 0) - RemCompDeferred(component.RelayEntity.Value); - else - Dirty(targetComp); - } - - SetMoveInput(inputMover, MoveButtons.None); + if (TryComp(component.RelayEntity, out MovementRelayTargetComponent? target) && target.LifeStage <= ComponentLifeStage.Running) + RemComp(component.RelayEntity, target); } - private void OnRelayHandleState(EntityUid uid, RelayInputMoverComponent component, ref ComponentHandleState args) - { - if (args.Current is not RelayInputMoverComponentState state) return; - - DebugTools.Assert(state.Entity != uid); - component.RelayEntity = state.Entity; - } - - private void OnRelayGetState(EntityUid uid, RelayInputMoverComponent component, ref ComponentGetState args) - { - args.State = new RelayInputMoverComponentState() - { - Entity = component.RelayEntity, - }; - } - - #region Target Relay - private void OnTargetRelayShutdown(EntityUid uid, MovementRelayTargetComponent component, ComponentShutdown args) { - if (component.Entities.Count == 0) + Physics.UpdateIsPredicted(uid); + Physics.UpdateIsPredicted(component.Source); + + if (Timing.ApplyingState) return; - var relayQuery = GetEntityQuery(); - - foreach (var ent in component.Entities) - { - if (!relayQuery.TryGetComponent(ent, out var relay)) - continue; - - DebugTools.Assert(relay.RelayEntity == uid); - - if (relay.RelayEntity != uid) - continue; - - RemCompDeferred(ent); - } - } - - private void OnTargetRelayHandleState(EntityUid uid, MovementRelayTargetComponent component, ref ComponentHandleState args) - { - if (args.Current is not MovementRelayTargetComponentState state) - return; - - component.Entities.Clear(); - component.Entities.AddRange(state.Entities); - DebugTools.Assert(component.Entities.Count <= 1, "Multiple relayed movers are not supported at the moment"); - } - - private void OnTargetRelayGetState(EntityUid uid, MovementRelayTargetComponent component, ref ComponentGetState args) - { - args.State = new MovementRelayTargetComponentState(component.Entities); - } - - #endregion - - [Serializable, NetSerializable] - private sealed class RelayInputMoverComponentState : ComponentState - { - public EntityUid? Entity; - } - - [Serializable, NetSerializable] - private sealed class MovementRelayTargetComponentState : ComponentState - { - public List Entities; - - public MovementRelayTargetComponentState(List entities) - { - Entities = entities; - } + if (TryComp(component.Source, out RelayInputMoverComponent? relay) && relay.LifeStage <= ComponentLifeStage.Running) + RemComp(component.Source, relay); } } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 57ee41b718..ba52975105 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -30,6 +30,7 @@ namespace Content.Shared.Movement.Systems { [Dependency] private readonly IConfigurationManager _configManager = default!; [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] protected readonly SharedPhysicsSystem Physics = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly InventorySystem _inventory = default!; @@ -108,25 +109,19 @@ namespace Content.Shared.Movement.Systems EntityQuery modifierQuery) { var canMove = mover.CanMove; - if (relayTargetQuery.TryGetComponent(uid, out var relayTarget) && relayTarget.Entities.Count > 0) + if (relayTargetQuery.TryGetComponent(uid, out var relayTarget)) { - DebugTools.Assert(relayTarget.Entities.Count <= 1, "Multiple relayed movers are not supported at the moment"); - - var found = false; - foreach (var ent in relayTarget.Entities) + if (_mobState.IsIncapacitated(relayTarget.Source) || + !moverQuery.TryGetComponent(relayTarget.Source, out var relayedMover)) + { + canMove = false; + } + else { - if (_mobState.IsIncapacitated(ent) || !moverQuery.TryGetComponent(ent, out var relayedMover)) - continue; - - found = true; mover.RelativeEntity = relayedMover.RelativeEntity; mover.RelativeRotation = relayedMover.RelativeRotation; mover.TargetRelativeRotation = relayedMover.TargetRelativeRotation; - break; } - - // lets just hope that this is the same entity that set the movement keys/direction. - canMove &= found; } // Update relative movement @@ -267,10 +262,7 @@ namespace Content.Shared.Movement.Systems // If we're a relay target then predict the sound for all relays. if (relayTarget != null) { - foreach (var ent in relayTarget.Entities) - { - _audio.PlayPredicted(sound, uid, ent, audioParams); - } + _audio.PlayPredicted(sound, uid, relayTarget.Source, audioParams); } else { diff --git a/Content.Shared/Vehicle/SharedVehicleSystem.cs b/Content.Shared/Vehicle/SharedVehicleSystem.cs index d299945a21..7193cdd613 100644 --- a/Content.Shared/Vehicle/SharedVehicleSystem.cs +++ b/Content.Shared/Vehicle/SharedVehicleSystem.cs @@ -121,8 +121,7 @@ public abstract partial class SharedVehicleSystem : EntitySystem Dirty(component); Appearance.SetData(uid, VehicleVisuals.HideRider, true); - var relay = EnsureComp(args.BuckledEntity); - _mover.SetRelay(args.BuckledEntity, uid, relay); + _mover.SetRelay(args.BuckledEntity, uid); rider.Vehicle = uid; // Update appearance stuff, add actions