From 03a496fc7d952b1136cb717f2e8d1ccfb798ee64 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 30 Oct 2025 11:02:50 -0530 Subject: cleanup some code related to mob effects --- CHANGELOG.md | 1 + azalea-client/src/plugins/packet/game/mod.rs | 10 +- azalea-entity/src/effects.rs | 106 ++++++++++++--------- azalea-entity/src/mining.rs | 10 +- azalea-physics/src/lib.rs | 6 +- .../src/packets/game/c_update_mob_effect.rs | 45 +-------- 6 files changed, 78 insertions(+), 100 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ddad98b..1d83334b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ is breaking anyways, semantic versioning is not followed. - Rename `SendPacketEvent` to `SendGamePacketEvent` and `PingEvent` to `GamePingEvent`. - Swap the order of the type parameters in entity filtering functions so query is first, then filter. - Add optional `timeout_ticks` field to `Client::open_container_at`. +- Implement initial support for mob effects, including jump boost, haste, conduit power, and mining fatigue. (@ShayBox) ### Fixed diff --git a/azalea-client/src/plugins/packet/game/mod.rs b/azalea-client/src/plugins/packet/game/mod.rs index 40446cee..fd5ebef2 100644 --- a/azalea-client/src/plugins/packet/game/mod.rs +++ b/azalea-client/src/plugins/packet/game/mod.rs @@ -8,7 +8,7 @@ use azalea_core::{ }; use azalea_entity::{ ActiveEffects, Dead, EntityBundle, EntityKindComponent, HasClientLoaded, LoadedBy, LocalEntity, - LookDirection, MobEffectData, Physics, PlayerAbilities, Position, RelativeEntityUpdate, + LookDirection, Physics, PlayerAbilities, Position, RelativeEntityUpdate, indexing::{EntityIdIndex, EntityUuidIndex}, metadata::{Health, apply_metadata}, }; @@ -1108,13 +1108,7 @@ impl GamePacketHandler<'_> { debug!("Got update mob effect packet {p:?}"); let mob_effect = p.mob_effect; - let effect_data = MobEffectData::new( - p.effect_amplifier, - p.effect_duration_ticks, - p.flags.ambient, - p.flags.show_particles, - p.flags.show_icon, - ); + let effect_data = &p.data; as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>( self.ecs, diff --git a/azalea-entity/src/effects.rs b/azalea-entity/src/effects.rs index d905414d..9519c627 100644 --- a/azalea-entity/src/effects.rs +++ b/azalea-entity/src/effects.rs @@ -1,46 +1,69 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + io::{self, Cursor, Write}, +}; +use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError}; +use azalea_core::bitset::FixedBitSet; use azalea_registry::MobEffect; use bevy_ecs::component::Component; -/// Data about an active mob effect that the client knows about. -#[derive(Clone, Debug, Default)] +/// Data about an active mob effect. +#[derive(Clone, Debug, Default, PartialEq, AzBuf)] pub struct MobEffectData { + /// The effect's amplifier level, starting at 0 if present. + #[var] pub amplifier: u32, + #[var] pub duration_ticks: u32, + + pub flags: MobEffectFlags, +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct MobEffectFlags { pub ambient: bool, pub show_particles: bool, pub show_icon: bool, + pub blend: bool, } -impl MobEffectData { - pub fn new( - amplifier: u32, - duration_ticks: u32, - ambient: bool, - show_particles: bool, - show_icon: bool, - ) -> Self { - Self { - amplifier, - duration_ticks, + +impl AzaleaRead for MobEffectFlags { + fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result { + let bitset = FixedBitSet::<8>::azalea_read(buf)?; + let ambient = bitset.index(0); + let show_particles = bitset.index(1); + let show_icon = bitset.index(2); + let blend = bitset.index(3); + Ok(Self { ambient, show_particles, show_icon, - } + blend, + }) } +} - pub fn is_ambient(&self) -> bool { - self.ambient - } - pub fn should_show_particles(&self) -> bool { - self.show_particles - } - pub fn should_show_icon(&self) -> bool { - self.show_icon +impl AzaleaWrite for MobEffectFlags { + fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> { + let mut bitset = FixedBitSet::<8>::new(); + if self.ambient { + bitset.set(0); + } + if self.show_particles { + bitset.set(1); + } + if self.show_icon { + bitset.set(2); + } + if self.blend { + bitset.set(3); + } + bitset.azalea_write(buf) } } -/// Component storing the active mob effects on an entity. +/// An ECS component that stores the active mob effects on an entity. #[derive(Component, Clone, Debug, Default)] pub struct ActiveEffects(pub HashMap); impl ActiveEffects { @@ -52,6 +75,7 @@ impl ActiveEffects { self.0.remove(&effect) } + /// Get the amplifier level for the effect, starting at 0. pub fn get_level(&self, effect: MobEffect) -> Option { self.0.get(&effect).map(|data| data.amplifier) } @@ -59,27 +83,23 @@ impl ActiveEffects { pub fn get(&self, effect: MobEffect) -> Option<&MobEffectData> { self.0.get(&effect) } -} -/// Returns the level (amplifier) of the given effect, or `None` if the effect -/// is not active. The lowest level is 0. -pub fn get_effect(active_effects: &ActiveEffects, effect: MobEffect) -> Option { - active_effects.get_level(effect) -} - -/// Returns the amplifier for dig speed (haste / conduit power), if present. -pub fn get_dig_speed_amplifier(active_effects: &ActiveEffects) -> Option { - let effect_plus_one = u32::max( - get_effect(active_effects, MobEffect::Haste) + /// Returns the amplifier for dig speed (haste / conduit power), if present. + pub fn get_dig_speed_amplifier(&self) -> Option { + let haste_level = self + .get_level(MobEffect::Haste) .map(|level| level + 1) - .unwrap_or_default(), - get_effect(active_effects, MobEffect::ConduitPower) + .unwrap_or_default(); + let conduit_power_level = self + .get_level(MobEffect::ConduitPower) .map(|level| level + 1) - .unwrap_or_default(), - ); - if effect_plus_one > 0 { - Some(effect_plus_one - 1) - } else { - None + .unwrap_or_default(); + + let effect_plus_one = u32::max(haste_level, conduit_power_level); + if effect_plus_one > 0 { + Some(effect_plus_one - 1) + } else { + None + } } } diff --git a/azalea-entity/src/mining.rs b/azalea-entity/src/mining.rs index 7c142020..2008da34 100644 --- a/azalea-entity/src/mining.rs +++ b/azalea-entity/src/mining.rs @@ -1,8 +1,8 @@ use azalea_block::{BlockBehavior, BlockTrait}; use azalea_core::tier::get_item_tier; -use azalea_registry as registry; +use azalea_registry::{self as registry, MobEffect}; -use crate::{ActiveEffects, FluidOnEyes, Physics, effects}; +use crate::{ActiveEffects, FluidOnEyes, Physics}; /// How much progress is made towards mining the block per tick, as a /// percentage. @@ -98,13 +98,11 @@ fn destroy_speed( // efficiency_level + 1) as f32; } // } - if let Some(dig_speed_amplifier) = effects::get_dig_speed_amplifier(active_effects) { + if let Some(dig_speed_amplifier) = active_effects.get_dig_speed_amplifier() { base_destroy_speed *= 1. + (dig_speed_amplifier + 1) as f32 * 0.2; } - if let Some(dig_slowdown) = - effects::get_effect(active_effects, registry::MobEffect::MiningFatigue) - { + if let Some(dig_slowdown) = active_effects.get_level(MobEffect::MiningFatigue) { let multiplier = match dig_slowdown { 0 => 0.3, 1 => 0.09, diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs index e3b95484..ad4626fb 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -350,10 +350,14 @@ pub fn jump_from_ground( let base_jump = jump_power(&world, position); let jump_power = base_jump + jump_boost_power(active_effects); + if jump_power <= 1.0E-5 { + return; + } + let old_delta_movement = physics.velocity; physics.velocity = Vec3 { x: old_delta_movement.x, - y: f64::from(jump_power), + y: f64::max(jump_power as f64, old_delta_movement.y), z: old_delta_movement.z, }; if *sprinting { diff --git a/azalea-protocol/src/packets/game/c_update_mob_effect.rs b/azalea-protocol/src/packets/game/c_update_mob_effect.rs index 896d547b..e9892950 100644 --- a/azalea-protocol/src/packets/game/c_update_mob_effect.rs +++ b/azalea-protocol/src/packets/game/c_update_mob_effect.rs @@ -1,6 +1,5 @@ -use std::io::{Cursor, Write}; - -use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError}; +use azalea_buf::AzBuf; +use azalea_entity::MobEffectData; use azalea_protocol_macros::ClientboundGamePacket; use azalea_registry::MobEffect; use azalea_world::MinecraftEntityId; @@ -10,43 +9,5 @@ pub struct ClientboundUpdateMobEffect { #[var] pub entity_id: MinecraftEntityId, pub mob_effect: MobEffect, - #[var] - pub effect_amplifier: u32, - #[var] - pub effect_duration_ticks: u32, - pub flags: MobEffectFlags, -} - -#[derive(Clone, Debug, Default, PartialEq)] -pub struct MobEffectFlags { - pub ambient: bool, - pub show_particles: bool, - pub show_icon: bool, -} - -impl AzaleaRead for MobEffectFlags { - fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result { - let bits = u8::azalea_read(buf)?; - Ok(MobEffectFlags { - ambient: bits & 0x01 != 0, - show_particles: bits & 0x02 != 0, - show_icon: bits & 0x04 != 0, - }) - } -} - -impl AzaleaWrite for MobEffectFlags { - fn azalea_write(&self, buf: &mut impl Write) -> std::io::Result<()> { - let mut bits = 0; - if self.ambient { - bits |= 0x01; - } - if self.show_particles { - bits |= 0x02; - } - if self.show_icon { - bits |= 0x04; - } - bits.azalea_write(buf) - } + pub data: MobEffectData, } -- cgit v1.2.3