feat(bedrock): implement Bedrock status effect packet and translation

This commit is contained in:
Alexander Medvedev
2026-08-12 21:31:31 +02:00
parent be279fa274
commit f49e29aa3f
6 changed files with 135 additions and 8 deletions

View File

@@ -419,6 +419,25 @@ impl StatusEffect {
attribute_modifiers: &[],
};
#[must_use]
pub const fn to_bedrock_id(&self) -> i32 {
match self.id {
0..=22 => self.id as i32 + 1,
24 => 24,
27 => 27,
28 => 26,
30 => 28,
31 => 29,
32 => 30,
33 => 31,
34 => 32,
35 => 33,
36 => 34,
37 => 35,
38 => 36,
_ => self.id as i32 + 1,
}
}
#[must_use]
pub fn from_name(name: &str) -> Option<&'static Self> {
match name {
"absorption" => Some(&Self::ABSORPTION),

View File

@@ -0,0 +1,49 @@
use pumpkin_macros::packet;
use crate::{
codec::{var_int::VarInt, var_ulong::VarULong},
serial::PacketWrite,
};
#[derive(PacketWrite)]
#[packet(28)]
pub struct CMobEffect {
pub runtime_entity_id: VarULong,
pub event_id: u8,
pub effect_id: VarInt,
pub amplifier: VarInt,
pub particles: bool,
pub duration: VarInt,
pub tick: VarULong,
pub ambient: bool,
}
impl CMobEffect {
pub const EVENT_ADD: u8 = 1;
pub const EVENT_MODIFY: u8 = 2;
pub const EVENT_REMOVE: u8 = 3;
#[expect(clippy::too_many_arguments)]
#[must_use]
pub const fn new(
runtime_entity_id: VarULong,
event_id: u8,
effect_id: VarInt,
amplifier: VarInt,
particles: bool,
duration: VarInt,
tick: VarULong,
ambient: bool,
) -> Self {
Self {
runtime_entity_id,
event_id,
effect_id,
amplifier,
particles,
duration,
tick,
ambient,
}
}
}

View File

@@ -20,6 +20,7 @@ pub mod item_registry;
pub mod item_stack_response;
pub mod level_chunk;
pub mod level_event;
pub mod mob_effect;
pub mod mob_equipment;
pub mod modal_form_request;
pub mod move_actor_absolute;
@@ -76,6 +77,7 @@ pub use item_stack_response::*;
pub use level_chunk::*;
pub use level_event::*;
pub use level_sound_event::*;
pub use mob_effect::*;
pub use mob_equipment::*;
pub use modal_form_request::*;
pub use move_actor_absolute::*;

View File

@@ -527,6 +527,7 @@ impl LivingEntity {
self.entity.entity_id
}
#[expect(clippy::too_many_lines)]
pub async fn add_effect(&self, effect: Effect) {
// Apply instant effects immediately before storing
if effect.effect_type == &StatusEffect::INSTANT_HEALTH {
@@ -625,7 +626,7 @@ impl LivingEntity {
flag |= 8;
}
let packet = CUpdateMobEffect::new(
let je_packet = CUpdateMobEffect::new(
self.entity.entity_id.into(),
VarInt(i32::from(effect.effect_type.id)),
effect.amplifier.into(),
@@ -633,7 +634,22 @@ impl LivingEntity {
flag,
);
self.entity.world.load().broadcast_packet_all(&packet);
let be_packet = pumpkin_protocol::bedrock::client::CMobEffect::new(
VarULong(self.entity.entity_id as u64),
pumpkin_protocol::bedrock::client::CMobEffect::EVENT_ADD,
VarInt(effect.effect_type.to_bedrock_id()),
VarInt(i32::from(effect.amplifier)),
effect.show_particles,
VarInt(effect.duration),
VarULong(0),
effect.ambient,
);
let chunk_pos = self.entity.chunk_pos.load();
self.entity
.world
.load()
.broadcast_to_chunk_editioned_sync(chunk_pos, &je_packet, &be_packet);
}
pub async fn remove_effect(&self, effect_type: &'static StatusEffect) -> bool {

View File

@@ -466,14 +466,23 @@ impl World {
pub fn send_remove_mob_effect(&self, entity: &Entity, effect_type: &'static StatusEffect) {
let chunk_pos = entity.chunk_pos.load();
self.broadcast_to_chunk(
chunk_pos,
&CRemoveMobEffect::new(entity.entity_id.into(), VarInt(i32::from(effect_type.id))),
let je_packet =
CRemoveMobEffect::new(entity.entity_id.into(), VarInt(i32::from(effect_type.id)));
let be_packet = pumpkin_protocol::bedrock::client::CMobEffect::new(
VarULong(entity.entity_id as u64),
pumpkin_protocol::bedrock::client::CMobEffect::EVENT_REMOVE,
VarInt(effect_type.to_bedrock_id()),
VarInt(0),
false,
VarInt(0),
VarULong(0),
false,
);
self.broadcast_to_chunk_editioned_sync(chunk_pos, &je_packet, &be_packet);
}
pub fn send_add_mob_effect(&self, entity: &Entity, effect: &pumpkin_data::potion::Effect) {
// TODO: only nearby
let chunk_pos = entity.chunk_pos.load();
let mut flags: i8 = 0;
if effect.ambient {
flags |= 0x01;
@@ -485,13 +494,25 @@ impl World {
flags |= 0x04;
}
self.broadcast_packet_all(&CUpdateMobEffect::new(
let je_packet = CUpdateMobEffect::new(
VarInt(entity.entity_id),
VarInt(i32::from(effect.effect_type.id)),
VarInt(i32::from(effect.amplifier)),
VarInt(effect.duration),
flags,
));
);
let be_packet = pumpkin_protocol::bedrock::client::CMobEffect::new(
VarULong(entity.entity_id as u64),
pumpkin_protocol::bedrock::client::CMobEffect::EVENT_ADD,
VarInt(effect.effect_type.to_bedrock_id()),
VarInt(i32::from(effect.amplifier)),
effect.show_particles,
VarInt(effect.duration),
VarULong(0),
effect.ambient,
);
self.broadcast_to_chunk_editioned_sync(chunk_pos, &je_packet, &be_packet);
}
pub fn set_difficulty(&self, difficulty: Difficulty) {

View File

@@ -157,6 +157,26 @@ pub fn build() -> TokenStream {
impl StatusEffect {
#variants
#[must_use]
pub const fn to_bedrock_id(&self) -> i32 {
match self.id {
0..=22 => self.id as i32 + 1,
24 => 24,
27 => 27,
28 => 26,
30 => 28,
31 => 29,
32 => 30,
33 => 31,
34 => 32,
35 => 33,
36 => 34,
37 => 35,
38 => 36,
_ => self.id as i32 + 1,
}
}
#[must_use]
pub fn from_name(name: &str) -> Option<&'static Self> {
match name {