Reduce network burden of the hunger system (#32986)
* reduce network burden of the hunger system * explicit start + last updated * remove auto reformat changes to otherwise untouched code add clamp helper * imagine making breaking changes, documenting them, and then not thinking to check the yaml * comments * Remove unused net manager in hunger system Remove lastAuthoritativeHungerValue from prototypes
This commit is contained in:
@@ -84,7 +84,7 @@ public sealed class EggLayerSystem : EntitySystem
|
||||
// Allow infinitely laying eggs if they can't get hungry.
|
||||
if (TryComp<HungerComponent>(uid, out var hunger))
|
||||
{
|
||||
if (hunger.CurrentHunger < egglayer.HungerUsage)
|
||||
if (_hunger.GetHunger(hunger) < egglayer.HungerUsage)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("action-popup-lay-egg-too-hungry"), uid, uid);
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.EntityEffects.EffectConditions;
|
||||
@@ -17,7 +17,7 @@ public sealed partial class Hunger : EntityEffectCondition
|
||||
{
|
||||
if (args.EntityManager.TryGetComponent(args.TargetEntity, out HungerComponent? hunger))
|
||||
{
|
||||
var total = hunger.CurrentHunger;
|
||||
var total = args.EntityManager.System<HungerSystem>().GetHunger(hunger);
|
||||
if (total > Min && total < Max)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public sealed class FatExtractorSystem : EntitySystem
|
||||
if (!TryComp<HungerComponent>(occupant, out var hunger))
|
||||
return false;
|
||||
|
||||
if (hunger.CurrentHunger < component.NutritionPerSecond)
|
||||
if (_hunger.GetHunger(hunger) < component.NutritionPerSecond)
|
||||
return false;
|
||||
|
||||
if (hunger.CurrentThreshold < component.MinHungerThreshold && !HasComp<EmaggedComponent>(uid))
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Content.Server.RatKing
|
||||
return;
|
||||
|
||||
//make sure the hunger doesn't go into the negatives
|
||||
if (hunger.CurrentHunger < component.HungerPerArmyUse)
|
||||
if (_hunger.GetHunger(hunger) < component.HungerPerArmyUse)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("rat-king-too-hungry"), uid, uid);
|
||||
return;
|
||||
@@ -77,7 +77,7 @@ namespace Content.Server.RatKing
|
||||
return;
|
||||
|
||||
//make sure the hunger doesn't go into the negatives
|
||||
if (hunger.CurrentHunger < component.HungerPerDomainUse)
|
||||
if (_hunger.GetHunger(hunger) < component.HungerPerDomainUse)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("rat-king-too-hungry"), uid, uid);
|
||||
return;
|
||||
|
||||
@@ -14,22 +14,33 @@ namespace Content.Shared.Nutrition.Components;
|
||||
public sealed partial class HungerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The current hunger amount of the entity
|
||||
/// The hunger value as authoritatively set by the server as of <see cref="LastAuthoritativeHungerChangeTime"/>.
|
||||
/// This value should be updated relatively infrequently. To get the current hunger, which changes with each update,
|
||||
/// use <see cref="HungerSystem.GetHunger"/>.
|
||||
/// </summary>
|
||||
[DataField("currentHunger"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
[AutoNetworkedField]
|
||||
public float CurrentHunger;
|
||||
public float LastAuthoritativeHungerValue;
|
||||
|
||||
/// <summary>
|
||||
/// The base amount at which <see cref="CurrentHunger"/> decays.
|
||||
/// The time at which <see cref="LastAuthoritativeHungerValue"/> was last updated.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public TimeSpan LastAuthoritativeHungerChangeTime;
|
||||
|
||||
/// <summary>
|
||||
/// The base amount at which <see cref="LastAuthoritativeHungerValue"/> decays.
|
||||
/// </summary>
|
||||
/// <remarks>Any time this is modified, <see cref="HungerSystem.SetAuthoritativeHungerValue"/> should be called.</remarks>
|
||||
[DataField("baseDecayRate"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseDecayRate = 0.01666666666f;
|
||||
|
||||
/// <summary>
|
||||
/// The actual amount at which <see cref="CurrentHunger"/> decays.
|
||||
/// The actual amount at which <see cref="LastAuthoritativeHungerValue"/> decays.
|
||||
/// Affected by <seealso cref="CurrentThreshold"/>
|
||||
/// </summary>
|
||||
/// <remarks>Any time this is modified, <see cref="HungerSystem.SetAuthoritativeHungerValue"/> should be called.</remarks>
|
||||
[DataField("actualDecayRate"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public float ActualDecayRate;
|
||||
@@ -45,12 +56,13 @@ public sealed partial class HungerComponent : Component
|
||||
/// <summary>
|
||||
/// The current hunger threshold the entity is at
|
||||
/// </summary>
|
||||
/// <remarks>Any time this is modified, <see cref="HungerSystem.SetAuthoritativeHungerValue"/> should be called.</remarks>
|
||||
[DataField("currentThreshold"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public HungerThreshold CurrentThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary relating HungerThreshold to the amount of <see cref="CurrentHunger"/> needed for each one
|
||||
/// A dictionary relating HungerThreshold to the amount of <see cref="HungerSystem.GetHunger">current hunger</see> needed for each one
|
||||
/// </summary>
|
||||
[DataField("thresholds", customTypeSerializer: typeof(DictionarySerializer<HungerThreshold, float>))]
|
||||
[AutoNetworkedField]
|
||||
@@ -106,19 +118,19 @@ public sealed partial class HungerComponent : Component
|
||||
public DamageSpecifier? StarvationDamage;
|
||||
|
||||
/// <summary>
|
||||
/// The time when the hunger will update next.
|
||||
/// The time when the hunger threshold will update next.
|
||||
/// </summary>
|
||||
[DataField("nextUpdateTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextUpdateTime;
|
||||
public TimeSpan NextThresholdUpdateTime;
|
||||
|
||||
/// <summary>
|
||||
/// The time between each update.
|
||||
/// The time between each hunger threshold update.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public TimeSpan UpdateRate = TimeSpan.FromSeconds(1);
|
||||
public TimeSpan ThresholdUpdateRate = TimeSpan.FromSeconds(1);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -72,6 +73,16 @@ public sealed class HungerSystem : EntitySystem
|
||||
SetHunger(uid, component.Thresholds[HungerThreshold.Okay], component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current hunger value of the given <see cref="HungerComponent"/>.
|
||||
/// </summary>
|
||||
public float GetHunger(HungerComponent component)
|
||||
{
|
||||
var dt = _timing.CurTime - component.LastAuthoritativeHungerChangeTime;
|
||||
var value = component.LastAuthoritativeHungerValue - (float)dt.TotalSeconds * component.ActualDecayRate;
|
||||
return ClampHungerWithinThresholds(component, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the current hunger of an entity by the specified value
|
||||
/// </summary>
|
||||
@@ -82,7 +93,7 @@ public sealed class HungerSystem : EntitySystem
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
SetHunger(uid, component.CurrentHunger + amount, component);
|
||||
SetHunger(uid, GetHunger(component) + amount, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,11 +106,23 @@ public sealed class HungerSystem : EntitySystem
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
component.CurrentHunger = Math.Clamp(amount,
|
||||
component.Thresholds[HungerThreshold.Dead],
|
||||
component.Thresholds[HungerThreshold.Overfed]);
|
||||
|
||||
SetAuthoritativeHungerValue((uid, component), amount);
|
||||
UpdateCurrentThreshold(uid, component);
|
||||
Dirty(uid, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="HungerComponent.LastAuthoritativeHungerValue"/> and
|
||||
/// <see cref="HungerComponent.LastAuthoritativeHungerChangeTime"/>, and dirties this entity. This "resets" the
|
||||
/// starting point for <see cref="GetHunger"/>'s calculation.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity whose hunger will be set.</param>
|
||||
/// <param name="value">The value to set the entity's hunger to.</param>
|
||||
private void SetAuthoritativeHungerValue(Entity<HungerComponent> entity, float value)
|
||||
{
|
||||
entity.Comp.LastAuthoritativeHungerChangeTime = _timing.CurTime;
|
||||
entity.Comp.LastAuthoritativeHungerValue = ClampHungerWithinThresholds(entity.Comp, value);
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component = null)
|
||||
@@ -112,7 +135,6 @@ public sealed class HungerSystem : EntitySystem
|
||||
return;
|
||||
component.CurrentThreshold = calculatedHungerThreshold;
|
||||
DoHungerThresholdEffects(uid, component);
|
||||
Dirty(uid, component);
|
||||
}
|
||||
|
||||
private void DoHungerThresholdEffects(EntityUid uid, HungerComponent? component = null, bool force = false)
|
||||
@@ -140,6 +162,7 @@ public sealed class HungerSystem : EntitySystem
|
||||
if (component.HungerThresholdDecayModifiers.TryGetValue(component.CurrentThreshold, out var modifier))
|
||||
{
|
||||
component.ActualDecayRate = component.BaseDecayRate * modifier;
|
||||
SetAuthoritativeHungerValue((uid, component), GetHunger(component));
|
||||
}
|
||||
|
||||
component.LastThreshold = component.CurrentThreshold;
|
||||
@@ -167,7 +190,7 @@ public sealed class HungerSystem : EntitySystem
|
||||
/// <returns></returns>
|
||||
public HungerThreshold GetHungerThreshold(HungerComponent component, float? food = null)
|
||||
{
|
||||
food ??= component.CurrentHunger;
|
||||
food ??= GetHunger(component);
|
||||
var result = HungerThreshold.Dead;
|
||||
var value = component.Thresholds[HungerThreshold.Overfed];
|
||||
foreach (var threshold in component.Thresholds)
|
||||
@@ -178,6 +201,7 @@ public sealed class HungerSystem : EntitySystem
|
||||
value = threshold.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -229,6 +253,13 @@ public sealed class HungerSystem : EntitySystem
|
||||
return prototype != null;
|
||||
}
|
||||
|
||||
private static float ClampHungerWithinThresholds(HungerComponent component, float hungerValue)
|
||||
{
|
||||
return Math.Clamp(hungerValue,
|
||||
component.Thresholds[HungerThreshold.Dead],
|
||||
component.Thresholds[HungerThreshold.Overfed]);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -236,13 +267,12 @@ public sealed class HungerSystem : EntitySystem
|
||||
var query = EntityQueryEnumerator<HungerComponent>();
|
||||
while (query.MoveNext(out var uid, out var hunger))
|
||||
{
|
||||
if (_timing.CurTime < hunger.NextUpdateTime)
|
||||
if (_timing.CurTime < hunger.NextThresholdUpdateTime)
|
||||
continue;
|
||||
hunger.NextUpdateTime = _timing.CurTime + hunger.UpdateRate;
|
||||
hunger.NextThresholdUpdateTime = _timing.CurTime + hunger.ThresholdUpdateRate;
|
||||
|
||||
ModifyHunger(uid, -hunger.ActualDecayRate, hunger);
|
||||
UpdateCurrentThreshold(uid, hunger);
|
||||
DoContinuousHungerEffects(uid, hunger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,10 @@ public abstract partial class SharedSericultureSystem : EntitySystem
|
||||
private void OnSericultureStart(EntityUid uid, SericultureComponent comp, SericultureActionEvent args)
|
||||
{
|
||||
if (TryComp<HungerComponent>(uid, out var hungerComp)
|
||||
&& _hungerSystem.IsHungerBelowState(uid, comp.MinHungerThreshold, hungerComp.CurrentHunger - comp.HungerCost, hungerComp))
|
||||
&& _hungerSystem.IsHungerBelowState(uid,
|
||||
comp.MinHungerThreshold,
|
||||
_hungerSystem.GetHunger(hungerComp) - comp.HungerCost,
|
||||
hungerComp))
|
||||
{
|
||||
_popupSystem.PopupClient(Loc.GetString(comp.PopupText), uid, uid);
|
||||
return;
|
||||
@@ -76,8 +79,12 @@ public abstract partial class SharedSericultureSystem : EntitySystem
|
||||
if (args.Cancelled || args.Handled || comp.Deleted)
|
||||
return;
|
||||
|
||||
if (TryComp<HungerComponent>(uid, out var hungerComp) // A check, just incase the doafter is somehow performed when the entity is not in the right hunger state.
|
||||
&& _hungerSystem.IsHungerBelowState(uid, comp.MinHungerThreshold, hungerComp.CurrentHunger - comp.HungerCost, hungerComp))
|
||||
if (TryComp<HungerComponent>(uid,
|
||||
out var hungerComp) // A check, just incase the doafter is somehow performed when the entity is not in the right hunger state.
|
||||
&& _hungerSystem.IsHungerBelowState(uid,
|
||||
comp.MinHungerThreshold,
|
||||
_hungerSystem.GetHunger(hungerComp) - comp.HungerCost,
|
||||
hungerComp))
|
||||
{
|
||||
_popupSystem.PopupClient(Loc.GetString(comp.PopupText), uid, uid);
|
||||
return;
|
||||
|
||||
@@ -1686,7 +1686,6 @@
|
||||
Dead: 0
|
||||
baseDecayRate: 0.04
|
||||
- type: Hunger
|
||||
currentHunger: 25 # spawn with Okay hunger state
|
||||
thresholds:
|
||||
Overfed: 35
|
||||
Okay: 25
|
||||
|
||||
@@ -448,7 +448,6 @@
|
||||
Dead: 0
|
||||
baseDecayRate: 0.04
|
||||
- type: Hunger
|
||||
currentHunger: 25 # spawn with Okay hunger state
|
||||
thresholds:
|
||||
Overfed: 35
|
||||
Okay: 25
|
||||
|
||||
Reference in New Issue
Block a user