fix: persist air supply across relog (#2646)

Air was not saved to NBT, causing it to reset to full on every
login. This allowed players to exploit drowning by relogging to
restore lost air bubbles.
This commit is contained in:
MythicSorcerer
2026-08-02 11:11:53 -07:00
committed by GitHub
parent 28eefe7518
commit 3b0fdfa4f8
2 changed files with 28 additions and 0 deletions

View File

@@ -4319,6 +4319,21 @@ impl NBTStorage for Player {
// Store food level, saturation, exhaustion, and tick timer
self.hunger_manager.write_nbt(nbt).await;
nbt.put_int(
"AirSupply",
self.breath_manager
.air_supply
.load(Ordering::Relaxed)
.clamp(0, super::breath::MAX_AIR),
);
nbt.put_int(
"DrowningTick",
self.breath_manager
.drowning_tick
.load(Ordering::Relaxed)
.clamp(0, super::breath::DROWNING_INTERVAL - 1),
);
nbt.put_string(
"Dimension",
self.world().dimension.minecraft_name.to_string(),
@@ -4371,6 +4386,18 @@ impl NBTStorage for Player {
self.hunger_manager.read_nbt(nbt).await;
if let Some(air) = nbt.get_int("AirSupply") {
self.breath_manager
.air_supply
.store(air.clamp(0, super::breath::MAX_AIR), Ordering::Relaxed);
}
if let Some(tick) = nbt.get_int("DrowningTick") {
self.breath_manager.drowning_tick.store(
tick.clamp(0, super::breath::DROWNING_INTERVAL - 1),
Ordering::Relaxed,
);
}
// Load any saved spawnpoint data (SpawnX/SpawnY/SpawnZ, SpawnDimension, SpawnForced)
if let (Some(x), Some(y), Some(z)) = (
nbt.get_int("SpawnX"),

View File

@@ -3175,6 +3175,7 @@ impl World {
.await;
player.send_active_effects().await;
player.breath_manager.send_air_supply(player);
self.send_player_equipment(player).await;
if let crate::net::ClientPlatform::Java(java_client) = player.client.as_ref()