diff --git a/Content.Server/Construction/Completions/SetStackCount.cs b/Content.Server/Construction/Completions/SetStackCount.cs index 82f6683a06..888f927fae 100644 --- a/Content.Server/Construction/Completions/SetStackCount.cs +++ b/Content.Server/Construction/Completions/SetStackCount.cs @@ -16,14 +16,14 @@ namespace Content.Server.Construction.Completions [DataDefinition] public class SetStackCount : IGraphAction { - [DataField("amount")] public int Amount { get; private set; } = 1; + [DataField("amount")] public int Amount { get; } = 1; public async Task PerformAction(IEntity entity, IEntity? user) { if (entity.Deleted) return; - if(!entity.HasComponent()) return; + if(!entity.TryGetComponent(out var stack)) return; - entity.EntityManager.EventBus.RaiseLocalEvent(entity.Uid, new StackChangeCountEvent(Amount), false); + EntitySystem.Get().SetCount(entity.Uid, stack, Amount); } } } diff --git a/Content.Server/Construction/Completions/SpawnPrototype.cs b/Content.Server/Construction/Completions/SpawnPrototype.cs index f1b7e3b1f2..5377d22c08 100644 --- a/Content.Server/Construction/Completions/SpawnPrototype.cs +++ b/Content.Server/Construction/Completions/SpawnPrototype.cs @@ -29,8 +29,9 @@ namespace Content.Server.Construction.Completions if (EntityPrototypeHelpers.HasComponent(Prototype)) { - var stack = entityManager.SpawnEntity(Prototype, coordinates); - stack.EntityManager.EventBus.RaiseLocalEvent(stack.Uid, new StackChangeCountEvent(Amount), false); + var stackEnt = entityManager.SpawnEntity(Prototype, coordinates); + var stack = stackEnt.GetComponent(); + EntitySystem.Get().SetCount(stackEnt.Uid, stack, Amount); } else { diff --git a/Content.Server/Construction/Components/ConstructionComponent.cs b/Content.Server/Construction/Components/ConstructionComponent.cs index 6885169dd4..0f61c0f76d 100644 --- a/Content.Server/Construction/Components/ConstructionComponent.cs +++ b/Content.Server/Construction/Components/ConstructionComponent.cs @@ -269,12 +269,11 @@ namespace Content.Server.Construction.Components if (materialStep.EntityValid(eventArgs.Using, out var stack) && await doAfterSystem.DoAfter(doAfterArgs) == DoAfterStatus.Finished) { - var splitStack = new StackSplitEvent() {Amount = materialStep.Amount, SpawnPosition = eventArgs.User.Transform.Coordinates}; - Owner.EntityManager.EventBus.RaiseLocalEvent(stack.Owner.Uid, splitStack); + var splitStack = EntitySystem.Get().Split(eventArgs.Using.Uid, stack, materialStep.Amount, eventArgs.User.Transform.Coordinates); - if (splitStack.Result != null) + if (splitStack != null) { - entityUsing = splitStack.Result; + entityUsing = splitStack; valid = true; } } diff --git a/Content.Server/Construction/Components/MachineComponent.cs b/Content.Server/Construction/Components/MachineComponent.cs index de3b870287..28c49906b4 100644 --- a/Content.Server/Construction/Components/MachineComponent.cs +++ b/Content.Server/Construction/Components/MachineComponent.cs @@ -86,16 +86,12 @@ namespace Content.Server.Construction.Components foreach (var (stackType, amount) in machineBoard.MaterialRequirements) { - var stackSpawn = new StackTypeSpawnEvent() - {Amount = amount, StackType = stackType, SpawnPosition = Owner.Transform.Coordinates}; - Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, stackSpawn); + var stack = EntitySystem.Get().Spawn(amount, stackType, Owner.Transform.Coordinates); - var s = stackSpawn.Result; - - if (s == null) + if (stack == null) throw new Exception($"Couldn't spawn stack of type {stackType}!"); - if (!partContainer.Insert(s)) + if (!partContainer.Insert(stack)) throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {Owner.Prototype?.ID ?? "N/A"}"); } diff --git a/Content.Server/Construction/Components/MachineFrameComponent.cs b/Content.Server/Construction/Components/MachineFrameComponent.cs index 23c7887f82..6fca7d836d 100644 --- a/Content.Server/Construction/Components/MachineFrameComponent.cs +++ b/Content.Server/Construction/Components/MachineFrameComponent.cs @@ -313,14 +313,12 @@ namespace Content.Server.Construction.Components return true; } - var splitStack = new StackSplitEvent() - {Amount = needed, SpawnPosition = Owner.Transform.Coordinates}; - Owner.EntityManager.EventBus.RaiseLocalEvent(stack.Owner.Uid, splitStack); + var splitStack = EntitySystem.Get().Split(eventArgs.Using.Uid, stack, needed, Owner.Transform.Coordinates); - if (splitStack.Result == null) + if (splitStack == null) return false; - if(!_partContainer.Insert(splitStack.Result)) + if(!_partContainer.Insert(splitStack)) return false; _materialProgress[type] += needed; diff --git a/Content.Server/Construction/Components/WelderRefinableComponent.cs b/Content.Server/Construction/Components/WelderRefinableComponent.cs index 4daad665d1..8c76a1755c 100644 --- a/Content.Server/Construction/Components/WelderRefinableComponent.cs +++ b/Content.Server/Construction/Components/WelderRefinableComponent.cs @@ -52,15 +52,15 @@ namespace Content.Server.Construction.Components var resultPosition = Owner.Transform.Coordinates; Owner.Delete(); - // spawn each result afrer refine + // spawn each result after refine foreach (var result in _refineResult!) { var droppedEnt = Owner.EntityManager.SpawnEntity(result, resultPosition); // TODO: If something has a stack... Just use a prototype with a single thing in the stack. // This is not a good way to do it. - if (droppedEnt.HasComponent()) - Owner.EntityManager.EventBus.RaiseLocalEvent(droppedEnt.Uid, new StackChangeCountEvent(1), false); + if (droppedEnt.TryGetComponent(out var stack)) + EntitySystem.Get().SetCount(droppedEnt.Uid, stack, 1); } return true; diff --git a/Content.Server/Construction/ConstructionSystem.cs b/Content.Server/Construction/ConstructionSystem.cs index 1993ac6725..97860e7975 100644 --- a/Content.Server/Construction/ConstructionSystem.cs +++ b/Content.Server/Construction/ConstructionSystem.cs @@ -170,19 +170,17 @@ namespace Content.Server.Construction if (!materialStep.EntityValid(entity, out var stack)) continue; - var splitStack = new StackSplitEvent() - {Amount = materialStep.Amount, SpawnPosition = user.ToCoordinates()}; - RaiseLocalEvent(entity.Uid, splitStack); + var splitStack = Get().Split(entity.Uid, stack, materialStep.Amount, user.ToCoordinates()); - if (splitStack.Result == null) + if (splitStack == null) continue; if (string.IsNullOrEmpty(materialStep.Store)) { - if (!container.Insert(splitStack.Result)) + if (!container.Insert(splitStack)) continue; } - else if (!GetContainer(materialStep.Store).Insert(splitStack.Result)) + else if (!GetContainer(materialStep.Store).Insert(splitStack)) continue; handled = true; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs index b788be5fda..43c9ccbe15 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs @@ -33,7 +33,8 @@ namespace Content.Server.Destructible.Thresholds.Behaviors if (EntityPrototypeHelpers.HasComponent(entityId)) { var spawned = owner.EntityManager.SpawnEntity(entityId, owner.Transform.MapPosition); - owner.EntityManager.EventBus.RaiseLocalEvent(spawned.Uid, new StackChangeCountEvent(count), false); + var stack = spawned.GetComponent(); + EntitySystem.Get().SetCount(spawned.Uid, stack, count); spawned.RandomOffset(0.5f); } else diff --git a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs index 4e24554bf8..a59a2521cb 100644 --- a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs +++ b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs @@ -5,6 +5,7 @@ using Content.Server.Engineering.Components; using Content.Server.Stack; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; +using Content.Shared.Stacks; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -65,20 +66,15 @@ namespace Content.Server.Engineering.EntitySystems if (component.Deleted || component.Owner.Deleted) return; - var hasStack = component.Owner.HasComponent(); - - if (hasStack && component.RemoveOnInteract) + if (component.Owner.TryGetComponent(out var stackComp) + && component.RemoveOnInteract && !Get().Use(uid, stackComp, 1)) { - var stackUse = new StackUseEvent() {Amount = 1}; - RaiseLocalEvent(component.Owner.Uid, stackUse); - - if (!stackUse.Result) - return; + return; } EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid)); - if (component.RemoveOnInteract && !hasStack && !component.Owner.Deleted) + if (component.RemoveOnInteract && stackComp == null && !component.Owner.Deleted) component.Owner.Delete(); } } diff --git a/Content.Server/Hands/HandsSystem.cs b/Content.Server/Hands/HandsSystem.cs index 728487a79c..8bbf5ca41f 100644 --- a/Content.Server/Hands/HandsSystem.cs +++ b/Content.Server/Hands/HandsSystem.cs @@ -188,13 +188,12 @@ namespace Content.Server.Hands } else { - var splitStack = new StackSplitEvent() { Amount = 1, SpawnPosition = playerEnt.Transform.Coordinates }; - RaiseLocalEvent(throwEnt.Uid, splitStack); + var splitStack = Get().Split(throwEnt.Uid, stackComp, 1, playerEnt.Transform.Coordinates); - if (splitStack.Result == null) + if (splitStack == null) return false; - throwEnt = splitStack.Result; + throwEnt = splitStack; } var direction = coords.ToMapPos(EntityManager) - playerEnt.Transform.WorldPosition; diff --git a/Content.Server/Medical/Components/HealingComponent.cs b/Content.Server/Medical/Components/HealingComponent.cs index c6b94f72c5..58558e7f78 100644 --- a/Content.Server/Medical/Components/HealingComponent.cs +++ b/Content.Server/Medical/Components/HealingComponent.cs @@ -6,6 +6,7 @@ using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; +using Content.Shared.Stacks; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -41,13 +42,9 @@ namespace Content.Server.Medical.Components return true; } - if (Owner.HasComponent()) + if (Owner.TryGetComponent(out var stack) && !EntitySystem.Get().Use(Owner.Uid, stack, 1)) { - var stackUse = new StackUseEvent() {Amount = 1}; - Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, stackUse); - - if(!stackUse.Result) - return true; + return true; } foreach (var (type, amount) in Heal) diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index d32876ce39..a0ac77bdf5 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -25,77 +25,63 @@ namespace Content.Server.Stack base.Initialize(); SubscribeLocalEvent(OnStackInteractUsing); - - // The following subscriptions are basically the "method calls" of this entity system. - SubscribeLocalEvent(OnStackUse); - SubscribeLocalEvent(OnStackSplit); - SubscribeLocalEvent(OnStackTypeSpawn); } /// - /// Try to use an amount of items on this stack. - /// See + /// Try to use an amount of items on this stack. Returns whether this succeeded. /// - private void OnStackUse(EntityUid uid, StackComponent stack, StackUseEvent args) + public bool Use(EntityUid uid, SharedStackComponent stack, int amount) { // Check if we have enough things in the stack for this... - if (stack.Count < args.Amount) + if (stack.Count <= amount) { - // Not enough things in the stack, so we set the output result to false. - args.Result = false; - } - else - { - // We do have enough things in the stack, so remove them and set the output result to true. - RaiseLocalEvent(uid, new StackChangeCountEvent(stack.Count - args.Amount), false); - args.Result = true; + // Not enough things in the stack, return false. + return false; } + + // We do have enough things in the stack, so remove them and change. + SetCount(uid, stack, stack.Count - amount); + return true; } /// - /// Try to split this stack into two. - /// See + /// Try to split this stack into two. Returns a non-null if successful. /// - private void OnStackSplit(EntityUid uid, StackComponent stack, StackSplitEvent args) + public IEntity? Split(EntityUid uid, SharedStackComponent stack, int amount, EntityCoordinates spawnPosition) { - // If the stack doesn't have enough things as specified in the parameters, we do nothing. - if (stack.Count < args.Amount) - return; - // Get a prototype ID to spawn the new entity. Null is also valid, although it should rarely be picked... var prototype = _prototypeManager.TryIndex(stack.StackTypeId, out var stackType) ? stackType.Spawn : stack.Owner.Prototype?.ID ?? null; - // Remove the amount of things we want to split from the original stack... - RaiseLocalEvent(uid, new StackChangeCountEvent(stack.Count - args.Amount), false); + // Try to remove the amount of things we want to split from the original stack... + if (!Use(uid, stack, amount)) + return null; // Set the output parameter in the event instance to the newly split stack. - args.Result = EntityManager.SpawnEntity(prototype, args.SpawnPosition); + var entity = EntityManager.SpawnEntity(prototype, spawnPosition); - if (args.Result.TryGetComponent(out StackComponent? stackComp)) + if (ComponentManager.TryGetComponent(entity.Uid, out SharedStackComponent? stackComp)) { // Set the split stack's count. - RaiseLocalEvent(args.Result.Uid, new StackChangeCountEvent(args.Amount), false); + SetCount(entity.Uid, stackComp, amount); } + + return entity; } /// - /// Tries to spawn a stack of a certain type. - /// See + /// Spawns a stack of a certain stack type. See . /// - private void OnStackTypeSpawn(StackTypeSpawnEvent args) + public IEntity Spawn(int amount, StackPrototype prototype, EntityCoordinates spawnPosition) { - // Can't spawn a stack for an invalid type. - if (args.StackType == null) - return; - // Set the output result parameter to the new stack entity... - args.Result = EntityManager.SpawnEntity(args.StackType.Spawn, args.SpawnPosition); - var stack = args.Result.GetComponent(); + var entity = EntityManager.SpawnEntity(prototype.Spawn, spawnPosition); + var stack = ComponentManager.GetComponent(entity.Uid); // And finally, set the correct amount! - RaiseLocalEvent(args.Result.Uid, new StackChangeCountEvent(args.Amount), false); + SetCount(entity.Uid, stack, amount); + return entity; } private void OnStackInteractUsing(EntityUid uid, StackComponent stack, InteractUsingEvent args) @@ -107,8 +93,8 @@ namespace Content.Server.Stack return; var toTransfer = Math.Min(stack.Count, otherStack.AvailableSpace); - RaiseLocalEvent(uid, new StackChangeCountEvent(stack.Count - toTransfer), false); - RaiseLocalEvent(args.Used.Uid, new StackChangeCountEvent(otherStack.Count + toTransfer), false); + SetCount(uid, stack, stack.Count - toTransfer); + SetCount(args.Used.Uid, otherStack, otherStack.Count + toTransfer); var popupPos = args.ClickLocation; if (!popupPos.IsValid(EntityManager)) @@ -145,110 +131,4 @@ namespace Content.Server.Stack args.Handled = true; } } - - /* - * The following events are actually funny ECS method calls! - * - * Instead of coupling systems together into a ball of spaghetti, - * we raise events that act as method calls. - * - * So for example, instead of having an Use() method in the - * stack component or stack system, we have a StackUseEvent. - * Before raising the event, you would set the Amount property, - * which acts as a parameter or argument, and afterwards the - * entity system in charge of handling this would perform the logic - * and then set the Result on the event instance. - * Then you can access this property to see whether your Use attempt succeeded. - * - * This is very powerful, as it completely removes the coupling - * between entity systems and allows for greater flexibility. - * If you want to intercept this event with another entity system, you can. - * And you don't have to write any bad, hacky code for this! - * You could even use handled events, or cancellable events... - * The possibilities are endless. - * - * Of course, not everything needs to be directed events! - * Broadcast events also work in the same way. - * For example, we use a broadcast event to spawn a stack of a certain type. - * - * Wrapping your head around this may be difficult at first, - * but soon you'll get it, coder. Soon you'll grasp the wisdom. - * Go forth and write some beautiful and robust code! - */ - - /// - /// Uses an amount of things from a stack. - /// Whether this succeeded is stored in . - /// - public class StackUseEvent : EntityEventArgs - { - /// - /// The amount of things to use on the stack. - /// Consider this the equivalent of a parameter for a method call. - /// - public int Amount { get; init; } - - /// - /// Whether the action succeeded or not. - /// Set by the after handling this event. - /// Consider this the equivalent of a return value for a method call. - /// - public bool Result { get; set; } = false; - } - - /// - /// Tries to split a stack into two. - /// If this succeeds, will be the new stack. - /// - public class StackSplitEvent : EntityEventArgs - { - /// - /// The amount of things to take from the original stack. - /// Input parameter. - /// - public int Amount { get; init; } - - /// - /// The position where to spawn the new stack. - /// Input parameter. - /// - public EntityCoordinates SpawnPosition { get; init; } - - /// - /// The newly split stack. May be null if the split failed. - /// Output parameter. - /// - public IEntity? Result { get; set; } = null; - } - - /// - /// Tries to spawn a stack of a certain type. - /// If this succeeds, will be the new stack. - /// - public class StackTypeSpawnEvent : EntityEventArgs - { - /// - /// The amount of things the spawned stack will have. - /// Input parameter. - /// - public int Amount { get; init; } - - /// - /// The stack type to be spawned. - /// Input parameter. - /// - public StackPrototype? StackType { get; init; } - - /// - /// The position where the new stack will be spawned. - /// Input parameter. - /// - public EntityCoordinates SpawnPosition { get; init; } - - /// - /// The newly spawned stack, or null if this failed. - /// Output parameter. - /// - public IEntity? Result { get; set; } = null; - } } diff --git a/Content.Server/Tiles/FloorTileItemComponent.cs b/Content.Server/Tiles/FloorTileItemComponent.cs index 786dc9730d..345167db84 100644 --- a/Content.Server/Tiles/FloorTileItemComponent.cs +++ b/Content.Server/Tiles/FloorTileItemComponent.cs @@ -80,10 +80,7 @@ namespace Content.Server.Tiles if (HasBaseTurf(currentTileDefinition, baseTurf.Name)) { - var stackUse = new StackUseEvent() {Amount = 1}; - Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, stackUse); - - if (!stackUse.Result) + if (!EntitySystem.Get().Use(Owner.Uid, stack, 1)) continue; PlaceAt(mapGrid, location, currentTileDefinition.TileId); diff --git a/Content.Server/Wires/Components/WireComponent.cs b/Content.Server/Wires/Components/WireComponent.cs index afb2aae8aa..473a6d8375 100644 --- a/Content.Server/Wires/Components/WireComponent.cs +++ b/Content.Server/Wires/Components/WireComponent.cs @@ -44,8 +44,8 @@ namespace Content.Server.Wires.Components var droppedEnt = Owner.EntityManager.SpawnEntity(_wireDroppedOnCutPrototype, eventArgs.ClickLocation); // TODO: Literally just use a prototype that has a single thing in the stack, it's not that complicated... - if (droppedEnt.HasComponent()) - Owner.EntityManager.EventBus.RaiseLocalEvent(droppedEnt.Uid, new StackChangeCountEvent(1), false); + if (droppedEnt.TryGetComponent(out var stack)) + EntitySystem.Get().SetCount(droppedEnt.Uid, stack, 1); return true; } diff --git a/Content.Server/Wires/Components/WirePlacerComponent.cs b/Content.Server/Wires/Components/WirePlacerComponent.cs index 8642178b24..6b83e9de6c 100644 --- a/Content.Server/Wires/Components/WirePlacerComponent.cs +++ b/Content.Server/Wires/Components/WirePlacerComponent.cs @@ -47,14 +47,9 @@ namespace Content.Server.Wires.Components } } - if (Owner.HasComponent()) - { - var stackUse = new StackUseEvent(){Amount = 1}; - Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, stackUse); - - if(!stackUse.Result) - return true; - } + if (Owner.TryGetComponent(out var stack) + && !EntitySystem.Get().Use(Owner.Uid, stack, 1)) + return true; Owner.EntityManager.SpawnEntity(_wirePrototypeID, grid.GridTileToLocal(snapPos)); return true; diff --git a/Content.Shared/Stacks/SharedStackComponent.cs b/Content.Shared/Stacks/SharedStackComponent.cs index d0ef839dd0..c9c0b8c300 100644 --- a/Content.Shared/Stacks/SharedStackComponent.cs +++ b/Content.Shared/Stacks/SharedStackComponent.cs @@ -57,7 +57,7 @@ namespace Content.Shared.Stacks return; // This will change the count and call events. - Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new StackChangeCountEvent(cast.Count)); + EntitySystem.Get().SetCount(Owner.Uid, this, cast.Count); MaxCount = cast.MaxCount; } diff --git a/Content.Shared/Stacks/SharedStackSystem.cs b/Content.Shared/Stacks/SharedStackSystem.cs index 4c6fbc0909..6eb5933f42 100644 --- a/Content.Shared/Stacks/SharedStackSystem.cs +++ b/Content.Shared/Stacks/SharedStackSystem.cs @@ -12,7 +12,6 @@ namespace Content.Shared.Stacks base.Initialize(); SubscribeLocalEvent(OnStackStarted); - SubscribeLocalEvent(OnStackCountChange); SubscribeLocalEvent(OnStackExamined); } @@ -26,26 +25,27 @@ namespace Content.Shared.Stacks appearance.SetData(StackVisuals.Hide, false); } - protected void OnStackCountChange(EntityUid uid, SharedStackComponent component, StackChangeCountEvent args) + public void SetCount(EntityUid uid, SharedStackComponent component, int amount) { - if (args.Amount == component.Count) + // Do nothing if amount is already the same. + if (amount == component.Count) return; + // Store old value for event-raising purposes... var old = component.Count; - if (args.Amount > component.MaxCount) + // Clamp the value. + if (amount > component.MaxCount) { - args.Amount = component.MaxCount; - args.Clamped = true; + amount = component.MaxCount; } - if (args.Amount < 0) + if (amount < 0) { - args.Amount = 0; - args.Clamped = true; + amount = 0; } - component.Count = args.Amount; + component.Count = amount; component.Dirty(); // Queue delete stack if count reaches zero. @@ -74,32 +74,6 @@ namespace Content.Shared.Stacks } } - /// - /// Attempts to change the amount of things in a stack to a specific number. - /// If the amount had to be clamped to zero or the max amount, will be true - /// and the amount will be changed to match the value set. - /// Does nothing if the amount is the same as the stack count already. - /// - public class StackChangeCountEvent : EntityEventArgs - { - /// - /// Amount to set the stack to. - /// Input/Output parameter. - /// - public int Amount { get; set; } - - /// - /// Whether the had to be clamped. - /// Output parameter. - /// - public bool Clamped { get; set; } - - public StackChangeCountEvent(int amount) - { - Amount = amount; - } - } - /// /// Event raised when a stack's count has changed. ///