diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs index b13ca0d869..87e0a96928 100644 --- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -35,10 +35,10 @@ namespace Content.Server.Chemistry.EntitySystems public void OnClickAttack(EntityUid uid, HyposprayComponent comp, ClickAttackEvent args) { - var target = args.TargetEntity; - var user = args.User; + if (args.Target == null) + return; - comp.TryDoInject(target, user); + comp.TryDoInject(args.Target.Value, args.User); } } } diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index 65e9fc4317..c5542c14c3 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -9,6 +9,7 @@ using Content.Server.Items; using Content.Server.Pulling; using Content.Server.Storage.Components; using Content.Shared.ActionBlocker; +using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.DragDrop; using Content.Shared.Input; @@ -38,7 +39,6 @@ namespace Content.Server.Interaction [UsedImplicitly] public sealed class InteractionSystem : SharedInteractionSystem { - [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly PullingSystem _pullSystem = default!; [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; @@ -74,7 +74,7 @@ namespace Content.Server.Interaction { userEntity = null; - if (!coords.IsValid(_entityManager)) + if (!coords.IsValid(EntityManager)) { Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}"); return false; @@ -89,7 +89,7 @@ namespace Content.Server.Interaction userEntity = ((IPlayerSession?) session)?.AttachedEntity; - if (userEntity == null || !EntityManager.EntityExists(userEntity.Value)) + if (userEntity == null || !userEntity.Value.IsValid()) { Logger.WarningS("system.interaction", $"Client sent interaction with no attached entity. Session={session}"); @@ -101,19 +101,19 @@ namespace Content.Server.Interaction public override bool CanAccessViaStorage(EntityUid user, EntityUid target) { - if (!EntityManager.EntityExists(target)) + if (Deleted(target)) return false; if (!target.TryGetContainer(out var container)) return false; - if (!EntityManager.TryGetComponent(container.Owner, out ServerStorageComponent storage)) + if (!TryComp(container.Owner, out ServerStorageComponent? storage)) return false; if (storage.Storage?.ID != container.ID) return false; - if (!EntityManager.TryGetComponent(user, out ActorComponent actor)) + if (!TryComp(user, out ActorComponent? actor)) return false; // we don't check if the user can access the storage entity itself. This should be handed by the UI system. @@ -127,15 +127,17 @@ namespace Content.Server.Interaction /// private void HandleInteractInventorySlotEvent(InteractInventorySlotEvent msg, EntitySessionEventArgs args) { - if (!EntityManager.EntityExists(msg.ItemUid)) + if (Deleted(msg.ItemUid)) { Logger.WarningS("system.interaction", $"Client sent inventory interaction with an invalid target item. Session={args.SenderSession}"); return; } + var itemCoords = Transform(msg.ItemUid).Coordinates; + // client sanitization - if (!ValidateClientInput(args.SenderSession, EntityManager.GetComponent(msg.ItemUid).Coordinates, msg.ItemUid, out var userEntity)) + if (!ValidateClientInput(args.SenderSession, itemCoords, msg.ItemUid, out var userEntity)) { Logger.InfoS("system.interaction", $"Inventory interaction validation failed. Session={args.SenderSession}"); return; @@ -143,7 +145,7 @@ namespace Content.Server.Interaction if (msg.AltInteract) // Use 'UserInteraction' function - behaves as if the user alt-clicked the item in the world. - UserInteraction(userEntity.Value, EntityManager.GetComponent(msg.ItemUid).Coordinates, msg.ItemUid, msg.AltInteract); + UserInteraction(userEntity.Value, itemCoords, msg.ItemUid, msg.AltInteract); else // User used 'E'. We want to activate it, not simulate clicking on the item InteractionActivate(userEntity.Value, msg.ItemUid); @@ -161,9 +163,7 @@ namespace Content.Server.Interaction if (!_actionBlockerSystem.CanInteract(userEntity.Value)) return; - if (!EntityManager.EntityExists(msg.Dropped)) - return; - if (!EntityManager.EntityExists(msg.Target)) + if (Deleted(msg.Dropped) || Deleted(msg.Target)) return; var interactionArgs = new DragDropEvent(userEntity.Value, msg.DropLocation, msg.Dropped, msg.Target); @@ -175,7 +175,7 @@ namespace Content.Server.Interaction // trigger dragdrops on the dropped entity RaiseLocalEvent(msg.Dropped, interactionArgs); - foreach (var dragDrop in EntityManager.GetComponents(msg.Dropped)) + foreach (var dragDrop in AllComps(msg.Dropped)) { if (dragDrop.CanDrop(interactionArgs) && dragDrop.Drop(interactionArgs)) @@ -186,7 +186,7 @@ namespace Content.Server.Interaction // trigger dragdropons on the targeted entity RaiseLocalEvent(msg.Target, interactionArgs, false); - foreach (var dragDropOn in EntityManager.GetComponents(msg.Target)) + foreach (var dragDropOn in AllComps(msg.Target)) { if (dragDropOn.CanDragDropOn(interactionArgs) && dragDropOn.DragDropOn(interactionArgs)) @@ -206,7 +206,7 @@ namespace Content.Server.Interaction return false; } - if (!EntityManager.EntityExists(uid)) + if (Deleted(uid)) return false; InteractionActivate(user.Value, uid); @@ -223,7 +223,7 @@ namespace Content.Server.Interaction return true; } - if (EntityManager.TryGetComponent(userEntity.Value, out CombatModeComponent? combatMode) && combatMode.IsInCombatMode) + if (TryComp(userEntity, out CombatModeComponent? combatMode) && combatMode.IsInCombatMode) DoAttack(userEntity.Value, coords, true); return true; @@ -238,7 +238,7 @@ namespace Content.Server.Interaction /// internal void AiUseInteraction(EntityUid entity, EntityCoordinates coords, EntityUid uid) { - if (EntityManager.HasComponent(entity)) + if (HasComp(entity)) throw new InvalidOperationException(); UserInteraction(entity, coords, uid); @@ -253,7 +253,7 @@ namespace Content.Server.Interaction return true; } - UserInteraction(userEntity.Value, coords, uid); + UserInteraction(userEntity.Value, coords, !Deleted(uid) ? uid : null); return true; } @@ -267,7 +267,7 @@ namespace Content.Server.Interaction return true; } - UserInteraction(userEntity.Value, coords, uid, altInteract : true ); + UserInteraction(userEntity.Value, coords, !Deleted(uid) ? uid : null, altInteract : true ); return true; } @@ -280,16 +280,16 @@ namespace Content.Server.Interaction return true; } - if (userEntity == uid) + if (userEntity.Value == uid) return false; - if (!EntityManager.EntityExists(uid)) + if (Deleted(uid)) return false; if (!InRangeUnobstructed(userEntity.Value, uid, popup: true)) return false; - if (!EntityManager.TryGetComponent(uid, out SharedPullableComponent? pull)) + if (!TryComp(uid, out SharedPullableComponent? pull)) return false; return _pullSystem.TogglePull(userEntity.Value, pull); @@ -304,10 +304,10 @@ namespace Content.Server.Interaction /// Whether to use default or alternative interactions (usually as a result of /// alt+clicking). If combat mode is enabled, the alternative action is to perform the default non-combat /// interaction. Having an item in the active hand also disables alternative interactions. - public async void UserInteraction(EntityUid user, EntityCoordinates coordinates, EntityUid target, bool altInteract = false) + public async void UserInteraction(EntityUid user, EntityCoordinates coordinates, EntityUid? target, bool altInteract = false) { // TODO COMBAT Consider using alt-interact for advanced combat? maybe alt-interact disarms? - if (!altInteract && EntityManager.TryGetComponent(user, out CombatModeComponent? combatMode) && combatMode.IsInCombatMode) + if (!altInteract && TryComp(user, out CombatModeComponent? combatMode) && combatMode.IsInCombatMode) { DoAttack(user, coordinates, false, target); return; @@ -321,22 +321,22 @@ namespace Content.Server.Interaction // Check if interacted entity is in the same container, the direct child, or direct parent of the user. // This is bypassed IF the interaction happened through an item slot (e.g., backpack UI) - if (target != default && !user.IsInSameOrParentContainer(target) && !CanAccessViaStorage(user, target)) + if (target != null && !Deleted(target.Value) && !user.IsInSameOrParentContainer(target.Value) && !CanAccessViaStorage(user, target.Value)) { Logger.WarningS("system.interaction", - $"User entity named {EntityManager.GetComponent(user).EntityName} clicked on object {EntityManager.GetComponent(target).EntityName} that isn't the parent, child, or in the same container"); + $"User entity {ToPrettyString(user):user} clicked on object {ToPrettyString(target.Value):target} that isn't the parent, child, or in the same container"); return; } // Verify user has a hand, and find what object they are currently holding in their active hand - if (!EntityManager.TryGetComponent(user, out var hands)) + if (!TryComp(user, out HandsComponent? hands)) return; var item = hands.GetActiveHand?.Owner; // TODO: Replace with body interaction range when we get something like arm length or telekinesis or something. var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true); - if (target == default || !inRangeUnobstructed) + if (target == null || Deleted(target.Value) || !inRangeUnobstructed) { if (item == null) return; @@ -350,29 +350,27 @@ namespace Content.Server.Interaction return; } - else - { - // We are close to the nearby object. - if (altInteract) - // Perform alternative interactions, using context menu verbs. - AltInteract(user, target); - else if (item != null && item != target) - // We are performing a standard interaction with an item, and the target isn't the same as the item - // currently in our hand. We will use the item in our hand on the nearby object via InteractUsing - await InteractUsing(user, item.Value, target, coordinates); - else if (item == null) - // Since our hand is empty we will use InteractHand/Activate - InteractHand(user, target); - } + + // We are close to the nearby object. + if (altInteract) + // Perform alternative interactions, using context menu verbs. + AltInteract(user, target.Value); + else if (item != null && item != target) + // We are performing a standard interaction with an item, and the target isn't the same as the item + // currently in our hand. We will use the item in our hand on the nearby object via InteractUsing + await InteractUsing(user, item.Value, target.Value, coordinates); + else if (item == null) + // Since our hand is empty we will use InteractHand/Activate + InteractHand(user, target.Value); } private bool ValidateInteractAndFace(EntityUid user, EntityCoordinates coordinates) { // Verify user is on the same map as the entity they clicked on - if (coordinates.GetMapId(_entityManager) != EntityManager.GetComponent(user).MapID) + if (coordinates.GetMapId(EntityManager) != Transform(user).MapID) { Logger.WarningS("system.interaction", - $"User entity named {EntityManager.GetComponent(user).EntityName} clicked on a map they aren't located on"); + $"User entity {ToPrettyString(user):user} clicked on a map they aren't located on"); return false; } @@ -400,7 +398,7 @@ namespace Content.Server.Interaction var interactHandEventArgs = new InteractHandEventArgs(user, target); - var interactHandComps = EntityManager.GetComponents(target).ToList(); + var interactHandComps = AllComps(target).ToList(); foreach (var interactHandComp in interactHandComps) { // If an InteractHand returns a status completion we finish our interaction @@ -418,19 +416,19 @@ namespace Content.Server.Interaction /// Will have two behaviors, either "uses" the used entity at range on the target entity if it is capable of accepting that action /// Or it will use the used entity itself on the position clicked, regardless of what was there /// - public async Task InteractUsingRanged(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation, bool inRangeUnobstructed) + public async Task InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool inRangeUnobstructed) { if (InteractDoBefore(user, used, inRangeUnobstructed ? target : null, clickLocation, false)) return true; - if (target != default) + if (target != null) { - var rangedMsg = new RangedInteractEvent(user, used, target, clickLocation); - RaiseLocalEvent(target, rangedMsg); + var rangedMsg = new RangedInteractEvent(user, used, target.Value, clickLocation); + RaiseLocalEvent(target.Value, rangedMsg); if (rangedMsg.Handled) return true; - var rangedInteractions = EntityManager.GetComponents(target).ToList(); + var rangedInteractions = AllComps(target.Value).ToList(); var rangedInteractionEventArgs = new RangedInteractEventArgs(user, used, clickLocation); // See if we have a ranged interaction @@ -447,7 +445,7 @@ namespace Content.Server.Interaction return await InteractDoAfter(user, used, inRangeUnobstructed ? target : null, clickLocation, false); } - public void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default) + public void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid? targetUid = null) { if (!ValidateInteractAndFace(user, coordinates)) return; @@ -458,10 +456,10 @@ namespace Content.Server.Interaction if (!wideAttack) { // Check if interacted entity is in the same container, the direct child, or direct parent of the user. - if (targetUid != default && !user.IsInSameOrParentContainer(targetUid) && !CanAccessViaStorage(user, targetUid)) + if (targetUid != null && !Deleted(targetUid.Value) && !user.IsInSameOrParentContainer(targetUid.Value) && !CanAccessViaStorage(user, targetUid.Value)) { Logger.WarningS("system.interaction", - $"User entity named {EntityManager.GetComponent(user).EntityName} clicked on object {EntityManager.GetComponent(targetUid).EntityName} that isn't the parent, child, or in the same container"); + $"User entity {ToPrettyString(user):user} clicked on object {ToPrettyString(targetUid.Value):target} that isn't the parent, child, or in the same container"); return; } @@ -471,47 +469,49 @@ namespace Content.Server.Interaction } // Verify user has a hand, and find what object they are currently holding in their active hand - if (EntityManager.TryGetComponent(user, out var hands)) + if (TryComp(user, out HandsComponent? hands)) { - if (hands.GetActiveHand?.Owner is {Valid: true} item) + var item = hands.GetActiveHand?.Owner; + + if (item != null && !Deleted(item.Value)) { if (wideAttack) { - var ev = new WideAttackEvent(item, user, coordinates); - RaiseLocalEvent(item, ev, false); + var ev = new WideAttackEvent(item.Value, user, coordinates); + RaiseLocalEvent(item.Value, ev, false); if (ev.Handled) { - _adminLogSystem.Add(LogType.AttackArmedWide, LogImpact.Medium, $"{ToPrettyString(user):user} wide attacked with {ToPrettyString(item):used} at {coordinates}"); + _adminLogSystem.Add(LogType.AttackArmedWide, LogImpact.Medium, $"{ToPrettyString(user):user} wide attacked with {ToPrettyString(item.Value):used} at {coordinates}"); return; } } else { - var ev = new ClickAttackEvent(item, user, coordinates, targetUid); - RaiseLocalEvent(item, ev, false); + var ev = new ClickAttackEvent(item.Value, user, coordinates, targetUid); + RaiseLocalEvent(item.Value, ev, false); if (ev.Handled) { - if (targetUid != default) + if (targetUid != null) { _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium, - $"{ToPrettyString(user):user} attacked {ToPrettyString(targetUid):target} with {ToPrettyString(item):used} at {coordinates}"); + $"{ToPrettyString(user):user} attacked {ToPrettyString(targetUid.Value):target} with {ToPrettyString(item.Value):used} at {coordinates}"); } else { _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium, - $"{ToPrettyString(user):user} attacked with {ToPrettyString(item):used} at {coordinates}"); + $"{ToPrettyString(user):user} attacked with {ToPrettyString(item.Value):used} at {coordinates}"); } return; } } } - else if (!wideAttack && targetUid != default && _entityManager.HasComponent(targetUid)) + else if (!wideAttack && targetUid != null && HasComp(targetUid.Value)) { // We pick up items if our hand is empty, even if we're in combat mode. - InteractHand(user, targetUid); + InteractHand(user, targetUid.Value); return; } } @@ -531,10 +531,10 @@ namespace Content.Server.Interaction RaiseLocalEvent(user, ev, false); if (ev.Handled) { - if (targetUid != default) + if (targetUid != null) { _adminLogSystem.Add(LogType.AttackUnarmedClick, LogImpact.Medium, - $"{ToPrettyString(user):user} attacked {ToPrettyString(targetUid):target} at {coordinates}"); + $"{ToPrettyString(user):user} attacked {ToPrettyString(targetUid.Value):target} at {coordinates}"); } else { diff --git a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs index ecd830600a..14e8d78330 100644 --- a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs @@ -74,7 +74,7 @@ namespace Content.Server.Weapon.Melee args.Handled = true; var curTime = _gameTiming.CurTime; - if (curTime < comp.CooldownEnd || !args.Target.IsValid()) + if (curTime < comp.CooldownEnd || args.Target == null) return; var location = EntityManager.GetComponent(args.User).Coordinates; @@ -101,10 +101,10 @@ namespace Content.Server.Weapon.Melee { if (args.Used == args.User) _logSystem.Add(LogType.MeleeHit, - $"{ToPrettyString(args.User):user} melee attacked {ToPrettyString(args.Target):target} using their hands and dealt {damageResult.Total:damage} damage"); + $"{ToPrettyString(args.User):user} melee attacked {ToPrettyString(args.Target.Value):target} using their hands and dealt {damageResult.Total:damage} damage"); else _logSystem.Add(LogType.MeleeHit, - $"{ToPrettyString(args.User):user} melee attacked {ToPrettyString(args.Target):target} using {ToPrettyString(args.Used):used} and dealt {damageResult.Total:damage} damage"); + $"{ToPrettyString(args.User):user} melee attacked {ToPrettyString(args.Target.Value):target} using {ToPrettyString(args.Used):used} and dealt {damageResult.Total:damage} damage"); } SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target); diff --git a/Content.Shared/Hands/Components/SharedHandsComponent.cs b/Content.Shared/Hands/Components/SharedHandsComponent.cs index 19426e0977..4301e0f851 100644 --- a/Content.Shared/Hands/Components/SharedHandsComponent.cs +++ b/Content.Shared/Hands/Components/SharedHandsComponent.cs @@ -892,6 +892,7 @@ namespace Content.Shared.Hands.Components [ViewVariables] public IContainer? Container { get; set; } + // TODO: Make this a nullable EntityUid... [ViewVariables] public EntityUid HeldEntity => Container?.ContainedEntities.FirstOrDefault() ?? EntityUid.Invalid; diff --git a/Content.Shared/Weapons/Melee/AttackEvent.cs b/Content.Shared/Weapons/Melee/AttackEvent.cs index b55bef6b0c..bf723ba586 100644 --- a/Content.Shared/Weapons/Melee/AttackEvent.cs +++ b/Content.Shared/Weapons/Melee/AttackEvent.cs @@ -27,21 +27,14 @@ namespace Content.Shared.Weapons.Melee /// /// UID of the entity that was attacked. /// - public EntityUid Target { get; } + public EntityUid? Target { get; } - /// - /// Entity that was attacked. - /// - public EntityUid? TargetEntity { get; } - - public ClickAttackEvent(EntityUid used, EntityUid user, EntityCoordinates clickLocation, EntityUid target = default) + public ClickAttackEvent(EntityUid used, EntityUid user, EntityCoordinates clickLocation, EntityUid? target = null) { Used = used; User = user; ClickLocation = clickLocation; Target = target; - - TargetEntity = IoCManager.Resolve().EntityExists(Target) ? Target : default(EntityUid?); } }