Predict virtual hands and co (#36617)

These are the easy ones anything else gets slightly spicier.
This commit is contained in:
metalgearsloth
2025-04-19 16:51:12 +10:00
committed by GitHub
parent 63dfd21b14
commit 4682149e74
5 changed files with 23 additions and 52 deletions

View File

@@ -51,7 +51,6 @@ namespace Content.Shared.Interaction
public abstract partial class SharedInteractionSystem : EntitySystem public abstract partial class SharedInteractionSystem : EntitySystem
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
@@ -223,24 +222,24 @@ namespace Content.Shared.Interaction
{ {
if (!item.DeleteOnDrop) if (!item.DeleteOnDrop)
RemCompDeferred<UnremoveableComponent>(uid); RemCompDeferred<UnremoveableComponent>(uid);
else if (_net.IsServer) else
QueueDel(uid); PredictedQueueDel(uid);
} }
private void OnUnequipHand(EntityUid uid, UnremoveableComponent item, GotUnequippedHandEvent args) private void OnUnequipHand(EntityUid uid, UnremoveableComponent item, GotUnequippedHandEvent args)
{ {
if (!item.DeleteOnDrop) if (!item.DeleteOnDrop)
RemCompDeferred<UnremoveableComponent>(uid); RemCompDeferred<UnremoveableComponent>(uid);
else if (_net.IsServer) else
QueueDel(uid); PredictedQueueDel(uid);
} }
private void OnDropped(EntityUid uid, UnremoveableComponent item, DroppedEvent args) private void OnDropped(EntityUid uid, UnremoveableComponent item, DroppedEvent args)
{ {
if (!item.DeleteOnDrop) if (!item.DeleteOnDrop)
RemCompDeferred<UnremoveableComponent>(uid); RemCompDeferred<UnremoveableComponent>(uid);
else if (_net.IsServer) else
QueueDel(uid); PredictedQueueDel(uid);
} }
private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid)

View File

@@ -157,11 +157,6 @@ public abstract class SharedVirtualItemSystem : EntitySystem
/// </summary> /// </summary>
public void DeleteInHandsMatching(EntityUid user, EntityUid matching) public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
{ {
// Client can't currently predict deleting networked entities so we use this workaround, another
// problem can popup when the hands leave PVS for example and this avoids that too
if (_netManager.IsClient)
return;
foreach (var hand in _handsSystem.EnumerateHands(user)) foreach (var hand in _handsSystem.EnumerateHands(user))
{ {
if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt) && virt.BlockingEntity == matching) if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
@@ -206,11 +201,6 @@ public abstract class SharedVirtualItemSystem : EntitySystem
/// <param name="slotName">Set this param if you have the name of the slot, it avoids unnecessary queries</param> /// <param name="slotName">Set this param if you have the name of the slot, it avoids unnecessary queries</param>
public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null) public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null)
{ {
// Client can't currently predict deleting networked entities so we use this workaround, another
// problem can popup when the hands leave PVS for example and this avoids that too
if (_netManager.IsClient)
return;
if (slotName != null) if (slotName != null)
{ {
if (!_inventorySystem.TryGetSlotEntity(user, slotName, out var slotEnt)) if (!_inventorySystem.TryGetSlotEntity(user, slotName, out var slotEnt))
@@ -244,14 +234,8 @@ public abstract class SharedVirtualItemSystem : EntitySystem
/// <param name="virtualItem">The virtual item, if spawned</param> /// <param name="virtualItem">The virtual item, if spawned</param>
public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem) public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
{ {
if (_netManager.IsClient)
{
virtualItem = null;
return false;
}
var pos = Transform(user).Coordinates; var pos = Transform(user).Coordinates;
virtualItem = Spawn(VirtualItem, pos); virtualItem = PredictedSpawnAttachedTo(VirtualItem, pos);
var virtualItemComp = Comp<VirtualItemComponent>(virtualItem.Value); var virtualItemComp = Comp<VirtualItemComponent>(virtualItem.Value);
virtualItemComp.BlockingEntity = blockingEnt; virtualItemComp.BlockingEntity = blockingEnt;
Dirty(virtualItem.Value, virtualItemComp); Dirty(virtualItem.Value, virtualItemComp);
@@ -273,7 +257,6 @@ public abstract class SharedVirtualItemSystem : EntitySystem
return; return;
_transformSystem.DetachEntity(item, Transform(item)); _transformSystem.DetachEntity(item, Transform(item));
if (_netManager.IsServer) PredictedQueueDel(item);
QueueDel(item);
} }
} }

View File

@@ -58,13 +58,7 @@ public sealed partial class EncryptionKeySystem : EntitySystem
_hands.PickupOrDrop(args.User, ent, dropNear: true); _hands.PickupOrDrop(args.User, ent, dropNear: true);
} }
if (!_timing.IsFirstTimePredicted) _popup.PopupPredicted(Loc.GetString("encryption-keys-all-extracted"), uid, args.User);
return;
// TODO add predicted pop-up overrides.
if (_net.IsServer)
_popup.PopupEntity(Loc.GetString("encryption-keys-all-extracted"), uid, args.User);
_audio.PlayPredicted(component.KeyExtractionSound, uid, args.User); _audio.PlayPredicted(component.KeyExtractionSound, uid, args.User);
} }

View File

@@ -74,8 +74,7 @@ public sealed class TechnologyDiskSystem : EntitySystem
} }
} }
_popup.PopupClient(Loc.GetString("tech-disk-inserted"), target, args.User); _popup.PopupClient(Loc.GetString("tech-disk-inserted"), target, args.User);
if (_net.IsServer) PredictedQueueDel(ent);
QueueDel(ent);
args.Handled = true; args.Handled = true;
} }

View File

@@ -21,6 +21,7 @@ using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components; using Content.Shared.Wieldable.Components;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Collections;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -260,26 +261,21 @@ public abstract class SharedWieldableSystem : EntitySystem
_audio.PlayPredicted(component.WieldSound, used, user); _audio.PlayPredicted(component.WieldSound, used, user);
//This section handles spawning the virtual item(s) to occupy the required additional hand(s). //This section handles spawning the virtual item(s) to occupy the required additional hand(s).
//Since the client can't currently predict entity spawning, only do this if this is running serverside. var virtuals = new ValueList<EntityUid>();
//Remove this check if TrySpawnVirtualItem in SharedVirtualItemSystem is allowed to complete clientside. for (var i = 0; i < component.FreeHandsRequired; i++)
if (_netManager.IsServer)
{ {
var virtuals = new List<EntityUid>(); if (_virtualItem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true))
for (var i = 0; i < component.FreeHandsRequired; i++)
{ {
if (_virtualItem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true)) virtuals.Add(virtualItem.Value);
{ continue;
virtuals.Add(virtualItem.Value);
continue;
}
foreach (var existingVirtual in virtuals)
{
QueueDel(existingVirtual);
}
return false;
} }
foreach (var existingVirtual in virtuals)
{
QueueDel(existingVirtual);
}
return false;
} }
var selfMessage = Loc.GetString("wieldable-component-successful-wield", ("item", used)); var selfMessage = Loc.GetString("wieldable-component-successful-wield", ("item", used));