steel_core/player/health_sync.rs
1//! Delta-tracking state for `CSetHealth` packet deduplication.
2//!
3//! Tracks the last health/food/saturation values sent to the client so we only
4//! send `CSetHealth` when something actually changes.
5//!
6//! Vanilla: `ServerPlayer.lastSentHealth`, `lastSentFood`, `lastFoodSaturationZero`.
7
8use crate::player::Player;
9
10/// Tracks the last health/food/saturation values sent to the client.
11pub struct HealthSyncState {
12 /// Last health value sent to the client.
13 pub last_health: f32,
14 /// Last food level sent to the client.
15 pub last_food: i32,
16 /// Whether saturation was zero last time we sent health.
17 pub saturation_zero: bool,
18}
19
20impl HealthSyncState {
21 /// Creates a new state that will trigger a send on the first tick.
22 #[must_use]
23 pub const fn new() -> Self {
24 Self {
25 last_health: -1.0,
26 last_food: -1,
27 saturation_zero: true,
28 }
29 }
30
31 /// Returns true if the given values differ from what was last sent.
32 #[expect(
33 clippy::float_cmp,
34 reason = "intentional exact comparison: we only want to send updates when the value changes from what we last sent"
35 )]
36 #[must_use]
37 pub fn needs_update(&self, health: f32, food: i32, saturation_zero: bool) -> bool {
38 self.last_health != health
39 || self.last_food != food
40 || self.saturation_zero != saturation_zero
41 }
42
43 /// Records that we just sent the given values to the client.
44 pub const fn record_sent(&mut self, health: f32, food: i32, saturation_zero: bool) {
45 self.last_health = health;
46 self.last_food = food;
47 self.saturation_zero = saturation_zero;
48 }
49
50 /// Invalidates the state so the next tick will re-send.
51 ///
52 /// Vanilla: `resetSentInfo`.
53 pub const fn invalidate(&mut self) {
54 self.last_health = -1.0e8;
55 }
56
57 /// Resets to respawn defaults (forces re-send on next tick).
58 pub const fn reset_for_respawn(&mut self) {
59 self.last_health = -1.0;
60 self.last_food = -1;
61 }
62}
63
64impl Player {
65 /// Invalidates the delta-tracking state so that the next `tick()` will send
66 /// `CSetHealth` to the client (vanilla: `resetSentInfo`).
67 pub fn reset_sent_info(&self) {
68 self.health_sync.lock().invalidate();
69 }
70}