Skip to content

Commit

Permalink
Reduce network burden of the hunger system (space-wizards#32986)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Centronias authored Dec 18, 2024
1 parent 87f39af commit 6b99493
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Content.Server/Animals/Systems/EggLayerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public bool TryLayEgg(EntityUid uid, EggLayerComponent? egglayer)
// 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;
Expand Down
4 changes: 2 additions & 2 deletions Content.Server/EntityEffects/EffectConditions/TotalHunger.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,7 +17,7 @@ public override bool Condition(EntityEffectBaseArgs args)
{
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public bool TryGetValidOccupant(EntityUid uid, [NotNullWhen(true)] out EntityUid
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))
Expand Down
4 changes: 2 additions & 2 deletions Content.Server/RatKing/RatKingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private void OnRaiseArmy(EntityUid uid, RatKingComponent component, RatKingRaise
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;
Expand Down Expand Up @@ -77,7 +77,7 @@ private void OnDomain(EntityUid uid, RatKingComponent component, RatKingDomainAc
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;
Expand Down
32 changes: 22 additions & 10 deletions Content.Shared/Nutrition/Components/HungerComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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]
Expand Down Expand Up @@ -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]
Expand Down
52 changes: 41 additions & 11 deletions Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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;
Expand Down Expand Up @@ -72,6 +73,16 @@ private void OnRejuvenate(EntityUid uid, HungerComponent component, RejuvenateEv
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>
Expand All @@ -82,7 +93,7 @@ public void ModifyHunger(EntityUid uid, float amount, HungerComponent? component
{
if (!Resolve(uid, ref component))
return;
SetHunger(uid, component.CurrentHunger + amount, component);
SetHunger(uid, GetHunger(component) + amount, component);
}

/// <summary>
Expand All @@ -95,11 +106,23 @@ public void SetHunger(EntityUid uid, float amount, HungerComponent? component =
{
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)
Expand All @@ -112,7 +135,6 @@ private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component =
return;
component.CurrentThreshold = calculatedHungerThreshold;
DoHungerThresholdEffects(uid, component);
Dirty(uid, component);
}

private void DoHungerThresholdEffects(EntityUid uid, HungerComponent? component = null, bool force = false)
Expand Down Expand Up @@ -140,6 +162,7 @@ private void DoHungerThresholdEffects(EntityUid uid, HungerComponent? component
if (component.HungerThresholdDecayModifiers.TryGetValue(component.CurrentThreshold, out var modifier))
{
component.ActualDecayRate = component.BaseDecayRate * modifier;
SetAuthoritativeHungerValue((uid, component), GetHunger(component));
}

component.LastThreshold = component.CurrentThreshold;
Expand Down Expand Up @@ -167,7 +190,7 @@ component.StarvationDamage is { } damage &&
/// <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)
Expand All @@ -178,6 +201,7 @@ public HungerThreshold GetHungerThreshold(HungerComponent component, float? food
value = threshold.Value;
}
}

return result;
}

Expand Down Expand Up @@ -229,20 +253,26 @@ public bool TryGetStatusIconPrototype(HungerComponent component, [NotNullWhen(tr
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);

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);
}
}
}

13 changes: 10 additions & 3 deletions Content.Shared/Sericulture/SericultureSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ private void OnCompRemove(EntityUid uid, SericultureComponent comp, ComponentShu
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;
Expand All @@ -76,8 +79,12 @@ private void OnSericultureDoAfter(EntityUid uid, SericultureComponent comp, Seri
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;
Expand Down
1 change: 0 additions & 1 deletion Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1686,7 +1686,6 @@
Dead: 0
baseDecayRate: 0.04
- type: Hunger
currentHunger: 25 # spawn with Okay hunger state
thresholds:
Overfed: 35
Okay: 25
Expand Down
1 change: 0 additions & 1 deletion Resources/Prototypes/Entities/Mobs/NPCs/space.yml
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,6 @@
Dead: 0
baseDecayRate: 0.04
- type: Hunger
currentHunger: 25 # spawn with Okay hunger state
thresholds:
Overfed: 35
Okay: 25
Expand Down

0 comments on commit 6b99493

Please sign in to comment.