fix: many non event firing
This commit is contained in:
parent
feba9acb25
commit
f8d1627697
@ -359,19 +359,24 @@ impl ClientPacket for CSpawnLivingEntity {
|
|||||||
self.velocity.write_legacy(&mut write)?;
|
self.velocity.write_legacy(&mut write)?;
|
||||||
|
|
||||||
if !v1_15 {
|
if !v1_15 {
|
||||||
|
if v1_9 {
|
||||||
if let Some(metadata) = &self.metadata {
|
if let Some(metadata) = &self.metadata {
|
||||||
write.write_slice(metadata)?;
|
write.write_slice(metadata)?;
|
||||||
} else if v1_9 {
|
|
||||||
write.write_u8(0xFF)?;
|
|
||||||
} else {
|
} else {
|
||||||
// In <= 1.8 (specifically 1.7.10), DataWatcher requires at least one entry
|
write.write_u8(0xFF)?;
|
||||||
// so that `readWatchedObjectsFromPacketBuffer` returns a non-null List,
|
}
|
||||||
// avoiding a NullPointerException in `S0FPacketSpawnMob.func_149027_c()`.
|
} else if let Some(metadata) = &self.metadata
|
||||||
// (0 << 5) | 0 = 0 (type: byte, index: 0 flags), 0 (value), 127 (terminator)
|
&& metadata.last().copied() == Some(127)
|
||||||
write.write_slice(&[0x00, 0x00, 127])?;
|
{
|
||||||
|
write.write_slice(metadata)?;
|
||||||
|
} else {
|
||||||
|
// In <= 1.8, write the 127 (0x7F) terminator byte for the DataWatcher list,
|
||||||
|
// matching vanilla and PacketEvents.
|
||||||
|
write.write_u8(127)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,20 @@ impl BlockBehaviour for FarmlandBlock {
|
|||||||
// TODO: add rain check. Remember to check which one is most optimized.
|
// TODO: add rain check. Remember to check which one is most optimized.
|
||||||
if is_water_nearby(args.world, args.position) {
|
if is_water_nearby(args.world, args.position) {
|
||||||
let mut props = FarmlandProperties::default(args.block);
|
let mut props = FarmlandProperties::default(args.block);
|
||||||
props.moisture = 7;
|
let mut new_moisture = 7;
|
||||||
|
if let Some(server) = args.world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::moisture_change::MoistureChangeEvent::new(
|
||||||
|
*args.position,
|
||||||
|
args.world.clone(),
|
||||||
|
new_moisture,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_moisture = event.new_moisture;
|
||||||
|
}
|
||||||
|
props.moisture = new_moisture.clamp(0, 7) as u8;
|
||||||
args.world.set_block_state(
|
args.world.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
props.to_state_id(args.block),
|
props.to_state_id(args.block),
|
||||||
@ -80,7 +93,20 @@ impl BlockBehaviour for FarmlandBlock {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
props.moisture = (props.moisture as i32 - 1).clamp(0, 7) as u8;
|
let mut new_moisture = (props.moisture as i32 - 1).clamp(0, 7);
|
||||||
|
if let Some(server) = args.world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::moisture_change::MoistureChangeEvent::new(
|
||||||
|
*args.position,
|
||||||
|
args.world.clone(),
|
||||||
|
new_moisture,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_moisture = event.new_moisture;
|
||||||
|
}
|
||||||
|
props.moisture = new_moisture.clamp(0, 7) as u8;
|
||||||
args.world.set_block_state(
|
args.world.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
props.to_state_id(args.block),
|
props.to_state_id(args.block),
|
||||||
|
|||||||
@ -144,6 +144,17 @@ impl FireBlock {
|
|||||||
let block = world.get_block(pos);
|
let block = world.get_block(pos);
|
||||||
let odds = Self::get_burn_odds(block);
|
let odds = Self::get_burn_odds(block);
|
||||||
if rand::rng().random_range(0..chance) < odds {
|
if rand::rng().random_range(0..chance) < odds {
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::block_burn::BlockBurnEvent {
|
||||||
|
igniting_block: &Block::FIRE,
|
||||||
|
block,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let old_block = block;
|
let old_block = block;
|
||||||
if rand::rng().random_range(0..(age + 10) as i32) < 5
|
if rand::rng().random_range(0..(age + 10) as i32) < 5
|
||||||
&& !Self::is_near_rain(world.as_ref(), pos)
|
&& !Self::is_near_rain(world.as_ref(), pos)
|
||||||
@ -416,10 +427,24 @@ impl BlockBehaviour for FireBlock {
|
|||||||
let mut new_fire_props =
|
let mut new_fire_props =
|
||||||
FireProperties::from_state_id(fire_state_id);
|
FireProperties::from_state_id(fire_state_id);
|
||||||
new_fire_props.age = spread_age;
|
new_fire_props.age = spread_age;
|
||||||
|
let new_state_id = new_fire_props.to_state_id(&Block::FIRE);
|
||||||
|
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::block_spread::BlockSpreadEvent::new(
|
||||||
|
*pos,
|
||||||
|
offset_pos,
|
||||||
|
world.clone(),
|
||||||
|
new_state_id,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
world.set_block_state(
|
world.set_block_state(
|
||||||
&offset_pos,
|
&offset_pos,
|
||||||
new_fire_props.to_state_id(&Block::FIRE),
|
new_state_id,
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,6 +101,16 @@ impl BlockBehaviour for LeavesBlock {
|
|||||||
let state_id = args.world.get_block_state_id(args.position);
|
let state_id = args.world.get_block_state_id(args.position);
|
||||||
let props = OakLeavesLikeProperties::from_state_id(state_id);
|
let props = OakLeavesLikeProperties::from_state_id(state_id);
|
||||||
if !props.persistent && props.distance == DECAY_DISTANCE {
|
if !props.persistent && props.distance == DECAY_DISTANCE {
|
||||||
|
if let Some(server) = args.world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::leaves_decay::LeavesDecayEvent::new(
|
||||||
|
*args.position,
|
||||||
|
args.world.clone(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
args.world
|
args.world
|
||||||
.break_block(args.position, None, BlockFlags::empty());
|
.break_block(args.position, None, BlockFlags::empty());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,11 +73,27 @@ trait CropBlockBase: PlantBlockBase {
|
|||||||
let f = get_available_moisture(world, pos, block);
|
let f = get_available_moisture(world, pos, block);
|
||||||
if rand::rng().random_range(0..=(25.0 / f).floor() as i64) == 0 {
|
if rand::rng().random_range(0..=(25.0 / f).floor() as i64) == 0 {
|
||||||
let new_state_id = self.state_with_age(block, state, age + 1);
|
let new_state_id = self.state_with_age(block, state, age + 1);
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::block_grow::BlockGrowEvent::new(
|
||||||
|
world.clone(),
|
||||||
|
block,
|
||||||
|
state,
|
||||||
|
block,
|
||||||
|
new_state_id,
|
||||||
|
*pos,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
world.set_block_state(pos, event.new_state_id, BlockFlags::NOTIFY_LISTENERS);
|
||||||
|
} else {
|
||||||
world.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_LISTENERS);
|
world.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_LISTENERS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_available_moisture(world: &World, pos: &BlockPos, block: &Block) -> f32 {
|
pub fn get_available_moisture(world: &World, pos: &BlockPos, block: &Block) -> f32 {
|
||||||
let mut moisture = 1.0;
|
let mut moisture = 1.0;
|
||||||
|
|||||||
@ -37,12 +37,28 @@ impl BlockBehaviour for SweetBerryBushBlock {
|
|||||||
match props.age {
|
match props.age {
|
||||||
2 | 3 => {
|
2 | 3 => {
|
||||||
let index = props.age;
|
let index = props.age;
|
||||||
props.age = 1;
|
|
||||||
let count: u8 = rand::rng().random_range((index - 1)..=(index));
|
let count: u8 = rand::rng().random_range((index - 1)..=(index));
|
||||||
for _ in 0..count {
|
let mut drops = vec![ItemStack::new(count, &Item::SWEET_BERRIES)];
|
||||||
|
if let Some(player_arc) = args.world.get_player_by_uuid(args.player.gameprofile.id)
|
||||||
|
&& let Some(server) = args.world.server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_harvest_block::PlayerHarvestBlockEvent {
|
||||||
|
player: player_arc,
|
||||||
|
block_pos: *args.position,
|
||||||
|
harvested_items: drops.clone(),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return BlockActionResult::Pass;
|
||||||
|
}
|
||||||
|
drops = event.harvested_items;
|
||||||
|
}
|
||||||
|
props.age = 1;
|
||||||
|
for stack in drops {
|
||||||
args.world.drop_stack(
|
args.world.drop_stack(
|
||||||
args.position,
|
args.position,
|
||||||
ItemStack::new(1, &Item::SWEET_BERRIES), //
|
stack,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
args.world.set_block_state(
|
args.world.set_block_state(
|
||||||
|
|||||||
@ -133,6 +133,18 @@ impl CrafterBlock {
|
|||||||
world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS);
|
world.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS);
|
||||||
|
|
||||||
let mut result_stack = ItemStack::new(recipe_result.count, item);
|
let mut result_stack = ItemStack::new(recipe_result.count, item);
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::crafter_craft::CrafterCraftEvent::new(
|
||||||
|
*pos,
|
||||||
|
world.clone(),
|
||||||
|
result_stack.clone(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result_stack = event.result;
|
||||||
|
}
|
||||||
Self::dispense_item(world, pos, crafter, &mut result_stack, props.orientation);
|
Self::dispense_item(world, pos, crafter, &mut result_stack, props.orientation);
|
||||||
|
|
||||||
for i in 0..CrafterBlockEntity::INVENTORY_SIZE {
|
for i in 0..CrafterBlockEntity::INVENTORY_SIZE {
|
||||||
|
|||||||
@ -106,6 +106,20 @@ impl BrewingStandBlockEntity {
|
|||||||
|
|
||||||
let ingredient_id = ingredient.get_item().id;
|
let ingredient_id = ingredient.get_item().id;
|
||||||
|
|
||||||
|
// Fire BrewEvent before mutating items
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut brew_event = crate::plugin::api::events::inventory::brew::BrewEvent::new(
|
||||||
|
self.position,
|
||||||
|
self.fuel.load(Ordering::Relaxed) as u8,
|
||||||
|
);
|
||||||
|
server
|
||||||
|
.plugin_manager
|
||||||
|
.fire_blocking(&server, &mut brew_event);
|
||||||
|
if brew_event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Brew potion slots
|
// Brew potion slots
|
||||||
let mut ingredient_used = false;
|
let mut ingredient_used = false;
|
||||||
{
|
{
|
||||||
@ -171,17 +185,6 @@ impl BrewingStandBlockEntity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire BrewEvent
|
|
||||||
if let Some(server) = world.server.upgrade() {
|
|
||||||
let mut brew_event = crate::plugin::api::events::inventory::brew::BrewEvent::new(
|
|
||||||
self.position,
|
|
||||||
self.fuel.load(Ordering::Relaxed) as u8,
|
|
||||||
);
|
|
||||||
server
|
|
||||||
.plugin_manager
|
|
||||||
.fire_blocking(&server, &mut brew_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if remaining ingredient matches or clear it
|
// Check if remaining ingredient matches or clear it
|
||||||
if let Ok(items) = self.items.read() {
|
if let Ok(items) = self.items.read() {
|
||||||
let remaining = &items[3];
|
let remaining = &items[3];
|
||||||
@ -441,6 +444,7 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn tick(&self, world: &Arc<crate::world::World>) {
|
fn tick(&self, world: &Arc<crate::world::World>) {
|
||||||
// Refill fuel counter from fuel item if needed
|
// Refill fuel counter from fuel item if needed
|
||||||
let fuel_refilled = self.fuel.load(Ordering::Relaxed) <= 0
|
let fuel_refilled = self.fuel.load(Ordering::Relaxed) <= 0
|
||||||
@ -458,10 +462,18 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
|
|||||||
server
|
server
|
||||||
.plugin_manager
|
.plugin_manager
|
||||||
.fire_blocking(&server, &mut fuel_event);
|
.fire_blocking(&server, &mut fuel_event);
|
||||||
|
if fuel_event.cancelled {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.fuel.store(fuel_event.fuel_power as i32, Ordering::Relaxed);
|
||||||
|
items[4].decrement(1);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
self.fuel.store(20, Ordering::Relaxed);
|
self.fuel.store(20, Ordering::Relaxed);
|
||||||
items[4].decrement(1);
|
items[4].decrement(1);
|
||||||
true
|
true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
@ -493,9 +505,24 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
|
|||||||
self.mark_dirty();
|
self.mark_dirty();
|
||||||
}
|
}
|
||||||
} else if brewable && self.fuel.load(Ordering::Relaxed) > 0 {
|
} else if brewable && self.fuel.load(Ordering::Relaxed) > 0 {
|
||||||
|
let brew_time = if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut start_event = crate::plugin::api::events::block::brewing_start::BrewingStartEvent::new(
|
||||||
|
self.position,
|
||||||
|
world.clone(),
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut start_event);
|
||||||
|
if start_event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
start_event.brewing_time
|
||||||
|
} else {
|
||||||
|
400
|
||||||
|
};
|
||||||
|
|
||||||
// Start new brewing cycle
|
// Start new brewing cycle
|
||||||
self.fuel.fetch_sub(1, Ordering::Relaxed);
|
self.fuel.fetch_sub(1, Ordering::Relaxed);
|
||||||
self.brew_time.store(400, Ordering::Relaxed);
|
self.brew_time.store(brew_time, Ordering::Relaxed);
|
||||||
*self
|
*self
|
||||||
.ingredient_item
|
.ingredient_item
|
||||||
.lock()
|
.lock()
|
||||||
|
|||||||
@ -108,7 +108,22 @@ impl BlockEntity for CampfireBlockEntity {
|
|||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
if *total <= 0 {
|
if *total <= 0 {
|
||||||
*total = recipe.cookingtime;
|
let mut cooking_time = recipe.cookingtime;
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::campfire_start::CampfireStartEvent::new(
|
||||||
|
self.position,
|
||||||
|
world.clone(),
|
||||||
|
stack.clone(),
|
||||||
|
slot as u8,
|
||||||
|
cooking_time,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cooking_time = event.cooking_time;
|
||||||
|
}
|
||||||
|
*total = cooking_time;
|
||||||
}
|
}
|
||||||
*total
|
*total
|
||||||
};
|
};
|
||||||
@ -130,7 +145,20 @@ impl BlockEntity for CampfireBlockEntity {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = ItemStack::new(recipe.result.count, result_item);
|
let mut result = ItemStack::new(recipe.result.count, result_item);
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::block_cook::BlockCookEvent::new(
|
||||||
|
self.position,
|
||||||
|
world.clone(),
|
||||||
|
stack.clone(),
|
||||||
|
result.clone(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result = event.result;
|
||||||
|
}
|
||||||
Self::spawn_cooked_item(world, self.position, result);
|
Self::spawn_cooked_item(world, self.position, result);
|
||||||
|
|
||||||
*self.items[slot]
|
*self.items[slot]
|
||||||
|
|||||||
@ -398,6 +398,8 @@ macro_rules! impl_block_entity_for_cooking {
|
|||||||
base_fuel_ticks
|
base_fuel_ticks
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut burn_ticks = adjusted_fuel_ticks;
|
||||||
|
let mut burn_cancelled = false;
|
||||||
if let Some(server) = world.server.upgrade() {
|
if let Some(server) = world.server.upgrade() {
|
||||||
let mut burn_event = $crate::plugin::api::events::inventory::furnace_burn::FurnaceBurnEvent::new(
|
let mut burn_event = $crate::plugin::api::events::inventory::furnace_burn::FurnaceBurnEvent::new(
|
||||||
self.position,
|
self.position,
|
||||||
@ -405,9 +407,15 @@ macro_rules! impl_block_entity_for_cooking {
|
|||||||
adjusted_fuel_ticks as u32,
|
adjusted_fuel_ticks as u32,
|
||||||
);
|
);
|
||||||
server.plugin_manager.fire_blocking(&server, &mut burn_event);
|
server.plugin_manager.fire_blocking(&server, &mut burn_event);
|
||||||
|
if burn_event.cancelled {
|
||||||
|
burn_cancelled = true;
|
||||||
|
} else {
|
||||||
|
burn_ticks = burn_event.burn_time as u16;
|
||||||
}
|
}
|
||||||
self.set_lit_time_remaining(adjusted_fuel_ticks);
|
}
|
||||||
self.set_lit_total_time(adjusted_fuel_ticks);
|
if !burn_cancelled {
|
||||||
|
self.set_lit_time_remaining(burn_ticks);
|
||||||
|
self.set_lit_total_time(burn_ticks);
|
||||||
|
|
||||||
if self.is_burning() {
|
if self.is_burning() {
|
||||||
is_dirty = true;
|
is_dirty = true;
|
||||||
@ -428,8 +436,10 @@ macro_rules! impl_block_entity_for_cooking {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.is_burning() && can_accept_output {
|
if self.is_burning() && can_accept_output {
|
||||||
|
let mut start_cancelled = false;
|
||||||
if self.get_cooking_time_spent() == 0 {
|
if self.get_cooking_time_spent() == 0 {
|
||||||
if let Some(server) = world.server.upgrade() {
|
if let Some(server) = world.server.upgrade() {
|
||||||
let mut start_event = $crate::plugin::api::events::inventory::furnace_start_smelt::FurnaceStartSmeltEvent::new(
|
let mut start_event = $crate::plugin::api::events::inventory::furnace_start_smelt::FurnaceStartSmeltEvent::new(
|
||||||
@ -438,8 +448,14 @@ macro_rules! impl_block_entity_for_cooking {
|
|||||||
self.get_cooking_total_time() as u32,
|
self.get_cooking_total_time() as u32,
|
||||||
);
|
);
|
||||||
server.plugin_manager.fire_blocking(&server, &mut start_event);
|
server.plugin_manager.fire_blocking(&server, &mut start_event);
|
||||||
|
if start_event.cancelled {
|
||||||
|
start_cancelled = true;
|
||||||
|
} else {
|
||||||
|
self.set_cooking_total_time(start_event.cooking_time as u16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !start_cancelled {
|
||||||
self.cooking_time_spent.fetch_add(1, Ordering::Relaxed);
|
self.cooking_time_spent.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
if self.get_cooking_time_spent() == self.get_cooking_total_time() {
|
if self.get_cooking_time_spent() == self.get_cooking_total_time() {
|
||||||
@ -448,6 +464,7 @@ macro_rules! impl_block_entity_for_cooking {
|
|||||||
let cooking_total_time = cooking_recipe.cookingtime;
|
let cooking_total_time = cooking_recipe.cookingtime;
|
||||||
self.set_cooking_total_time(cooking_total_time as u16);
|
self.set_cooking_total_time(cooking_total_time as u16);
|
||||||
|
|
||||||
|
let mut smelt_cancelled = false;
|
||||||
if let Some(server) = world.server.upgrade() {
|
if let Some(server) = world.server.upgrade() {
|
||||||
let mut smelt_event = $crate::plugin::api::events::inventory::furnace_smelt::FurnaceSmeltEvent::new(
|
let mut smelt_event = $crate::plugin::api::events::inventory::furnace_smelt::FurnaceSmeltEvent::new(
|
||||||
self.position,
|
self.position,
|
||||||
@ -455,11 +472,17 @@ macro_rules! impl_block_entity_for_cooking {
|
|||||||
cooking_recipe.result.id.to_string(),
|
cooking_recipe.result.id.to_string(),
|
||||||
);
|
);
|
||||||
server.plugin_manager.fire_blocking(&server, &mut smelt_event);
|
server.plugin_manager.fire_blocking(&server, &mut smelt_event);
|
||||||
|
if smelt_event.cancelled {
|
||||||
|
smelt_cancelled = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !smelt_cancelled {
|
||||||
self.craft_recipe(Some(cooking_recipe));
|
self.craft_recipe(Some(cooking_recipe));
|
||||||
is_dirty = true;
|
is_dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.set_cooking_time_spent(0);
|
self.set_cooking_time_spent(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -526,6 +526,18 @@ impl BlockRegistry {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if behaviour.is_bonemeal_success(args) {
|
if behaviour.is_bonemeal_success(args) {
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::block::block_fertilize::BlockFertilizeEvent::new(
|
||||||
|
*position,
|
||||||
|
world.clone(),
|
||||||
|
None,
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
behaviour.perform_bonemeal(args);
|
behaviour.perform_bonemeal(args);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|||||||
@ -155,6 +155,9 @@ impl Goal for BreakDoorGoal {
|
|||||||
);
|
);
|
||||||
if let Some(server) = world.server.upgrade() {
|
if let Some(server) = world.server.upgrade() {
|
||||||
server.plugin_manager.fire_blocking(&server, &mut event);
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
world.set_block_state(&door_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL);
|
world.set_block_state(&door_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL);
|
||||||
world.sync_world_event(
|
world.sync_world_event(
|
||||||
|
|||||||
@ -58,14 +58,11 @@ impl BreathManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let in_water = Self::is_eye_in_water(player);
|
let in_water = Self::is_eye_in_water(player);
|
||||||
|
let prev = self.air_supply.load(Ordering::Relaxed);
|
||||||
|
|
||||||
if in_water {
|
if in_water {
|
||||||
let prev = self
|
let mut new_air = (prev - AIR_DEPLETION_RATE).max(0);
|
||||||
.air_supply
|
|
||||||
.fetch_sub(AIR_DEPLETION_RATE, Ordering::Relaxed);
|
|
||||||
let new_air = (prev - AIR_DEPLETION_RATE).max(0);
|
|
||||||
if new_air != prev {
|
if new_air != prev {
|
||||||
self.air_supply.store(new_air, Ordering::Relaxed);
|
|
||||||
let server = player.world().server.upgrade();
|
let server = player.world().server.upgrade();
|
||||||
if let Some(server) = server {
|
if let Some(server) = server {
|
||||||
let mut event = crate::plugin::api::events::entity::entity_air_change::EntityAirChangeEvent::new(
|
let mut event = crate::plugin::api::events::entity::entity_air_change::EntityAirChangeEvent::new(
|
||||||
@ -73,7 +70,12 @@ impl BreathManager {
|
|||||||
new_air,
|
new_air,
|
||||||
);
|
);
|
||||||
server.plugin_manager.fire_blocking(&server, &mut event);
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
new_air = event.amount.clamp(0, MAX_AIR);
|
||||||
|
}
|
||||||
|
self.air_supply.store(new_air, Ordering::Relaxed);
|
||||||
self.send_air_supply(player);
|
self.send_air_supply(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,9 +90,20 @@ impl BreathManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let prev = self.air_supply.load(Ordering::Relaxed);
|
let mut new_air = (prev + AIR_RECOVERY_RATE).min(MAX_AIR);
|
||||||
let new_air = (prev + AIR_RECOVERY_RATE).min(MAX_AIR);
|
|
||||||
if new_air != prev {
|
if new_air != prev {
|
||||||
|
let server = player.world().server.upgrade();
|
||||||
|
if let Some(server) = server {
|
||||||
|
let mut event = crate::plugin::api::events::entity::entity_air_change::EntityAirChangeEvent::new(
|
||||||
|
player.entity_id(),
|
||||||
|
new_air,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_air = event.amount.clamp(0, MAX_AIR);
|
||||||
|
}
|
||||||
self.air_supply.store(new_air, Ordering::Relaxed);
|
self.air_supply.store(new_air, Ordering::Relaxed);
|
||||||
self.send_air_supply(player);
|
self.send_air_supply(player);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -407,6 +407,7 @@ impl CombatTracker {
|
|||||||
pub fn get_death_message(
|
pub fn get_death_message(
|
||||||
&self,
|
&self,
|
||||||
victim_name: pumpkin_util::text::TextComponent,
|
victim_name: pumpkin_util::text::TextComponent,
|
||||||
|
kill_credit_name: Option<pumpkin_util::text::TextComponent>,
|
||||||
) -> pumpkin_util::text::TextComponent {
|
) -> pumpkin_util::text::TextComponent {
|
||||||
if self.entries.is_empty() {
|
if self.entries.is_empty() {
|
||||||
return pumpkin_util::text::TextComponent::translate_cross(
|
return pumpkin_util::text::TextComponent::translate_cross(
|
||||||
@ -456,7 +457,7 @@ impl CombatTracker {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
pumpkin_data::damage::DeathMessageType::Default => {
|
pumpkin_data::damage::DeathMessageType::Default => {
|
||||||
self.get_default_death_message(victim_name, killing_blow)
|
self.get_default_death_message(victim_name, killing_blow, kill_credit_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,6 +586,7 @@ impl CombatTracker {
|
|||||||
&self,
|
&self,
|
||||||
victim_name: pumpkin_util::text::TextComponent,
|
victim_name: pumpkin_util::text::TextComponent,
|
||||||
killing_blow: &CombatEntry,
|
killing_blow: &CombatEntry,
|
||||||
|
kill_credit_name: Option<pumpkin_util::text::TextComponent>,
|
||||||
) -> pumpkin_util::text::TextComponent {
|
) -> pumpkin_util::text::TextComponent {
|
||||||
let damage_type = killing_blow.damage_type;
|
let damage_type = killing_blow.damage_type;
|
||||||
let msg_id = damage_type.message_id;
|
let msg_id = damage_type.message_id;
|
||||||
@ -598,18 +600,18 @@ impl CombatTracker {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
pumpkin_util::text::TextComponent::translate_cross(
|
pumpkin_util::text::TextComponent::translate_cross(
|
||||||
format!("death.attack.{msg_id}.player"),
|
format!("death.attack.{msg_id}"),
|
||||||
format!("death.attack.{msg_id}.player"),
|
format!("death.attack.{msg_id}"),
|
||||||
[victim_name, attacker_name.clone()],
|
[victim_name, attacker_name.clone()],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if let Some(killer) = self.get_killer_entry()
|
} else if let Some(killer_name) = kill_credit_name.or_else(|| {
|
||||||
&& let Some(killer_name) = &killer.attacker_name
|
self.get_killer_entry().and_then(|k| k.attacker_name.clone())
|
||||||
{
|
}) {
|
||||||
pumpkin_util::text::TextComponent::translate_cross(
|
pumpkin_util::text::TextComponent::translate_cross(
|
||||||
format!("death.attack.{msg_id}.player"),
|
format!("death.attack.{msg_id}.player"),
|
||||||
format!("death.attack.{msg_id}.player"),
|
format!("death.attack.{msg_id}.player"),
|
||||||
[victim_name, killer_name.clone()],
|
[victim_name, killer_name],
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
pumpkin_util::text::TextComponent::translate_cross(
|
pumpkin_util::text::TextComponent::translate_cross(
|
||||||
|
|||||||
@ -14,10 +14,9 @@ use pumpkin_util::Hand;
|
|||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicI32;
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::atomic::{
|
use std::sync::atomic::{
|
||||||
AtomicBool, AtomicU8,
|
AtomicBool, AtomicI32, AtomicI64, AtomicU8,
|
||||||
Ordering::{Relaxed, SeqCst},
|
Ordering::{Relaxed, SeqCst},
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
@ -119,6 +118,15 @@ pub struct LivingEntity {
|
|||||||
/// Tracks combat entries, assisted falls, kill credit, and death messages.
|
/// Tracks combat entries, assisted falls, kill credit, and death messages.
|
||||||
pub combat_tracker: std::sync::Mutex<CombatTracker>,
|
pub combat_tracker: std::sync::Mutex<CombatTracker>,
|
||||||
|
|
||||||
|
/// The ID of the player that last hurt this entity.
|
||||||
|
pub last_hurt_by_player_id: AtomicI32,
|
||||||
|
/// The tick at which this entity was last hurt by a player.
|
||||||
|
pub last_hurt_by_player_time: AtomicI64,
|
||||||
|
/// The ID of the mob/entity that last hurt this entity.
|
||||||
|
pub last_hurt_by_mob_id: AtomicI32,
|
||||||
|
/// The tick at which this entity was last hurt by a mob/entity.
|
||||||
|
pub last_hurt_by_mob_time: AtomicI64,
|
||||||
|
|
||||||
water_movement_speed_multiplier: f32,
|
water_movement_speed_multiplier: f32,
|
||||||
livings_flags: AtomicU8,
|
livings_flags: AtomicU8,
|
||||||
|
|
||||||
@ -231,12 +239,56 @@ impl LivingEntity {
|
|||||||
last_attacking_id: AtomicI32::new(0),
|
last_attacking_id: AtomicI32::new(0),
|
||||||
last_attack_time: AtomicI32::new(0),
|
last_attack_time: AtomicI32::new(0),
|
||||||
combat_tracker: std::sync::Mutex::new(CombatTracker::new()),
|
combat_tracker: std::sync::Mutex::new(CombatTracker::new()),
|
||||||
|
last_hurt_by_player_id: AtomicI32::new(0),
|
||||||
|
last_hurt_by_player_time: AtomicI64::new(0),
|
||||||
|
last_hurt_by_mob_id: AtomicI32::new(0),
|
||||||
|
last_hurt_by_mob_time: AtomicI64::new(0),
|
||||||
movement_input: AtomicCell::new(Vector3::default()),
|
movement_input: AtomicCell::new(Vector3::default()),
|
||||||
water_movement_speed_multiplier,
|
water_movement_speed_multiplier,
|
||||||
last_block_pos: AtomicCell::new(None),
|
last_block_pos: AtomicCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the entity that should receive kill credit for this entity's death.
|
||||||
|
/// Following vanilla Java logic (`LivingEntity.getKillCredit`):
|
||||||
|
/// 1. Prioritize `last_hurt_by_player` if hurt within the last 100 ticks (5 seconds).
|
||||||
|
/// 2. Then `last_hurt_by_mob` if hurt within the last 100 ticks.
|
||||||
|
/// 3. Fall back to combat tracker's killer entry if available.
|
||||||
|
pub fn get_kill_credit(&self) -> Option<Arc<dyn EntityBase>> {
|
||||||
|
let world = self.entity.world.load();
|
||||||
|
let current_tick = world.level_info.load().day_time;
|
||||||
|
|
||||||
|
let player_id = self.last_hurt_by_player_id.load(Relaxed);
|
||||||
|
let player_time = self.last_hurt_by_player_time.load(Relaxed);
|
||||||
|
if player_id != 0
|
||||||
|
&& (current_tick - player_time).abs() <= 100
|
||||||
|
&& let Some(player) = world.get_entity_by_id(player_id)
|
||||||
|
{
|
||||||
|
return Some(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mob_id = self.last_hurt_by_mob_id.load(Relaxed);
|
||||||
|
let mob_time = self.last_hurt_by_mob_time.load(Relaxed);
|
||||||
|
if mob_id != 0
|
||||||
|
&& (current_tick - mob_time).abs() <= 100
|
||||||
|
&& let Some(mob) = world.get_entity_by_id(mob_id)
|
||||||
|
{
|
||||||
|
return Some(mob);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tracker = self
|
||||||
|
.combat_tracker
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
|
if let Some(killer) = tracker.get_killer_entry()
|
||||||
|
&& let Some(killer_id) = killer.attacker_id
|
||||||
|
{
|
||||||
|
return world.get_entity_by_id(killer_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Triggers location-based enchantment effects (e.g. Frost Walker) when the entity's block position changes.
|
/// Triggers location-based enchantment effects (e.g. Frost Walker) when the entity's block position changes.
|
||||||
pub fn on_changed_block(&self, caller: &dyn EntityBase, _pos: BlockPos) {
|
pub fn on_changed_block(&self, caller: &dyn EntityBase, _pos: BlockPos) {
|
||||||
let pos_f64 = self.entity.pos.load();
|
let pos_f64 = self.entity.pos.load();
|
||||||
@ -1607,28 +1659,40 @@ impl LivingEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::redundant_closure_for_method_calls)]
|
||||||
pub fn get_death_message(
|
pub fn get_death_message(
|
||||||
dyn_self: &dyn EntityBase,
|
dyn_self: &dyn EntityBase,
|
||||||
damage_type: DamageType,
|
damage_type: DamageType,
|
||||||
source: Option<&dyn EntityBase>,
|
source: Option<&dyn EntityBase>,
|
||||||
cause: Option<&dyn EntityBase>,
|
cause: Option<&dyn EntityBase>,
|
||||||
) -> TextComponent {
|
) -> TextComponent {
|
||||||
|
let kill_credit = dyn_self
|
||||||
|
.get_living_entity()
|
||||||
|
.and_then(Self::get_kill_credit);
|
||||||
|
let kill_credit_name = kill_credit.as_ref().map(|c| c.get_display_name());
|
||||||
|
|
||||||
if let Some(living) = dyn_self.get_living_entity() {
|
if let Some(living) = dyn_self.get_living_entity() {
|
||||||
let tracker = living
|
let tracker = living
|
||||||
.combat_tracker
|
.combat_tracker
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
return tracker.get_death_message(dyn_self.get_display_name());
|
return tracker.get_death_message(dyn_self.get_display_name(), kill_credit_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(cause) = cause
|
if let Some(cause) = cause
|
||||||
&& source.is_some()
|
&& source.is_some()
|
||||||
{
|
{
|
||||||
TextComponent::translate_cross(
|
TextComponent::translate_cross(
|
||||||
format!("death.attack.{}.player", damage_type.message_id),
|
format!("death.attack.{}", damage_type.message_id),
|
||||||
format!("death.attack.{}.player", damage_type.message_id),
|
format!("death.attack.{}", damage_type.message_id),
|
||||||
[dyn_self.get_display_name(), cause.get_display_name()],
|
[dyn_self.get_display_name(), cause.get_display_name()],
|
||||||
)
|
)
|
||||||
|
} else if let Some(killer) = cause.or(source).map(|c| c.get_display_name()).or(kill_credit_name) {
|
||||||
|
TextComponent::translate_cross(
|
||||||
|
format!("death.attack.{}.player", damage_type.message_id),
|
||||||
|
format!("death.attack.{}.player", damage_type.message_id),
|
||||||
|
[dyn_self.get_display_name(), killer],
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
TextComponent::translate_cross(
|
TextComponent::translate_cross(
|
||||||
format!("death.attack.{}", damage_type.message_id),
|
format!("death.attack.{}", damage_type.message_id),
|
||||||
@ -1656,7 +1720,10 @@ impl LivingEntity {
|
|||||||
self.movement_input.store(Vector3::default());
|
self.movement_input.store(Vector3::default());
|
||||||
self.jumping.store(false, Relaxed);
|
self.jumping.store(false, Relaxed);
|
||||||
|
|
||||||
self.update_death_stats(&*dyn_self, cause.or(source));
|
let kill_credit = self.get_kill_credit();
|
||||||
|
let killer = cause.or(source).or(kill_credit.as_deref());
|
||||||
|
|
||||||
|
self.update_death_stats(&*dyn_self, killer);
|
||||||
|
|
||||||
// Plays the death sound
|
// Plays the death sound
|
||||||
world.send_entity_status(&self.entity, EntityStatus::Death, Some(ActorEventID::Death));
|
world.send_entity_status(&self.entity, EntityStatus::Death, Some(ActorEventID::Death));
|
||||||
@ -1686,7 +1753,7 @@ impl LivingEntity {
|
|||||||
let is_thundering = world.is_thundering();
|
let is_thundering = world.is_thundering();
|
||||||
|
|
||||||
let has_player_kill =
|
let has_player_kill =
|
||||||
cause.is_some_and(|c| c.get_entity().entity_type == &EntityType::PLAYER) || {
|
killer.is_some_and(|c| c.get_entity().entity_type == &EntityType::PLAYER) || {
|
||||||
let tracker = self
|
let tracker = self
|
||||||
.combat_tracker
|
.combat_tracker
|
||||||
.lock()
|
.lock()
|
||||||
@ -1697,7 +1764,7 @@ impl LivingEntity {
|
|||||||
let params = LootContextParameters {
|
let params = LootContextParameters {
|
||||||
killed_by_player: Some(has_player_kill),
|
killed_by_player: Some(has_player_kill),
|
||||||
this_entity: Some(self.entity.entity_type),
|
this_entity: Some(self.entity.entity_type),
|
||||||
killer_entity: cause.map(|c| c.get_entity().entity_type),
|
killer_entity: killer.map(|c| c.get_entity().entity_type),
|
||||||
direct_killer_entity: source.map(|s| s.get_entity().entity_type),
|
direct_killer_entity: source.map(|s| s.get_entity().entity_type),
|
||||||
position: Some(self.entity.pos.load()),
|
position: Some(self.entity.pos.load()),
|
||||||
world_time: world.level_info.load().day_time as u64,
|
world_time: world.level_info.load().day_time as u64,
|
||||||
@ -1721,7 +1788,7 @@ impl LivingEntity {
|
|||||||
if params.killed_by_player.unwrap_or(false)
|
if params.killed_by_player.unwrap_or(false)
|
||||||
&& world.level_info.load().game_rules.mob_drops
|
&& world.level_info.load().game_rules.mob_drops
|
||||||
{
|
{
|
||||||
let amount = dyn_self.get_experience_reward(cause);
|
let amount = dyn_self.get_experience_reward(killer);
|
||||||
if amount > 0 {
|
if amount > 0 {
|
||||||
ExperienceOrbEntity::spawn(&world, self.entity.pos.load(), amount);
|
ExperienceOrbEntity::spawn(&world, self.entity.pos.load(), amount);
|
||||||
}
|
}
|
||||||
@ -1813,14 +1880,42 @@ impl LivingEntity {
|
|||||||
) {
|
) {
|
||||||
let world = self.entity.world.load();
|
let world = self.entity.world.load();
|
||||||
let show_death_messages = { world.level_info.load().game_rules.show_death_messages };
|
let show_death_messages = { world.level_info.load().game_rules.show_death_messages };
|
||||||
if self.entity.entity_type == &EntityType::PLAYER && show_death_messages {
|
if self.entity.entity_type == &EntityType::PLAYER {
|
||||||
//TODO: KillCredit
|
|
||||||
let death_message = Self::get_death_message(dyn_self, damage_type, source, cause);
|
let death_message = Self::get_death_message(dyn_self, damage_type, source, cause);
|
||||||
if let Some(server) = world.server.upgrade() {
|
let mut final_death_message = death_message;
|
||||||
|
if let Some(player) = dyn_self.get_player() {
|
||||||
|
if let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id)
|
||||||
|
&& let Some(server) = world.server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::entity::entity_death::PlayerDeathEvent::new(
|
||||||
|
player_arc,
|
||||||
|
final_death_message.clone(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final_death_message = event.death_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.handle_killed(&final_death_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if show_death_messages
|
||||||
|
&& let Some(server) = world.server.upgrade()
|
||||||
|
{
|
||||||
for player in server.get_all_players() {
|
for player in server.get_all_players() {
|
||||||
player.send_system_message(&death_message);
|
player.send_system_message(&final_death_message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if self.entity.custom_name.load().is_some() {
|
||||||
|
let death_message = Self::get_death_message(dyn_self, damage_type, source, cause);
|
||||||
|
tracing::info!(
|
||||||
|
"Named entity {} died: {}",
|
||||||
|
dyn_self.get_display_name().to_pretty_console(),
|
||||||
|
death_message.to_pretty_console()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1894,11 +1989,6 @@ impl LivingEntity {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
killer_player.increment_stat(
|
|
||||||
StatisticCategory::Killed,
|
|
||||||
self.entity.entity_type.id as i32,
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2521,6 +2611,54 @@ impl LivingEntity {
|
|||||||
}
|
}
|
||||||
amount = damage_event.damage;
|
amount = damage_event.damage;
|
||||||
|
|
||||||
|
if let Some(damager) = source.or(cause) {
|
||||||
|
let mut by_entity_event =
|
||||||
|
crate::plugin::api::events::entity::entity_damage_by_entity::EntityDamageByEntityEvent {
|
||||||
|
entity_id: self.entity.entity_id,
|
||||||
|
damager_id: damager.get_entity().entity_id,
|
||||||
|
damage: amount,
|
||||||
|
cause: format!("{damage_type:?}"),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
if let Some(server) = self.entity.world.load().server.upgrade() {
|
||||||
|
server
|
||||||
|
.plugin_manager
|
||||||
|
.fire_blocking(&server, &mut by_entity_event);
|
||||||
|
}
|
||||||
|
if by_entity_event.cancelled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
amount = by_entity_event.damage;
|
||||||
|
} else if position.is_some()
|
||||||
|
|| matches!(
|
||||||
|
damage_type,
|
||||||
|
DamageType::CACTUS
|
||||||
|
| DamageType::SWEET_BERRY_BUSH
|
||||||
|
| DamageType::CAMPFIRE
|
||||||
|
| DamageType::HOT_FLOOR
|
||||||
|
| DamageType::STALAGMITE
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let damager_pos = position.map(|p| BlockPos(Vector3::new(p.x.floor() as i32, p.y.floor() as i32, p.z.floor() as i32)));
|
||||||
|
let mut by_block_event =
|
||||||
|
crate::plugin::api::events::entity::entity_damage_by_block::EntityDamageByBlockEvent {
|
||||||
|
entity_id: self.entity.entity_id,
|
||||||
|
damager_pos,
|
||||||
|
damage: amount,
|
||||||
|
cause: format!("{damage_type:?}"),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
if let Some(server) = self.entity.world.load().server.upgrade() {
|
||||||
|
server
|
||||||
|
.plugin_manager
|
||||||
|
.fire_blocking(&server, &mut by_block_event);
|
||||||
|
}
|
||||||
|
if by_block_event.cancelled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
amount = by_block_event.damage;
|
||||||
|
}
|
||||||
|
|
||||||
let world = self.entity.world.load();
|
let world = self.entity.world.load();
|
||||||
let is_fire_damage = damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_FIRE);
|
let is_fire_damage = damage_type.has_tag(&tag::DamageType::MINECRAFT_IS_FIRE);
|
||||||
|
|
||||||
@ -2758,10 +2896,20 @@ impl LivingEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(attacker) = cause.or(source) {
|
if let Some(attacker) = cause.or(source) {
|
||||||
|
let attacker_id = attacker.get_entity().entity_id;
|
||||||
self.last_attacker_id
|
self.last_attacker_id
|
||||||
.store(attacker.get_entity().entity_id, Relaxed);
|
.store(attacker_id, Relaxed);
|
||||||
self.last_attacked_time
|
self.last_attacked_time
|
||||||
.store(self.entity.age.load(Relaxed), Relaxed);
|
.store(self.entity.age.load(Relaxed), Relaxed);
|
||||||
|
|
||||||
|
let current_tick = world.level_info.load().day_time;
|
||||||
|
if attacker.get_player().is_some() {
|
||||||
|
self.last_hurt_by_player_id.store(attacker_id, Relaxed);
|
||||||
|
self.last_hurt_by_player_time.store(current_tick, Relaxed);
|
||||||
|
} else if attacker.get_living_entity().is_some() {
|
||||||
|
self.last_hurt_by_mob_id.store(attacker_id, Relaxed);
|
||||||
|
self.last_hurt_by_mob_time.store(current_tick, Relaxed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -305,6 +305,14 @@ pub trait EntityBase: Send + Sync + std::any::Any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn java_spawn_metadata(&self, version: JavaMinecraftVersion) -> Option<Box<[u8]>> {
|
fn java_spawn_metadata(&self, version: JavaMinecraftVersion) -> Option<Box<[u8]>> {
|
||||||
|
if version < JavaMinecraftVersion::V_1_9 {
|
||||||
|
let entity = self.get_entity();
|
||||||
|
let shared_flags = entity.flags.load(Ordering::Relaxed);
|
||||||
|
return (shared_flags != 0).then(|| {
|
||||||
|
// (0 << 5) | 0 = 0 (type: byte, index: 0 flags), value, 127 (terminator)
|
||||||
|
Box::<[u8]>::from([0x00u8, shared_flags as u8, 127u8])
|
||||||
|
});
|
||||||
|
}
|
||||||
self.get_mob().map_or_else(
|
self.get_mob().map_or_else(
|
||||||
|| {
|
|| {
|
||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
@ -382,7 +390,9 @@ pub trait EntityBase: Send + Sync + std::any::Any {
|
|||||||
if let Ok(data) = client.serialize_packet(&spawn_packet) {
|
if let Ok(data) = client.serialize_packet(&spawn_packet) {
|
||||||
client.try_enqueue_packet(data);
|
client.try_enqueue_packet(data);
|
||||||
}
|
}
|
||||||
if let Some(meta) = metadata {
|
if let Some(meta) = metadata
|
||||||
|
&& (version >= JavaMinecraftVersion::V_1_9 || meta.last().copied() == Some(127))
|
||||||
|
{
|
||||||
let meta_packet = CSetEntityMetadata::new(entity.entity_id.into(), meta);
|
let meta_packet = CSetEntityMetadata::new(entity.entity_id.into(), meta);
|
||||||
if let Ok(meta_data) = client.serialize_packet(&meta_packet) {
|
if let Ok(meta_data) = client.serialize_packet(&meta_packet) {
|
||||||
client.try_enqueue_packet(meta_data);
|
client.try_enqueue_packet(meta_data);
|
||||||
@ -3356,6 +3366,23 @@ impl Entity {
|
|||||||
|
|
||||||
pub fn leash_to(&self, holder: Arc<dyn EntityBase>) {
|
pub fn leash_to(&self, holder: Arc<dyn EntityBase>) {
|
||||||
let holder_entity_id = holder.get_entity().entity_id;
|
let holder_entity_id = holder.get_entity().entity_id;
|
||||||
|
let world = self.world.load();
|
||||||
|
if let Some(server) = world.server.upgrade()
|
||||||
|
&& let Some(player) = holder.get_player()
|
||||||
|
&& let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id)
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_leash_entity::PlayerLeashEntityEvent {
|
||||||
|
player: player_arc,
|
||||||
|
entity_id: self.entity_id,
|
||||||
|
holder_id: holder_entity_id,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*self
|
*self
|
||||||
.leashed_to
|
.leashed_to
|
||||||
.lock()
|
.lock()
|
||||||
@ -3387,6 +3414,18 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn unleash(&self) {
|
pub fn unleash(&self) {
|
||||||
|
let world = self.world.load();
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::entity::entity_unleash::EntityUnleashEvent::new(
|
||||||
|
self.entity_id,
|
||||||
|
"unleashed".to_string(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let old_holder = self
|
let old_holder = self
|
||||||
.leashed_to
|
.leashed_to
|
||||||
.lock()
|
.lock()
|
||||||
@ -3396,6 +3435,19 @@ impl Entity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(holder) = &old_holder
|
||||||
|
&& let Some(server) = world.server.upgrade()
|
||||||
|
&& let Some(player) = holder.get_player()
|
||||||
|
&& let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id)
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_unleash_entity::PlayerUnleashEntityEvent {
|
||||||
|
player: player_arc,
|
||||||
|
entity_id: self.entity_id,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
}
|
||||||
|
|
||||||
let je_packet =
|
let je_packet =
|
||||||
pumpkin_protocol::java::client::play::CSetEntityLink::new(self.entity_id, -1, true);
|
pumpkin_protocol::java::client::play::CSetEntityLink::new(self.entity_id, -1, true);
|
||||||
let be_packet = pumpkin_protocol::bedrock::client::CSetActorLink {
|
let be_packet = pumpkin_protocol::bedrock::client::CSetActorLink {
|
||||||
|
|||||||
@ -296,9 +296,21 @@ impl Mob for AxolotlEntity {
|
|||||||
let item = item_stack.get_item();
|
let item = item_stack.get_item();
|
||||||
|
|
||||||
if item == &Item::WATER_BUCKET {
|
if item == &Item::WATER_BUCKET {
|
||||||
item_stack.decrement_unless_creative(player.gamemode.load(), 1);
|
|
||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
let world = entity.world.load();
|
let world = entity.world.load();
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::player::player_bucket_entity::PlayerBucketEntityEvent {
|
||||||
|
player: player.clone(),
|
||||||
|
entity_id: entity.entity_id,
|
||||||
|
bucket_item: "axolotl_bucket".to_string(),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item_stack.decrement_unless_creative(player.gamemode.load(), 1);
|
||||||
let pos = entity.pos.load();
|
let pos = entity.pos.load();
|
||||||
world.play_sound(Sound::ItemBucketFillAxolotl, SoundCategory::Neutral, &pos);
|
world.play_sound(Sound::ItemBucketFillAxolotl, SoundCategory::Neutral, &pos);
|
||||||
entity.remove();
|
entity.remove();
|
||||||
|
|||||||
@ -127,9 +127,21 @@ impl Mob for TadpoleEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if item_stack.get_item() == &Item::WATER_BUCKET {
|
if item_stack.get_item() == &Item::WATER_BUCKET {
|
||||||
item_stack.decrement_unless_creative(player.gamemode.load(), 1);
|
|
||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
let world = entity.world.load();
|
let world = entity.world.load();
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::player::player_bucket_entity::PlayerBucketEntityEvent {
|
||||||
|
player: player.clone(),
|
||||||
|
entity_id: entity.entity_id,
|
||||||
|
bucket_item: "tadpole_bucket".to_string(),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item_stack.decrement_unless_creative(player.gamemode.load(), 1);
|
||||||
let pos = entity.pos.load();
|
let pos = entity.pos.load();
|
||||||
world.play_sound(Sound::ItemBucketFillTadpole, SoundCategory::Neutral, &pos);
|
world.play_sound(Sound::ItemBucketFillTadpole, SoundCategory::Neutral, &pos);
|
||||||
entity.remove();
|
entity.remove();
|
||||||
|
|||||||
@ -2141,6 +2141,9 @@ impl Mob for VillagerEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mob_java_spawn_metadata(&self, version: JavaMinecraftVersion) -> Option<Box<[u8]>> {
|
fn mob_java_spawn_metadata(&self, version: JavaMinecraftVersion) -> Option<Box<[u8]>> {
|
||||||
|
if version < JavaMinecraftVersion::V_1_9 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let mut metadata = Vec::new();
|
let mut metadata = Vec::new();
|
||||||
Metadata::new(
|
Metadata::new(
|
||||||
tracked_data::villager::VILLAGER_DATA,
|
tracked_data::villager::VILLAGER_DATA,
|
||||||
|
|||||||
@ -1603,19 +1603,38 @@ impl Player {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut final_block_pos = block_pos;
|
||||||
|
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||||
|
&& let Some(server) = self.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_spawn_change::PlayerSpawnChangeEvent {
|
||||||
|
player: player_arc,
|
||||||
|
new_spawn: Some(block_pos),
|
||||||
|
forced,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Some(pos) = event.new_spawn {
|
||||||
|
final_block_pos = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let bedrock_dimension = match dimension.minecraft_name {
|
let bedrock_dimension = match dimension.minecraft_name {
|
||||||
"minecraft:the_nether" => 1,
|
"minecraft:the_nether" => 1,
|
||||||
"minecraft:the_end" => 2,
|
"minecraft:the_end" => 2,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
self.client.try_enqueue_packet_editioned(
|
self.client.try_enqueue_packet_editioned(
|
||||||
&CPlayerSpawnPosition::new(block_pos, yaw, pitch, dimension.minecraft_name.to_owned()),
|
&CPlayerSpawnPosition::new(final_block_pos, yaw, pitch, dimension.minecraft_name.to_owned()),
|
||||||
&pumpkin_protocol::bedrock::client::CSetSpawnPosition {
|
&pumpkin_protocol::bedrock::client::CSetSpawnPosition {
|
||||||
spawn_position_type:
|
spawn_position_type:
|
||||||
pumpkin_protocol::bedrock::client::SpawnPositionType::PlayerRespawn,
|
pumpkin_protocol::bedrock::client::SpawnPositionType::PlayerRespawn,
|
||||||
block_position: block_pos,
|
block_position: final_block_pos,
|
||||||
dimension_type: bedrock_dimension.into(),
|
dimension_type: bedrock_dimension.into(),
|
||||||
spawn_block_pos: block_pos,
|
spawn_block_pos: final_block_pos,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1624,7 +1643,7 @@ impl Player {
|
|||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(RespawnPoint {
|
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(RespawnPoint {
|
||||||
dimension,
|
dimension,
|
||||||
position: block_pos,
|
position: final_block_pos,
|
||||||
yaw,
|
yaw,
|
||||||
force: forced,
|
force: forced,
|
||||||
});
|
});
|
||||||
@ -2293,11 +2312,39 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_sign_editor(&self, location: BlockPos, is_front_text: bool) {
|
pub fn open_sign_editor(&self, location: BlockPos, is_front_text: bool) {
|
||||||
|
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||||
|
&& let Some(server) = self.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_open_sign::PlayerOpenSignEvent {
|
||||||
|
player: player_arc,
|
||||||
|
block_pos: location,
|
||||||
|
is_front: is_front_text,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let packet = COpenSignEditor::new(location, is_front_text);
|
let packet = COpenSignEditor::new(location, is_front_text);
|
||||||
self.try_send_client_packet(&packet);
|
self.try_send_client_packet(&packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_velocity(&self, velocity: Vector3<f64>) {
|
pub fn set_velocity(&self, mut velocity: Vector3<f64>) {
|
||||||
|
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||||
|
&& let Some(server) = self.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_velocity::PlayerVelocityEvent {
|
||||||
|
player: player_arc,
|
||||||
|
velocity,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
velocity = event.velocity;
|
||||||
|
}
|
||||||
self.living_entity.entity.set_velocity(velocity);
|
self.living_entity.entity.set_velocity(velocity);
|
||||||
self.try_send_client_packet(&CEntityVelocity::new(self.entity_id().into(), velocity));
|
self.try_send_client_packet(&CEntityVelocity::new(self.entity_id().into(), velocity));
|
||||||
}
|
}
|
||||||
@ -2340,6 +2387,19 @@ impl Player {
|
|||||||
|
|
||||||
/// Sends custom server links to the player (displayed in the client Esc pause menu).
|
/// Sends custom server links to the player (displayed in the client Esc pause menu).
|
||||||
pub fn set_server_links(&self, links: &[pumpkin_protocol::Link<'_>]) {
|
pub fn set_server_links(&self, links: &[pumpkin_protocol::Link<'_>]) {
|
||||||
|
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||||
|
&& let Some(server) = self.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let link_strings = links.iter().map(|l| l.url.clone()).collect();
|
||||||
|
let mut event = crate::plugin::api::events::player::player_links_send::PlayerLinksSendEvent::new(
|
||||||
|
player_arc,
|
||||||
|
link_strings,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let packet = CPlayServerLinks::new(links);
|
let packet = CPlayServerLinks::new(links);
|
||||||
self.try_send_client_packet(&packet);
|
self.try_send_client_packet(&packet);
|
||||||
}
|
}
|
||||||
@ -3029,8 +3089,25 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment_stat(&self, category: statistics::StatisticCategory, stat: i32, amount: i32) {
|
pub fn increment_stat(&self, category: statistics::StatisticCategory, stat: i32, amount: i32) {
|
||||||
|
let final_amount = if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||||
|
&& let Some(server) = self.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_statistic_increment::PlayerStatisticIncrementEvent {
|
||||||
|
player: player_arc,
|
||||||
|
statistic_id: format!("{category:?}:{stat}"),
|
||||||
|
amount,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.amount
|
||||||
|
} else {
|
||||||
|
amount
|
||||||
|
};
|
||||||
if let Ok(mut stats) = self.stats.try_lock() {
|
if let Ok(mut stats) = self.stats.try_lock() {
|
||||||
stats.increment(category, stat, amount);
|
stats.increment(category, stat, final_amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3729,6 +3806,14 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
player.request_teleport(position, yaw, pitch);
|
player.request_teleport(position, yaw, pitch);
|
||||||
|
|
||||||
|
let mut changed_world_event = crate::plugin::api::events::player::player_changed_world::PlayerChangedWorldEvent {
|
||||||
|
player: player.clone(),
|
||||||
|
from_world: current_world,
|
||||||
|
to_world: new_world,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire(&server, &mut changed_world_event).await;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -4364,8 +4449,19 @@ impl Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn send_combat_death(&self, death_msg: &TextComponent) {
|
||||||
async fn handle_killed(&self, death_msg: TextComponent) {
|
self.try_enqueue_packet_editioned(
|
||||||
|
&CCombatDeath::new(self.entity_id().into(), death_msg),
|
||||||
|
&SActorEvent {
|
||||||
|
target_runtime_id: VarULong(self.entity_id() as u64),
|
||||||
|
event_id: ActorEventID::Death,
|
||||||
|
data: VarInt(0),
|
||||||
|
fire_at_position: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_killed(&self, death_msg: &TextComponent) {
|
||||||
self.trigger_advancement(
|
self.trigger_advancement(
|
||||||
crate::entity::player::advancement::trigger::AdvancementTrigger::PlayerKilled,
|
crate::entity::player::advancement::trigger::AdvancementTrigger::PlayerKilled,
|
||||||
);
|
);
|
||||||
@ -4402,17 +4498,7 @@ impl Player {
|
|||||||
if matches!(self.client.as_ref(), ClientPlatform::Java(_)) {
|
if matches!(self.client.as_ref(), ClientPlatform::Java(_)) {
|
||||||
self.set_client_loaded(false);
|
self.set_client_loaded(false);
|
||||||
}
|
}
|
||||||
self.client
|
self.send_combat_death(death_msg);
|
||||||
.send_packet_now_editioned(
|
|
||||||
&CCombatDeath::new(self.entity_id().into(), &death_msg),
|
|
||||||
&SActorEvent {
|
|
||||||
target_runtime_id: VarULong(self.entity_id() as u64),
|
|
||||||
event_id: ActorEventID::Death,
|
|
||||||
data: VarInt(0),
|
|
||||||
fire_at_position: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.send_health();
|
self.send_health();
|
||||||
self.send_bedrock_respawn_state(RespawnState::SearchingForSpawn);
|
self.send_bedrock_respawn_state(RespawnState::SearchingForSpawn);
|
||||||
}
|
}
|
||||||
@ -5137,10 +5223,27 @@ impl Player {
|
|||||||
return xp;
|
return xp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let xp_used = (repaired + 1) / 2;
|
||||||
|
|
||||||
|
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||||
|
&& let Some(server) = self.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_item_mend::PlayerItemMendEvent {
|
||||||
|
player: player_arc,
|
||||||
|
item_name: stack.item.registry_key.to_string(),
|
||||||
|
repair_amount: repaired,
|
||||||
|
exp_consumed: xp_used,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return xp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let updated_stack = stack.clone();
|
let updated_stack = stack.clone();
|
||||||
self.inventory.set_slot(slot_index, updated_stack.clone());
|
self.inventory.set_slot(slot_index, updated_stack.clone());
|
||||||
|
|
||||||
let xp_used = (repaired + 1) / 2;
|
|
||||||
xp = xp.saturating_sub(xp_used);
|
xp = xp.saturating_sub(xp_used);
|
||||||
|
|
||||||
self.try_send_slot_set_packet(&CSetPlayerInventory::new(
|
self.try_send_slot_set_packet(&CSetPlayerInventory::new(
|
||||||
|
|||||||
@ -1013,6 +1013,19 @@ impl EntityBase for ArrowEntity {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
|
||||||
|
&& let Some(server) = player.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_pickup_arrow::PlayerPickupArrowEvent::new(
|
||||||
|
player_arc,
|
||||||
|
self.entity.entity_id,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to insert an arrow into the player's inventory
|
// Try to insert an arrow into the player's inventory
|
||||||
let item_stack = self
|
let item_stack = self
|
||||||
.item_stack
|
.item_stack
|
||||||
|
|||||||
@ -70,6 +70,16 @@ impl FireworkRocketEntity {
|
|||||||
|
|
||||||
pub fn explode_and_remove(&self, world: &World) {
|
pub fn explode_and_remove(&self, world: &World) {
|
||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::entity::firework_explode::FireworkExplodeEvent {
|
||||||
|
entity_id: entity.entity_id,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
world.send_entity_status(
|
world.send_entity_status(
|
||||||
entity,
|
entity,
|
||||||
EntityStatus::FireworksExplode,
|
EntityStatus::FireworksExplode,
|
||||||
@ -97,6 +107,21 @@ impl EntityBase for FireworkRocketEntity {
|
|||||||
let shooter = shooter.get_entity();
|
let shooter = shooter.get_entity();
|
||||||
|
|
||||||
if shooter.is_fall_flying() {
|
if shooter.is_fall_flying() {
|
||||||
|
let mut boost_cancelled = false;
|
||||||
|
if let Some(player) = world.get_player_by_id(shooter_id)
|
||||||
|
&& let Some(server) = world.server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_elytra_boost::PlayerElytraBoostEvent {
|
||||||
|
player,
|
||||||
|
firework_id: entity.entity_id,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
boost_cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !boost_cancelled {
|
||||||
let rotation = shooter.rotation().to_f64();
|
let rotation = shooter.rotation().to_f64();
|
||||||
let shooter_vel = shooter.velocity.load();
|
let shooter_vel = shooter.velocity.load();
|
||||||
|
|
||||||
@ -109,6 +134,7 @@ impl EntityBase for FireworkRocketEntity {
|
|||||||
entity.set_velocity(new_shooter_vel);
|
entity.set_velocity(new_shooter_vel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
velocity.x *= 1.15;
|
velocity.x *= 1.15;
|
||||||
velocity.z *= 1.15;
|
velocity.z *= 1.15;
|
||||||
|
|||||||
@ -182,6 +182,18 @@ impl EntityBase for LingeringPotionEntity {
|
|||||||
// Spawn and configure an `AreaEffectCloud` entity
|
// Spawn and configure an `AreaEffectCloud` entity
|
||||||
extinguish_fire_if_water_potion(&world, hit_pos, &stack);
|
extinguish_fire_if_water_potion(&world, hit_pos, &stack);
|
||||||
|
|
||||||
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::entity::lingering_potion_splash::LingeringPotionSplashEvent::new(
|
||||||
|
self.get_entity().entity_id,
|
||||||
|
block_pos,
|
||||||
|
stack.item.registry_key.to_string(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let cloud_entity = crate::entity::Entity::from_uuid(
|
let cloud_entity = crate::entity::Entity::from_uuid(
|
||||||
Uuid::new_v4(),
|
Uuid::new_v4(),
|
||||||
world.clone(),
|
world.clone(),
|
||||||
|
|||||||
@ -132,6 +132,7 @@ impl EntityBase for SplashPotionEntity {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) {
|
fn on_hit(&self, hit: crate::entity::projectile::ProjectileHit) {
|
||||||
let world = self.get_entity().world.load();
|
let world = self.get_entity().world.load();
|
||||||
let hit_pos = hit.hit_pos();
|
let hit_pos = hit.hit_pos();
|
||||||
@ -228,20 +229,38 @@ impl EntityBase for SplashPotionEntity {
|
|||||||
candidates.push(p.clone() as Arc<dyn EntityBase>);
|
candidates.push(p.clone() as Arc<dyn EntityBase>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut affected: Vec<(Arc<dyn EntityBase>, f32)> = Vec::new();
|
||||||
for cand in candidates {
|
for cand in candidates {
|
||||||
if let Some(living) = cand.get_living_entity() {
|
if cand.get_living_entity().is_some() {
|
||||||
let pos = cand.get_entity().pos.load();
|
let pos = cand.get_entity().pos.load();
|
||||||
let dx = pos.x - hit_pos.x;
|
let dx = pos.x - hit_pos.x;
|
||||||
let dy = pos.y - hit_pos.y;
|
let dy = pos.y - hit_pos.y;
|
||||||
let dz = pos.z - hit_pos.z;
|
let dz = pos.z - hit_pos.z;
|
||||||
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
|
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
|
||||||
if dist > radius {
|
if dist <= radius {
|
||||||
continue;
|
let scale = (1.0f32 - (dist as f32 / radius as f32)).max(0.0);
|
||||||
|
affected.push((cand, scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Distance scaling
|
let affected_ids: Vec<i32> = affected.iter().map(|(c, _)| c.get_entity().entity_id).collect();
|
||||||
let scale = (1.0f32 - (dist as f32 / radius as f32)).max(0.0);
|
if let Some(server) = world.server.upgrade() {
|
||||||
|
let mut event = crate::plugin::api::events::entity::potion_splash::PotionSplashEvent::new(
|
||||||
|
self.get_entity().entity_id,
|
||||||
|
block_pos,
|
||||||
|
stack.item.registry_key.to_string(),
|
||||||
|
affected_ids,
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
affected.retain(|(c, _)| event.affected_entities.contains(&c.get_entity().entity_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cand, scale) in affected {
|
||||||
|
if let Some(living) = cand.get_living_entity() {
|
||||||
crate::item::potion::PotionContents::apply_effects_to(
|
crate::item::potion::PotionContents::apply_effects_to(
|
||||||
living,
|
living,
|
||||||
effects.clone(),
|
effects.clone(),
|
||||||
|
|||||||
@ -117,6 +117,21 @@ impl ItemBehaviour for BrushItem {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if is_sand || is_gravel {
|
if is_sand || is_gravel {
|
||||||
|
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
|
||||||
|
&& let Some(server) = player.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::block::block_brush::BlockBrushEvent::new(
|
||||||
|
location,
|
||||||
|
world.clone(),
|
||||||
|
player_arc,
|
||||||
|
player.inventory().held_item(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let current_state_id = world.get_block_state_id(&location);
|
let current_state_id = world.get_block_state_id(&location);
|
||||||
let current_stage = get_dusted_stage(block, current_state_id);
|
let current_stage = get_dusted_stage(block, current_state_id);
|
||||||
|
|
||||||
|
|||||||
@ -76,6 +76,20 @@ impl ItemBehaviour for ShearsItem {
|
|||||||
.downcast_ref::<crate::entity::passive::sheep::SheepEntity>()
|
.downcast_ref::<crate::entity::passive::sheep::SheepEntity>()
|
||||||
&& !sheep.is_sheared()
|
&& !sheep.is_sheared()
|
||||||
{
|
{
|
||||||
|
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
|
||||||
|
&& let Some(server) = player.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_shear_entity::PlayerShearEntityEvent {
|
||||||
|
player: player_arc,
|
||||||
|
entity_id: sheep.mob_entity.living_entity.entity.entity_id,
|
||||||
|
hand: 0,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
sheep.set_sheared(true);
|
sheep.set_sheared(true);
|
||||||
let world = player.world();
|
let world = player.world();
|
||||||
let pos = sheep.mob_entity.living_entity.entity.pos.load();
|
let pos = sheep.mob_entity.living_entity.entity.pos.load();
|
||||||
@ -176,6 +190,23 @@ fn handle_beehive(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(new_state_id) = action {
|
if let Some(new_state_id) = action {
|
||||||
|
let mut drops = vec![ItemStack::new(3, &Item::HONEYCOMB)];
|
||||||
|
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
|
||||||
|
&& let Some(server) = player.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_harvest_block::PlayerHarvestBlockEvent {
|
||||||
|
player: player_arc,
|
||||||
|
block_pos: *location,
|
||||||
|
harvested_items: drops.clone(),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
drops = event.harvested_items;
|
||||||
|
}
|
||||||
|
|
||||||
world.set_block_state(location, new_state_id, BlockFlags::NOTIFY_ALL);
|
world.set_block_state(location, new_state_id, BlockFlags::NOTIFY_ALL);
|
||||||
world.play_sound(
|
world.play_sound(
|
||||||
Sound::BlockBeehiveShear,
|
Sound::BlockBeehiveShear,
|
||||||
@ -188,11 +219,13 @@ fn handle_beehive(
|
|||||||
f64::from(location.0.y) + 0.5,
|
f64::from(location.0.y) + 0.5,
|
||||||
f64::from(location.0.z) + 0.5,
|
f64::from(location.0.z) + 0.5,
|
||||||
);
|
);
|
||||||
|
for item in drops {
|
||||||
let item_entity = Arc::new(ItemEntity::new(
|
let item_entity = Arc::new(ItemEntity::new(
|
||||||
Entity::new(world.clone(), drop_pos, &EntityType::ITEM),
|
Entity::new(world.clone(), drop_pos, &EntityType::ITEM),
|
||||||
ItemStack::new(3, &Item::HONEYCOMB),
|
item,
|
||||||
));
|
));
|
||||||
world.spawn_entity(item_entity);
|
world.spawn_entity(item_entity);
|
||||||
|
}
|
||||||
player.damage_held_item(1);
|
player.damage_held_item(1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,21 @@ impl ItemBehaviour for TridentItem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id)
|
||||||
|
&& let Some(server) = world.server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_riptide::PlayerRiptideEvent {
|
||||||
|
player: player_arc,
|
||||||
|
item_name: "trident".to_string(),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
player.living_entity.clear_active_hand();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let f = f64::from(riptide_level);
|
let f = f64::from(riptide_level);
|
||||||
let (yaw, pitch) = player.rotation();
|
let (yaw, pitch) = player.rotation();
|
||||||
let f_yaw = f32::to_radians(yaw);
|
let f_yaw = f32::to_radians(yaw);
|
||||||
|
|||||||
@ -160,7 +160,9 @@ impl ChunkSender {
|
|||||||
epoch: u32,
|
epoch: u32,
|
||||||
version: JavaMinecraftVersion,
|
version: JavaMinecraftVersion,
|
||||||
) -> Option<PreparedBatch> {
|
) -> Option<PreparedBatch> {
|
||||||
if self.in_flight_batches >= self.max_in_flight {
|
if version >= JavaMinecraftVersion::V_1_20_2
|
||||||
|
&& self.in_flight_batches >= self.max_in_flight
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,9 +296,9 @@ impl ChunkSender {
|
|||||||
&& let ClientPlatform::Java(java_client) = client
|
&& let ClientPlatform::Java(java_client) = client
|
||||||
{
|
{
|
||||||
java_client.try_send_packet(&CChunkBatchEnd::new(sent_count as u16));
|
java_client.try_send_packet(&CChunkBatchEnd::new(sent_count as u16));
|
||||||
|
self.in_flight_batches = self.in_flight_batches.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.in_flight_batches = self.in_flight_batches.saturating_add(1);
|
|
||||||
self.send_quota -= sent_count as f32;
|
self.send_quota -= sent_count as f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ impl PendingConnection {
|
|||||||
|
|
||||||
pub async fn handle_encryption_response(
|
pub async fn handle_encryption_response(
|
||||||
&mut self,
|
&mut self,
|
||||||
server: &Server,
|
server: &Arc<Server>,
|
||||||
encryption_response: SEncryptionResponse,
|
encryption_response: SEncryptionResponse,
|
||||||
) -> Option<PacketHandlerResult> {
|
) -> Option<PacketHandlerResult> {
|
||||||
debug!("Handling encryption");
|
debug!("Handling encryption");
|
||||||
@ -146,9 +146,22 @@ impl PendingConnection {
|
|||||||
|
|
||||||
pub(super) async fn finish_login(
|
pub(super) async fn finish_login(
|
||||||
&mut self,
|
&mut self,
|
||||||
server: &Server,
|
server: &Arc<Server>,
|
||||||
profile: &GameProfile,
|
profile: &GameProfile,
|
||||||
) -> Option<PacketHandlerResult> {
|
) -> Option<PacketHandlerResult> {
|
||||||
|
let mut pre_login_event = crate::plugin::api::events::player::async_player_pre_login::AsyncPlayerPreLoginEvent {
|
||||||
|
player_name: profile.name.clone(),
|
||||||
|
player_uuid: profile.id,
|
||||||
|
ip_address: self.address,
|
||||||
|
kick_message: TextComponent::text("Disconnected"),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire(server, &mut pre_login_event).await;
|
||||||
|
if pre_login_event.cancelled {
|
||||||
|
self.kick(pre_login_event.kick_message).await;
|
||||||
|
return Some(PacketHandlerResult::Stop);
|
||||||
|
}
|
||||||
|
|
||||||
let props = profile.properties.load();
|
let props = profile.properties.load();
|
||||||
let packet = CLoginSuccess::new(
|
let packet = CLoginSuccess::new(
|
||||||
&profile.id,
|
&profile.id,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use super::*;
|
|||||||
impl PendingConnection {
|
impl PendingConnection {
|
||||||
pub async fn handle_login_start(
|
pub async fn handle_login_start(
|
||||||
&mut self,
|
&mut self,
|
||||||
server: &Server,
|
server: &Arc<Server>,
|
||||||
login_start: SLoginStart,
|
login_start: SLoginStart,
|
||||||
) -> Option<PacketHandlerResult> {
|
) -> Option<PacketHandlerResult> {
|
||||||
debug!("login start");
|
debug!("login start");
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use super::*;
|
|||||||
impl PendingConnection {
|
impl PendingConnection {
|
||||||
pub async fn handle_plugin_response(
|
pub async fn handle_plugin_response(
|
||||||
&mut self,
|
&mut self,
|
||||||
server: &Server,
|
server: &Arc<Server>,
|
||||||
plugin_response: SLoginPluginResponse,
|
plugin_response: SLoginPluginResponse,
|
||||||
) -> Option<PacketHandlerResult> {
|
) -> Option<PacketHandlerResult> {
|
||||||
debug!("Handling plugin");
|
debug!("Handling plugin");
|
||||||
|
|||||||
@ -1148,12 +1148,45 @@ impl JavaClient {
|
|||||||
}
|
}
|
||||||
id if id == SCustomPayload::to_id(version) => {
|
id if id == SCustomPayload::to_id(version) => {
|
||||||
let payload = SCustomPayload::read(&mut payload, &version)?;
|
let payload = SCustomPayload::read(&mut payload, &version)?;
|
||||||
|
let channel_str = payload.channel.to_string();
|
||||||
let mut event = PlayerCustomPayloadEvent::new(
|
let mut event = PlayerCustomPayloadEvent::new(
|
||||||
player.clone(),
|
player.clone(),
|
||||||
payload.channel.to_string(),
|
channel_str.clone(),
|
||||||
Bytes::copy_from_slice(payload.data),
|
Bytes::copy_from_slice(payload.data),
|
||||||
);
|
);
|
||||||
server.plugin_manager.fire_blocking(server, &mut event);
|
server.plugin_manager.fire_blocking(server, &mut event);
|
||||||
|
|
||||||
|
if channel_str == "minecraft:register" {
|
||||||
|
if let Ok(channels_data) = std::str::from_utf8(payload.data) {
|
||||||
|
for ch in channels_data.split('\0') {
|
||||||
|
if !ch.is_empty() {
|
||||||
|
let mut reg_event = crate::plugin::api::events::player::player_register_channel::PlayerRegisterChannelEvent::new(
|
||||||
|
player.clone(),
|
||||||
|
ch.to_string(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(server, &mut reg_event);
|
||||||
|
let mut ch_event = crate::plugin::api::events::player::player_channel::PlayerChannelEvent {
|
||||||
|
player: player.clone(),
|
||||||
|
channel: ch.to_string(),
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(server, &mut ch_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if channel_str == "minecraft:unregister"
|
||||||
|
&& let Ok(channels_data) = std::str::from_utf8(payload.data)
|
||||||
|
{
|
||||||
|
for ch in channels_data.split('\0') {
|
||||||
|
if !ch.is_empty() {
|
||||||
|
let mut unreg_event = crate::plugin::api::events::player::player_unregister_channel::PlayerUnregisterChannelEvent::new(
|
||||||
|
player.clone(),
|
||||||
|
ch.to_string(),
|
||||||
|
);
|
||||||
|
server.plugin_manager.fire_blocking(server, &mut unreg_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
id if id == SRecipeBookChangeSettings::to_id(version) => {
|
id if id == SRecipeBookChangeSettings::to_id(version) => {
|
||||||
self.handle_recipe_book_change_settings(
|
self.handle_recipe_book_change_settings(
|
||||||
|
|||||||
@ -313,7 +313,7 @@ impl PendingConnection {
|
|||||||
|
|
||||||
async fn handle_login_packet(
|
async fn handle_login_packet(
|
||||||
&mut self,
|
&mut self,
|
||||||
server: &Server,
|
server: &Arc<Server>,
|
||||||
packet: &RawPacket,
|
packet: &RawPacket,
|
||||||
) -> Result<Option<PacketHandlerResult>, ReadingError> {
|
) -> Result<Option<PacketHandlerResult>, ReadingError> {
|
||||||
debug!("Handling login group");
|
debug!("Handling login group");
|
||||||
|
|||||||
@ -8,12 +8,34 @@ impl JavaClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pages: Vec<String> = packet.pages.iter().map(|p| (*p).to_string()).collect();
|
let mut pages: Vec<String> = packet.pages.iter().map(|p| (*p).to_string()).collect();
|
||||||
|
let mut title = packet.title.map(std::string::ToString::to_string);
|
||||||
|
let signing = title.is_some();
|
||||||
|
let slot = player.inventory().get_selected_slot() as u32;
|
||||||
|
|
||||||
if let Some(title) = packet.title {
|
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
|
||||||
|
&& let Some(server) = player.world().server.upgrade()
|
||||||
|
{
|
||||||
|
let mut event = crate::plugin::api::events::player::player_edit_book::PlayerEditBookEvent {
|
||||||
|
player: player_arc,
|
||||||
|
slot,
|
||||||
|
pages: pages.clone(),
|
||||||
|
title: title.clone(),
|
||||||
|
signing,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
||||||
|
if event.cancelled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pages = event.pages;
|
||||||
|
title = event.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(title) = title {
|
||||||
let mut written_book = ItemStack::new(1, &Item::WRITTEN_BOOK);
|
let mut written_book = ItemStack::new(1, &Item::WRITTEN_BOOK);
|
||||||
let content = WrittenBookContentImpl {
|
let content = WrittenBookContentImpl {
|
||||||
title: title.to_string(),
|
title,
|
||||||
author: player.gameprofile.name.clone(),
|
author: player.gameprofile.name.clone(),
|
||||||
pages,
|
pages,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -160,9 +160,20 @@ impl JavaClient {
|
|||||||
self.update_sequence(player_action.sequence.0);
|
self.update_sequence(player_action.sequence.0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player.mining.store(false, Ordering::Relaxed);
|
|
||||||
let entity = &player.get_entity();
|
let entity = &player.get_entity();
|
||||||
entity.world.load().set_block_breaking(
|
let world = entity.world.load_full();
|
||||||
|
if let Some(server_arc) = world.server.upgrade() {
|
||||||
|
let mut abort_event = crate::plugin::api::events::block::block_damage_abort::BlockDamageAbortEvent::new(
|
||||||
|
player.clone(),
|
||||||
|
player_action.position,
|
||||||
|
world.clone(),
|
||||||
|
player.inventory().held_item(),
|
||||||
|
);
|
||||||
|
server_arc.plugin_manager.fire_blocking(&server_arc, &mut abort_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.mining.store(false, Ordering::Relaxed);
|
||||||
|
world.set_block_breaking(
|
||||||
entity,
|
entity,
|
||||||
player_action.position,
|
player_action.position,
|
||||||
BlockBreakingProgress::Stop,
|
BlockBreakingProgress::Stop,
|
||||||
|
|||||||
@ -79,22 +79,7 @@ impl HostEntity for PluginHostState {
|
|||||||
) -> wasmtime::Result<entity_types::EntityType> {
|
) -> wasmtime::Result<entity_types::EntityType> {
|
||||||
let entity = entity_from_resource(self, &entity)?;
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
let original_name = entity.get_entity().entity_type.resource_name;
|
let original_name = entity.get_entity().entity_type.resource_name;
|
||||||
|
to_wit_entity_type(original_name)
|
||||||
let mut names: Vec<String> = serde_json::from_str::<
|
|
||||||
std::collections::BTreeMap<String, serde_json::Value>,
|
|
||||||
>(&std::fs::read_to_string("assets/entities.json")?)?
|
|
||||||
.keys()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
names.sort();
|
|
||||||
|
|
||||||
let index = names
|
|
||||||
.iter()
|
|
||||||
.position(|n| n == original_name)
|
|
||||||
.ok_or_else(|| wasmtime::Error::msg(format!("Unknown entity type: {original_name}")))?;
|
|
||||||
|
|
||||||
// SAFETY: The WIT enum is generated from the sorted keys of assets/entities.json.
|
|
||||||
Ok(unsafe { std::mem::transmute::<u8, entity_types::EntityType>(index as u8) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_position(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Position> {
|
async fn get_position(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Position> {
|
||||||
@ -925,3 +910,24 @@ impl HostEntity for PluginHostState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_wit_entity_type(name: &str) -> wasmtime::Result<entity_types::EntityType> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
let index = pumpkin_data::entity::EntityType::ALL
|
||||||
|
.binary_search_by_key(&name, |e| e.resource_name)
|
||||||
|
.map_err(|_| wasmtime::Error::msg(format!("Unknown entity type: {name}")))?;
|
||||||
|
|
||||||
|
// SAFETY: The WIT enum is generated with variants matching EntityType::ALL in alphabetical order.
|
||||||
|
Ok(unsafe { std::mem::transmute::<u8, entity_types::EntityType>(index as u8) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_wit_entity_type(
|
||||||
|
entity_type: entity_types::EntityType,
|
||||||
|
) -> wasmtime::Result<&'static pumpkin_data::entity::EntityType> {
|
||||||
|
let index = entity_type as usize;
|
||||||
|
pumpkin_data::entity::EntityType::ALL
|
||||||
|
.get(index)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| wasmtime::Error::msg(format!("Invalid entity type index: {index}")))
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -281,21 +281,7 @@ impl PluginHostState {
|
|||||||
fn get_wit_biome(
|
fn get_wit_biome(
|
||||||
biome: &pumpkin_data::biome::Biome,
|
biome: &pumpkin_data::biome::Biome,
|
||||||
) -> wasmtime::Result<pumpkin::plugin::biomes::Biome> {
|
) -> wasmtime::Result<pumpkin::plugin::biomes::Biome> {
|
||||||
let mut names: Vec<String> = serde_json::from_str::<
|
to_wit_biome(biome)
|
||||||
std::collections::BTreeMap<String, serde_json::Value>,
|
|
||||||
>(&std::fs::read_to_string("assets/biome.json")?)?
|
|
||||||
.keys()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
names.sort();
|
|
||||||
|
|
||||||
let index = names
|
|
||||||
.iter()
|
|
||||||
.position(|n| n.strip_prefix("minecraft:").unwrap_or(n) == biome.registry_id)
|
|
||||||
.ok_or_else(|| wasmtime::Error::msg(format!("Unknown biome: {}", biome.registry_id)))?;
|
|
||||||
|
|
||||||
// SAFETY: The WIT enum is generated from the sorted keys of assets/biome.json.
|
|
||||||
Ok(unsafe { std::mem::transmute::<u8, pumpkin::plugin::biomes::Biome>(index as u8) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_wit_block_entity(
|
fn get_wit_block_entity(
|
||||||
@ -1038,20 +1024,8 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
|||||||
let world_ref = self.get_world_res(&world)?;
|
let world_ref = self.get_world_res(&world)?;
|
||||||
let world_provider = world_ref.provider.clone();
|
let world_provider = world_ref.provider.clone();
|
||||||
|
|
||||||
let mut names: Vec<String> = serde_json::from_str::<
|
let internal_type =
|
||||||
std::collections::BTreeMap<String, serde_json::Value>,
|
super::entity::from_wit_entity_type(entity_type)?;
|
||||||
>(&std::fs::read_to_string("assets/entities.json")?)?
|
|
||||||
.keys()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
names.sort();
|
|
||||||
|
|
||||||
let type_name = names.get(entity_type as usize).ok_or_else(|| {
|
|
||||||
wasmtime::Error::msg(format!("Invalid entity type index: {}", entity_type as u8))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let internal_type = pumpkin_data::entity::EntityType::from_name(type_name)
|
|
||||||
.ok_or_else(|| wasmtime::Error::msg(format!("Invalid entity type: {type_name}")))?;
|
|
||||||
|
|
||||||
let internal_pos = pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2);
|
let internal_pos = pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2);
|
||||||
let entity = crate::entity::r#type::from_type(
|
let entity = crate::entity::r#type::from_type(
|
||||||
@ -2340,9 +2314,24 @@ pub const fn from_wit_sound_category(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_wit_biome(
|
||||||
|
biome: &pumpkin_data::biome::Biome,
|
||||||
|
) -> wasmtime::Result<pumpkin::plugin::biomes::Biome> {
|
||||||
|
let name = biome
|
||||||
|
.registry_id
|
||||||
|
.strip_prefix("minecraft:")
|
||||||
|
.unwrap_or(biome.registry_id);
|
||||||
|
let index = pumpkin_data::biome::Biome::ALL
|
||||||
|
.binary_search_by_key(&name, |b| b.registry_id)
|
||||||
|
.map_err(|_| wasmtime::Error::msg(format!("Unknown biome: {}", biome.registry_id)))?;
|
||||||
|
|
||||||
|
// SAFETY: The WIT enum is generated with variants matching Biome::ALL in alphabetical order.
|
||||||
|
Ok(unsafe { std::mem::transmute::<u8, pumpkin::plugin::biomes::Biome>(index as u8) })
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::pumpkin;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wit_particle_ids_match_internal_particle_ids() {
|
fn wit_particle_ids_match_internal_particle_ids() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user