diff --git a/Content.Server/Storage/Components/PickRandomComponent.cs b/Content.Shared/Storage/Components/PickRandomComponent.cs similarity index 69% rename from Content.Server/Storage/Components/PickRandomComponent.cs rename to Content.Shared/Storage/Components/PickRandomComponent.cs index 00c79b9ea4..e243d25dde 100644 --- a/Content.Server/Storage/Components/PickRandomComponent.cs +++ b/Content.Shared/Storage/Components/PickRandomComponent.cs @@ -1,31 +1,32 @@ -using Content.Server.Storage.EntitySystems; +using Content.Shared.Storage.EntitySystems; using Content.Shared.Whitelist; +using Robust.Shared.GameStates; -namespace Content.Server.Storage.Components; +namespace Content.Shared.Storage.Components; /// /// Adds a verb to pick a random item from a container. /// Only picks items that match the whitelist. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(PickRandomSystem))] public sealed partial class PickRandomComponent : Component { /// /// Whitelist for potential picked items. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public EntityWhitelist? Whitelist; /// /// Locale id for the pick verb text. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public LocId VerbText = "comp-pick-random-verb-text"; /// /// Locale id for the empty storage message. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public LocId EmptyText = "comp-pick-random-empty"; } diff --git a/Content.Server/Storage/EntitySystems/PickRandomSystem.cs b/Content.Shared/Storage/EntitySystems/PickRandomSystem.cs similarity index 78% rename from Content.Server/Storage/EntitySystems/PickRandomSystem.cs rename to Content.Shared/Storage/EntitySystems/PickRandomSystem.cs index f0e986e199..87fb4219ad 100644 --- a/Content.Server/Storage/EntitySystems/PickRandomSystem.cs +++ b/Content.Shared/Storage/EntitySystems/PickRandomSystem.cs @@ -1,22 +1,22 @@ using System.Linq; -using Content.Server.Storage.Components; using Content.Shared.Database; using Content.Shared.Hands.EntitySystems; -using Content.Shared.Storage; +using Content.Shared.Storage.Components; using Content.Shared.Verbs; using Content.Shared.Whitelist; using Robust.Shared.Containers; +using Robust.Shared.Network; using Robust.Shared.Random; -namespace Content.Server.Storage.EntitySystems; +namespace Content.Shared.Storage.EntitySystems; -// TODO: move this to shared for verb prediction if/when storage is in shared public sealed class PickRandomSystem : EntitySystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -50,12 +50,19 @@ public sealed class PickRandomSystem : EntitySystem private void TryPick(EntityUid uid, PickRandomComponent comp, StorageComponent storage, EntityUid user) { + // It's hard to predict picking a random entity from a container since the contained entity list will have a different order on the server and client. + // One idea might be to sort them by NetEntity ID, but that is expensive if there are a lot of entities. + // Another option would be to make this client authorative. + if (_net.IsClient) + return; + var entities = storage.Container.ContainedEntities.Where(item => _whitelistSystem.IsWhitelistPassOrNull(comp.Whitelist, item)).ToArray(); - if (!entities.Any()) + if (entities.Length == 0) return; var picked = _random.Pick(entities); + // if it fails to go into a hand of the user, will be on the storage _container.AttachParentToContainerOrGrid((picked, Transform(picked)));