Fix food & do-after bugs (#5716)

This commit is contained in:
Leon Friedrich
2021-12-07 19:19:26 +13:00
committed by GitHub
parent 7fa117bf7e
commit bd8acc5b6b
7 changed files with 117 additions and 36 deletions

View File

@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Content.Server.Administration.Logs;
using Content.Server.Body.Components;
@@ -64,9 +65,10 @@ namespace Content.Server.Chemistry.Components
public float Delay = 5;
/// <summary>
/// Is this component currently being used in a DoAfter?
/// Token for interrupting a do-after action (e.g., injection another player). If not null, implies
/// component is currently "in use".
/// </summary>
public bool InUse = false;
public CancellationTokenSource? CancelToken;
private InjectorToggleMode _toggleState;
@@ -127,8 +129,11 @@ namespace Content.Server.Chemistry.Components
/// <param name="eventArgs"></param>
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
{
if (InUse)
return false;
if (CancelToken != null)
{
CancelToken.Cancel();
return true;
}
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
return false;
@@ -197,7 +202,6 @@ namespace Content.Server.Chemistry.Components
/// </summary>
public async Task<bool> TryInjectDoAfter(EntityUid user, EntityUid target)
{
InUse = true;
var popupSys = EntitySystem.Get<SharedPopupSystem>();
// Create a pop-up for the user
@@ -249,8 +253,9 @@ namespace Content.Server.Chemistry.Components
//TODO solution pretty string.
}
CancelToken = new();
var status = await EntitySystem.Get<DoAfterSystem>().WaitDoAfter(
new DoAfterEventArgs(user, actualDelay, target: target)
new DoAfterEventArgs(user, actualDelay, CancelToken.Token, target)
{
BreakOnUserMove = true,
BreakOnDamage = true,
@@ -258,7 +263,7 @@ namespace Content.Server.Chemistry.Components
BreakOnTargetMove = true,
MovementThreshold = 1.0f
});
InUse = false;
CancelToken = null;
return status == DoAfterStatus.Finished;
}

View File

@@ -1,6 +1,8 @@
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Components;
using Content.Shared.Hands;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using System;
namespace Content.Server.Chemistry.EntitySystems
{
@@ -12,6 +14,16 @@ namespace Content.Server.Chemistry.EntitySystems
base.Initialize();
SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<InjectorComponent, HandDeselectedEvent>(OnInjectorDeselected);
}
private void OnInjectorDeselected(EntityUid uid, InjectorComponent component, HandDeselectedEvent args)
{
if (component.CancelToken != null)
{
component.CancelToken.Cancel();
component.CancelToken = null;
}
}
private void OnSolutionChange(EntityUid uid, InjectorComponent component, SolutionChangedEvent args)

View File

@@ -12,8 +12,8 @@ namespace Content.Server.DoAfter
public sealed class DoAfterSystem : EntitySystem
{
// We cache these lists as to not allocate them every update tick...
private readonly List<DoAfter> _cancelled = new();
private readonly List<DoAfter> _finished = new();
private readonly Queue<DoAfter> _cancelled = new();
private readonly Queue<DoAfter> _finished = new();
public override void Initialize()
{
@@ -52,17 +52,17 @@ namespace Content.Server.DoAfter
case DoAfterStatus.Running:
break;
case DoAfterStatus.Cancelled:
_cancelled.Add(doAfter);
_cancelled.Enqueue(doAfter);
break;
case DoAfterStatus.Finished:
_finished.Add(doAfter);
_finished.Enqueue(doAfter);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
foreach (var doAfter in _cancelled)
while (_cancelled.TryDequeue(out var doAfter))
{
comp.Cancelled(doAfter);
@@ -76,7 +76,7 @@ namespace Content.Server.DoAfter
RaiseLocalEvent(doAfter.EventArgs.BroadcastCancelledEvent);
}
foreach (var doAfter in _finished)
while (_finished.TryDequeue(out var doAfter))
{
comp.Finished(doAfter);
@@ -89,10 +89,6 @@ namespace Content.Server.DoAfter
if(doAfter.EventArgs.BroadcastFinishedEvent != null)
RaiseLocalEvent(doAfter.EventArgs.BroadcastFinishedEvent);
}
// Clean the shared lists at the end, ensuring they'll be clean for the next time we need them.
_cancelled.Clear();
_finished.Clear();
}
}

View File

@@ -1,4 +1,3 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Sound;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
@@ -7,6 +6,7 @@ using Robust.Shared.ViewVariables;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.Analyzers;
using System.Threading;
namespace Content.Server.Nutrition.Components
{
@@ -50,8 +50,9 @@ namespace Content.Server.Nutrition.Components
public float ForceFeedDelay = 3;
/// <summary>
/// If true, this drink has some DoAfter active (someone is being force fed).
/// Token for interrupting a do-after action (e.g., force feeding). If not null, implies component is
/// currently "in use".
/// </summary>
public bool InUse = false;
public CancellationTokenSource? CancelToken;
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Threading;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.FixedPoint;
@@ -55,9 +56,10 @@ namespace Content.Server.Nutrition.Components
public float ForceFeedDelay = 3;
/// <summary>
/// If true, this food has some DoAfter active (someone is being force fed).
/// Token for interrupting a do-after action (e.g., force feeding). If not null, implies component is
/// currently "in use".
/// </summary>
public bool InUse = false;
public CancellationTokenSource? CancelToken;
[ViewVariables]
public int UsesRemaining

View File

@@ -16,6 +16,7 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.Hands;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Nutrition.Components;
@@ -54,12 +55,26 @@ namespace Content.Server.Nutrition.EntitySystems
SubscribeLocalEvent<DrinkComponent, ComponentInit>(OnDrinkInit);
SubscribeLocalEvent<DrinkComponent, LandEvent>(HandleLand);
SubscribeLocalEvent<DrinkComponent, UseInHandEvent>(OnUse);
SubscribeLocalEvent<DrinkComponent, HandDeselectedEvent>(OnDrinkDeselected);
SubscribeLocalEvent<DrinkComponent, AfterInteractEvent>(AfterInteract);
SubscribeLocalEvent<DrinkComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<SharedBodyComponent, ForceDrinkEvent>(OnForceDrink);
SubscribeLocalEvent<ForceDrinkCancelledEvent>(OnForceDrinkCancelled);
}
/// <summary>
/// If the user is currently forcing someone do drink, this cancels the attempt if they swap hands or
/// otherwise loose the item. Prevents force-feeding dual-wielding.
/// </summary>
private void OnDrinkDeselected(EntityUid uid, DrinkComponent component, HandDeselectedEvent args)
{
if (component.CancelToken != null)
{
component.CancelToken.Cancel();
component.CancelToken = null;
}
}
public bool IsEmpty(EntityUid uid, DrinkComponent? component = null)
{
if(!Resolve(uid, ref component))
@@ -240,6 +255,14 @@ namespace Content.Server.Nutrition.EntitySystems
if (!Resolve(uid, ref drink))
return false;
// if currently being used to force-feed, cancel that action.
if (drink.CancelToken != null)
{
drink.CancelToken.Cancel();
drink.CancelToken = null;
return true;
}
if (!drink.Opened)
{
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-not-open",
@@ -312,8 +335,12 @@ namespace Content.Server.Nutrition.EntitySystems
return false;
// cannot stack do-afters
if (drink.InUse)
return false;
if (drink.CancelToken != null)
{
drink.CancelToken.Cancel();
drink.CancelToken = null;
return true;
}
if (!EntityManager.HasComponent<SharedBodyComponent>(targetUid))
return false;
@@ -342,7 +369,8 @@ namespace Content.Server.Nutrition.EntitySystems
_popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)),
userUid, Filter.Entities(targetUid));
_doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, drink.ForceFeedDelay, target: targetUid)
drink.CancelToken = new();
_doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, drink.ForceFeedDelay, drink.CancelToken.Token, targetUid)
{
BreakOnUserMove = true,
BreakOnDamage = true,
@@ -350,7 +378,7 @@ namespace Content.Server.Nutrition.EntitySystems
BreakOnTargetMove = true,
MovementThreshold = 1.0f,
TargetFinishedEvent = new ForceDrinkEvent(userUid, drink, drinkSolution),
BroadcastCancelledEvent = new ForceDrinkCancelledEvent(drink)
BroadcastCancelledEvent = new ForceDrinkCancelledEvent(drink),
});
// logging
@@ -359,7 +387,6 @@ namespace Content.Server.Nutrition.EntitySystems
var drinkable = EntityManager.GetEntity(uid);
_logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{user} is forcing {target} to drink {drinkable}");
drink.InUse = true;
return true;
}
@@ -368,7 +395,10 @@ namespace Content.Server.Nutrition.EntitySystems
/// </summary>
private void OnForceDrink(EntityUid uid, SharedBodyComponent body, ForceDrinkEvent args)
{
args.Drink.InUse = false;
if (args.Drink.Deleted)
return;
args.Drink.CancelToken = null;
var transferAmount = FixedPoint2.Min(args.Drink.TransferAmount, args.DrinkSolution.DrainAvailable);
var drained = _solutionContainerSystem.Drain(args.Drink.OwnerUid, args.DrinkSolution, transferAmount);
@@ -414,7 +444,7 @@ namespace Content.Server.Nutrition.EntitySystems
private void OnForceDrinkCancelled(ForceDrinkCancelledEvent args)
{
args.Drink.InUse = false;
args.Drink.CancelToken = null;
}
}
}

View File

@@ -9,7 +9,6 @@ using Content.Server.Popups;
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
@@ -23,10 +22,10 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Player;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Utility;
using Content.Server.Inventory.Components;
using Content.Shared.Inventory;
using Content.Shared.Hands;
namespace Content.Server.Nutrition.EntitySystems
{
@@ -50,12 +49,26 @@ namespace Content.Server.Nutrition.EntitySystems
SubscribeLocalEvent<FoodComponent, UseInHandEvent>(OnUseFoodInHand);
SubscribeLocalEvent<FoodComponent, AfterInteractEvent>(OnFeedFood);
SubscribeLocalEvent<FoodComponent, HandDeselectedEvent>(OnFoodDeselected);
SubscribeLocalEvent<FoodComponent, GetInteractionVerbsEvent>(AddEatVerb);
SubscribeLocalEvent<SharedBodyComponent, ForceFeedEvent>(OnForceFeed);
SubscribeLocalEvent<ForceFeedCancelledEvent>(OnForceFeedCancelled);
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt);
}
/// <summary>
/// If the user is currently force feeding someone, this cancels the attempt if they swap hands or otherwise
/// loose the item. Prevents force-feeding dual-wielding.
/// </summary>
private void OnFoodDeselected(EntityUid uid, FoodComponent component, HandDeselectedEvent args)
{
if (component.CancelToken != null)
{
component.CancelToken.Cancel();
component.CancelToken = null;
}
}
/// <summary>
/// Eat item
/// </summary>
@@ -120,6 +133,14 @@ namespace Content.Server.Nutrition.EntitySystems
if (!Resolve(uid, ref component))
return false;
// if currently being used to force-feed, cancel that action.
if (component.CancelToken != null)
{
component.CancelToken.Cancel();
component.CancelToken = null;
return true;
}
if (uid == userUid || //Suppresses self-eating
EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) && mobState.IsAlive()) // Suppresses eating alive mobs
return false;
@@ -214,6 +235,9 @@ namespace Content.Server.Nutrition.EntitySystems
private void AddEatVerb(EntityUid uid, FoodComponent component, GetInteractionVerbsEvent ev)
{
if (component.CancelToken != null)
return;
if (uid == ev.UserUid ||
!ev.CanInteract ||
!ev.CanAccess ||
@@ -244,6 +268,14 @@ namespace Content.Server.Nutrition.EntitySystems
if (!Resolve(uid, ref food))
return false;
// if currently being used to force-feed, cancel that action.
if (food.CancelToken != null)
{
food.CancelToken.Cancel();
food.CancelToken = null;
return true;
}
if (!EntityManager.HasComponent<SharedBodyComponent>(targetUid))
return false;
@@ -270,7 +302,8 @@ namespace Content.Server.Nutrition.EntitySystems
_popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)),
userUid, Filter.Entities(targetUid));
_doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, food.ForceFeedDelay, target: targetUid)
food.CancelToken = new();
_doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, food.ForceFeedDelay, food.CancelToken.Token, targetUid)
{
BreakOnUserMove = true,
BreakOnDamage = true,
@@ -287,13 +320,15 @@ namespace Content.Server.Nutrition.EntitySystems
var edible = EntityManager.GetEntity(uid);
_logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{user} is forcing {target} to eat {edible}");
food.InUse = true;
return true;
}
private void OnForceFeed(EntityUid uid, SharedBodyComponent body, ForceFeedEvent args)
{
args.Food.InUse = false;
if (args.Food.Deleted)
return;
args.Food.CancelToken = null;
if (!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(uid, out var stomachs, body))
return;
@@ -433,7 +468,7 @@ namespace Content.Server.Nutrition.EntitySystems
private void OnForceFeedCancelled(ForceFeedCancelledEvent args)
{
args.Food.InUse = false;
args.Food.CancelToken = null;
}
/// <summary>