From f27e64afbfd1848f9330fd5319aff8e981486d38 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 14 Aug 2026 15:58:16 +0200 Subject: [PATCH] chore: add some more events --- .../enchanting/enchanting_screen_handler.rs | 29 +- .../pumpkin-inventory/src/screen_handler.rs | 24 ++ .../src/events/block/block_dispense.rs | 20 + .../src/events/block/block_explode.rs | 20 + .../src/events/block/block_physics.rs | 20 + .../src/events/block/block_piston.rs | 40 ++ .../src/events/block/mod.rs | 24 ++ .../src/events/block/note_play.rs | 20 + .../src/events/block/sign_change.rs | 20 + .../src/events/block/sponge_absorb.rs | 20 + .../src/events/block/tnt_prime.rs | 20 + .../src/events/entity/entity_air_change.rs | 21 + .../src/events/entity/entity_breed.rs | 21 + .../src/events/entity/entity_dismount.rs | 21 + .../src/events/entity/entity_dye.rs | 21 + .../events/entity/entity_enter_love_mode.rs | 21 + .../src/events/entity/entity_explode.rs | 21 + .../src/events/entity/entity_mount.rs | 21 + .../src/events/entity/entity_pickup_item.rs | 21 + .../src/events/entity/entity_portal.rs | 21 + .../src/events/entity/entity_resurrect.rs | 21 + .../src/events/entity/entity_shoot_bow.rs | 21 + .../src/events/entity/entity_tame.rs | 21 + .../src/events/entity/entity_target.rs | 21 + .../src/events/entity/entity_teleport.rs | 21 + .../src/events/entity/entity_toggle_glide.rs | 21 + .../src/events/entity/entity_transform.rs | 21 + .../src/events/entity/mod.rs | 48 +++ crates/pumpkin-plugin-wit | 2 +- crates/pumpkin/src/block/blocks/farmland.rs | 12 + crates/pumpkin/src/block/blocks/note.rs | 11 + .../pumpkin/src/block/blocks/piston/piston.rs | 24 ++ .../pumpkin/src/block/blocks/plant/sapling.rs | 9 + .../src/block/blocks/redstone/dispenser.rs | 13 + crates/pumpkin/src/block/blocks/sponge.rs | 9 + crates/pumpkin/src/block/blocks/tnt.rs | 24 ++ .../src/block/entities/brewing_stand.rs | 11 + .../entities/furnace_like_block_entity.rs | 84 ++-- .../pumpkin/src/block/fluid/flowing_trait.rs | 12 + .../pumpkin/src/entity/ai/goal/bow_attack.rs | 13 + .../src/entity/ai/goal/pick_up_block.rs | 12 + crates/pumpkin/src/entity/item.rs | 16 +- crates/pumpkin/src/entity/living.rs | 101 ++++- crates/pumpkin/src/entity/mob/mod.rs | 147 ++++++- crates/pumpkin/src/entity/mod.rs | 103 ++++- crates/pumpkin/src/entity/passive/chicken.rs | 13 +- crates/pumpkin/src/entity/player.rs | 143 ++++++- crates/pumpkin/src/entity/projectile/arrow.rs | 34 +- crates/pumpkin/src/entity/vehicle/vehicle.rs | 93 +++++ .../src/item/items/ignite/flint_and_steel.rs | 9 +- .../src/net/java/play/player_command.rs | 9 +- .../pumpkin/src/net/java/play/select_trade.rs | 11 + .../pumpkin/src/net/java/play/update_sign.rs | 21 + crates/pumpkin/src/net/java/play/use_item.rs | 10 + .../api/events/enchantment/enchant_item.rs | 46 ++ .../src/plugin/api/events/enchantment/mod.rs | 5 + .../enchantment/prepare_item_enchant.rs | 50 +++ .../api/events/entity/entity_break_door.rs | 23 + .../api/events/entity/entity_change_block.rs | 26 ++ .../api/events/entity/entity_dismount.rs | 23 + .../api/events/entity/entity_drop_item.rs | 25 ++ .../api/events/entity/entity_enter_block.rs | 23 + .../events/entity/entity_enter_love_mode.rs | 27 ++ .../api/events/entity/entity_exhaustion.rs | 22 + .../api/events/entity/entity_interact.rs | 23 + .../api/events/entity/entity_pickup_item.rs | 27 ++ .../plugin/api/events/entity/entity_place.rs | 26 ++ .../plugin/api/events/entity/entity_portal.rs | 24 ++ .../api/events/entity/entity_pose_change.rs | 22 + .../api/events/entity/entity_potion_effect.rs | 28 ++ .../api/events/entity/entity_resurrect.rs | 19 + .../api/events/entity/entity_shoot_bow.rs | 27 ++ .../api/events/entity/entity_toggle_glide.rs | 23 + .../api/events/entity/entity_toggle_swim.rs | 22 + .../api/events/entity/entity_transform.rs | 27 ++ .../api/events/entity/explosion_prime.rs | 25 ++ .../api/events/entity/food_level_change.rs | 22 + .../plugin/api/events/entity/item_despawn.rs | 19 + .../plugin/api/events/entity/item_spawn.rs | 26 ++ .../src/plugin/api/events/entity/mod.rs | 48 +++ .../api/events/entity/projectile_hit.rs | 30 ++ .../api/events/entity/projectile_launch.rs | 22 + crates/pumpkin/src/plugin/api/events/mod.rs | 3 + .../pumpkin/src/plugin/api/events/raid/mod.rs | 9 + .../src/plugin/api/events/raid/raid_finish.rs | 19 + .../plugin/api/events/raid/raid_spawn_wave.rs | 23 + .../src/plugin/api/events/raid/raid_stop.rs | 19 + .../plugin/api/events/raid/raid_trigger.rs | 20 + .../src/plugin/api/events/server/mod.rs | 20 + .../api/events/server/plugin_disable.rs | 19 + .../plugin/api/events/server/plugin_enable.rs | 19 + .../events/server/remote_server_command.rs | 19 + .../api/events/server/service_register.rs | 19 + .../api/events/server/service_unregister.rs | 19 + .../plugin/api/events/server/tab_complete.rs | 22 + .../src/plugin/api/events/vehicle/mod.rs | 19 + .../events/vehicle/vehicle_block_collision.rs | 23 + .../api/events/vehicle/vehicle_create.rs | 19 + .../api/events/vehicle/vehicle_damage.rs | 25 ++ .../api/events/vehicle/vehicle_destroy.rs | 22 + .../api/events/vehicle/vehicle_enter.rs | 22 + .../vehicle/vehicle_entity_collision.rs | 22 + .../plugin/api/events/vehicle/vehicle_exit.rs | 22 + .../plugin/api/events/vehicle/vehicle_move.rs | 26 ++ .../api/events/vehicle/vehicle_update.rs | 19 + .../plugin/api/events/world/chunk_populate.rs | 20 + .../plugin/api/events/world/chunk_unload.rs | 20 + .../plugin/api/events/world/entities_load.rs | 23 + .../api/events/world/entities_unload.rs | 23 + .../plugin/api/events/world/loot_generate.rs | 19 + .../src/plugin/api/events/world/mod.rs | 14 + .../src/plugin/api/events/world/time_skip.rs | 19 + .../src/plugin/api/events/world/world_save.rs | 19 + .../loader/wasm/wasm_host/wit/v0_1/context.rs | 189 ++++++++- .../wasm/wasm_host/wit/v0_1/events/block.rs | 219 +++++++++- .../wasm_host/wit/v0_1/events/enchantment.rs | 134 ++++++ .../wasm/wasm_host/wit/v0_1/events/entity.rs | 393 +++++++++++++++++- .../wasm/wasm_host/wit/v0_1/events/mod.rs | 1 + crates/pumpkin/src/server/mod.rs | 61 +++ crates/pumpkin/src/world/explosion.rs | 16 + crates/pumpkin/src/world/mod.rs | 176 +++++++- 121 files changed, 4051 insertions(+), 61 deletions(-) create mode 100644 crates/pumpkin-plugin-api/src/events/block/block_dispense.rs create mode 100644 crates/pumpkin-plugin-api/src/events/block/block_explode.rs create mode 100644 crates/pumpkin-plugin-api/src/events/block/block_physics.rs create mode 100644 crates/pumpkin-plugin-api/src/events/block/block_piston.rs create mode 100644 crates/pumpkin-plugin-api/src/events/block/note_play.rs create mode 100644 crates/pumpkin-plugin-api/src/events/block/sign_change.rs create mode 100644 crates/pumpkin-plugin-api/src/events/block/sponge_absorb.rs create mode 100644 crates/pumpkin-plugin-api/src/events/block/tnt_prime.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_air_change.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_breed.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_dismount.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_dye.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_enter_love_mode.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_explode.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_mount.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_pickup_item.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_portal.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_resurrect.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_shoot_bow.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_tame.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_target.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_teleport.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_toggle_glide.rs create mode 100644 crates/pumpkin-plugin-api/src/events/entity/entity_transform.rs create mode 100644 crates/pumpkin/src/plugin/api/events/enchantment/enchant_item.rs create mode 100644 crates/pumpkin/src/plugin/api/events/enchantment/mod.rs create mode 100644 crates/pumpkin/src/plugin/api/events/enchantment/prepare_item_enchant.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_break_door.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_change_block.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_dismount.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_drop_item.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_enter_block.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_enter_love_mode.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_exhaustion.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_interact.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_pickup_item.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_place.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_portal.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_pose_change.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_potion_effect.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_resurrect.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_shoot_bow.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_toggle_glide.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_toggle_swim.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/entity_transform.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/explosion_prime.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/food_level_change.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/item_despawn.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/item_spawn.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/projectile_hit.rs create mode 100644 crates/pumpkin/src/plugin/api/events/entity/projectile_launch.rs create mode 100644 crates/pumpkin/src/plugin/api/events/raid/mod.rs create mode 100644 crates/pumpkin/src/plugin/api/events/raid/raid_finish.rs create mode 100644 crates/pumpkin/src/plugin/api/events/raid/raid_spawn_wave.rs create mode 100644 crates/pumpkin/src/plugin/api/events/raid/raid_stop.rs create mode 100644 crates/pumpkin/src/plugin/api/events/raid/raid_trigger.rs create mode 100644 crates/pumpkin/src/plugin/api/events/server/plugin_disable.rs create mode 100644 crates/pumpkin/src/plugin/api/events/server/plugin_enable.rs create mode 100644 crates/pumpkin/src/plugin/api/events/server/remote_server_command.rs create mode 100644 crates/pumpkin/src/plugin/api/events/server/service_register.rs create mode 100644 crates/pumpkin/src/plugin/api/events/server/service_unregister.rs create mode 100644 crates/pumpkin/src/plugin/api/events/server/tab_complete.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/mod.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_block_collision.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_create.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_damage.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_destroy.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_enter.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_entity_collision.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_exit.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_move.rs create mode 100644 crates/pumpkin/src/plugin/api/events/vehicle/vehicle_update.rs create mode 100644 crates/pumpkin/src/plugin/api/events/world/chunk_populate.rs create mode 100644 crates/pumpkin/src/plugin/api/events/world/chunk_unload.rs create mode 100644 crates/pumpkin/src/plugin/api/events/world/entities_load.rs create mode 100644 crates/pumpkin/src/plugin/api/events/world/entities_unload.rs create mode 100644 crates/pumpkin/src/plugin/api/events/world/loot_generate.rs create mode 100644 crates/pumpkin/src/plugin/api/events/world/time_skip.rs create mode 100644 crates/pumpkin/src/plugin/api/events/world/world_save.rs create mode 100644 crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/enchantment.rs diff --git a/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs b/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs index 43d34c730..6bfca91b0 100644 --- a/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs +++ b/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs @@ -93,7 +93,7 @@ impl EnchantingTableScreenHandler { handler } - pub async fn update_enchantments(&mut self, _player: &dyn InventoryPlayer) { + pub async fn update_enchantments(&mut self, player: &dyn InventoryPlayer) { let item = self.inventory.get_stack(0).await; if item.is_empty() || item.has_enchantments() { @@ -145,6 +145,23 @@ impl EnchantingTableScreenHandler { self.enchantment_level[i] = -1; } } + + if player + .fire_prepare_item_enchant_event( + &item, + &mut self.level_requirements, + &mut self.enchantment_id, + &mut self.enchantment_level, + self.bookshelf_count, + ) + .await + { + for i in 0..3 { + self.level_requirements[i] = 0; + self.enchantment_id[i] = -1; + self.enchantment_level[i] = -1; + } + } } } self.send_property_updates().await; @@ -367,13 +384,21 @@ impl ScreenHandler for EnchantingTableScreenHandler { } let mut random = self.create_enchantment_random(id as usize); - let enchantments = + let mut enchantments = Self::get_enchantment_list(&mut random, &item_stack, id as usize, level_req); if enchantments.is_empty() { return false; } + if player + .fire_enchant_item_event(&item_stack, id, level_req, &mut enchantments) + .await + || enchantments.is_empty() + { + return false; + } + if !player.is_creative() { player.add_experience_levels(-(id + 1)).await; lapis_stack.decrement(lapis_cost); diff --git a/crates/pumpkin-inventory/src/screen_handler.rs b/crates/pumpkin-inventory/src/screen_handler.rs index 392e8bc5b..3be6ed9ad 100644 --- a/crates/pumpkin-inventory/src/screen_handler.rs +++ b/crates/pumpkin-inventory/src/screen_handler.rs @@ -33,6 +33,7 @@ use crate::{ }; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::{ + Enchantment, data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl}, screen::WindowType, statistic::StatisticCategory, @@ -203,6 +204,29 @@ pub trait InventoryPlayer: Send + Sync { stat_id: i32, amount: i32, ) -> PlayerFuture<'_, ()>; + + /// Fires a prepare item enchant event. Returns true if cancelled. + fn fire_prepare_item_enchant_event<'a>( + &'a self, + _item: &'a ItemStack, + _level_requirements: &'a mut [i32; 3], + _enchantment_id: &'a mut [i32; 3], + _enchantment_level: &'a mut [i32; 3], + _bookshelf_count: i32, + ) -> PlayerFuture<'a, bool> { + Box::pin(async move { false }) + } + + /// Fires an enchant item event. Returns true if cancelled. + fn fire_enchant_item_event<'a>( + &'a self, + _item: &'a ItemStack, + _option: i32, + _exp_level_cost: i32, + _enchantments_to_add: &'a mut Vec<(&'static Enchantment, i32)>, + ) -> PlayerFuture<'a, bool> { + Box::pin(async move { false }) + } } /// Gives a stack to the player or drops it if inventory is full. diff --git a/crates/pumpkin-plugin-api/src/events/block/block_dispense.rs b/crates/pumpkin-plugin-api/src/events/block/block_dispense.rs new file mode 100644 index 000000000..c3f9c106f --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/block_dispense.rs @@ -0,0 +1,20 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{BlockDispenseEventData, Event, EventType}; + +/// An event that occurs when a block dispenses an item. +pub struct BlockDispenseEvent; +impl FromIntoEvent for BlockDispenseEvent { + const EVENT_TYPE: EventType = EventType::BlockDispenseEvent; + type Data = BlockDispenseEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::BlockDispenseEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::BlockDispenseEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/block/block_explode.rs b/crates/pumpkin-plugin-api/src/events/block/block_explode.rs new file mode 100644 index 000000000..726b9bf9f --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/block_explode.rs @@ -0,0 +1,20 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{BlockExplodeEventData, Event, EventType}; + +/// An event that occurs when a block explodes. +pub struct BlockExplodeEvent; +impl FromIntoEvent for BlockExplodeEvent { + const EVENT_TYPE: EventType = EventType::BlockExplodeEvent; + type Data = BlockExplodeEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::BlockExplodeEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::BlockExplodeEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/block/block_physics.rs b/crates/pumpkin-plugin-api/src/events/block/block_physics.rs new file mode 100644 index 000000000..e68bf6231 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/block_physics.rs @@ -0,0 +1,20 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{BlockPhysicsEventData, Event, EventType}; + +/// An event that occurs when a block physics check is run. +pub struct BlockPhysicsEvent; +impl FromIntoEvent for BlockPhysicsEvent { + const EVENT_TYPE: EventType = EventType::BlockPhysicsEvent; + type Data = BlockPhysicsEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::BlockPhysicsEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::BlockPhysicsEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/block/block_piston.rs b/crates/pumpkin-plugin-api/src/events/block/block_piston.rs new file mode 100644 index 000000000..93c97c541 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/block_piston.rs @@ -0,0 +1,40 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{ + BlockPistonExtendEventData, BlockPistonRetractEventData, Event, EventType, +}; + +/// An event that occurs when a piston extends. +pub struct BlockPistonExtendEvent; +impl FromIntoEvent for BlockPistonExtendEvent { + const EVENT_TYPE: EventType = EventType::BlockPistonExtendEvent; + type Data = BlockPistonExtendEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::BlockPistonExtendEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::BlockPistonExtendEvent(data) + } +} + +/// An event that occurs when a piston retracts. +pub struct BlockPistonRetractEvent; +impl FromIntoEvent for BlockPistonRetractEvent { + const EVENT_TYPE: EventType = EventType::BlockPistonRetractEvent; + type Data = BlockPistonRetractEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::BlockPistonRetractEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::BlockPistonRetractEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/block/mod.rs b/crates/pumpkin-plugin-api/src/events/block/mod.rs index 6dc456eb6..4de629e90 100644 --- a/crates/pumpkin-plugin-api/src/events/block/mod.rs +++ b/crates/pumpkin-plugin-api/src/events/block/mod.rs @@ -6,6 +6,10 @@ pub mod block_burn; pub mod block_can_build; /// Block damage event. pub mod block_damage; +/// Block dispense event. +pub mod block_dispense; +/// Block explode event. +pub mod block_explode; /// Block fade event. pub mod block_fade; /// Block form event. @@ -16,19 +20,39 @@ pub mod block_from_to; pub mod block_grow; /// Block ignite event. pub mod block_ignite; +/// Block physics event. +pub mod block_physics; +/// Block piston event. +pub mod block_piston; /// Block place event. pub mod block_place; /// Block redstone signal event. pub mod block_redstone; +/// Note block play event. +pub mod note_play; +/// Sign text change event. +pub mod sign_change; +/// Sponge absorb water event. +pub mod sponge_absorb; +/// TNT prime event. +pub mod tnt_prime; pub use block_break::*; pub use block_burn::*; pub use block_can_build::*; pub use block_damage::*; +pub use block_dispense::*; +pub use block_explode::*; pub use block_fade::*; pub use block_form::*; pub use block_from_to::*; pub use block_grow::*; pub use block_ignite::*; +pub use block_physics::*; +pub use block_piston::*; pub use block_place::*; pub use block_redstone::*; +pub use note_play::*; +pub use sign_change::*; +pub use sponge_absorb::*; +pub use tnt_prime::*; diff --git a/crates/pumpkin-plugin-api/src/events/block/note_play.rs b/crates/pumpkin-plugin-api/src/events/block/note_play.rs new file mode 100644 index 000000000..774f745ee --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/note_play.rs @@ -0,0 +1,20 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{Event, EventType, NotePlayEventData}; + +/// An event that occurs when a note block plays. +pub struct NotePlayEvent; +impl FromIntoEvent for NotePlayEvent { + const EVENT_TYPE: EventType = EventType::NotePlayEvent; + type Data = NotePlayEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::NotePlayEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::NotePlayEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/block/sign_change.rs b/crates/pumpkin-plugin-api/src/events/block/sign_change.rs new file mode 100644 index 000000000..0eecfdcd8 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/sign_change.rs @@ -0,0 +1,20 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{Event, EventType, SignChangeEventData}; + +/// An event that occurs when a sign's text is changed. +pub struct SignChangeEvent; +impl FromIntoEvent for SignChangeEvent { + const EVENT_TYPE: EventType = EventType::SignChangeEvent; + type Data = SignChangeEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::SignChangeEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::SignChangeEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/block/sponge_absorb.rs b/crates/pumpkin-plugin-api/src/events/block/sponge_absorb.rs new file mode 100644 index 000000000..9b819d1b9 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/sponge_absorb.rs @@ -0,0 +1,20 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{Event, EventType, SpongeAbsorbEventData}; + +/// An event that occurs when a sponge absorbs water. +pub struct SpongeAbsorbEvent; +impl FromIntoEvent for SpongeAbsorbEvent { + const EVENT_TYPE: EventType = EventType::SpongeAbsorbEvent; + type Data = SpongeAbsorbEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::SpongeAbsorbEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::SpongeAbsorbEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/block/tnt_prime.rs b/crates/pumpkin-plugin-api/src/events/block/tnt_prime.rs new file mode 100644 index 000000000..9266a8b28 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/block/tnt_prime.rs @@ -0,0 +1,20 @@ +use super::super::FromIntoEvent; +use crate::wit::pumpkin::plugin::event::{Event, EventType, TntPrimeEventData}; + +/// An event that occurs when TNT is primed. +pub struct TNTPrimeEvent; +impl FromIntoEvent for TNTPrimeEvent { + const EVENT_TYPE: EventType = EventType::TntPrimeEvent; + type Data = TntPrimeEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::TntPrimeEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::TntPrimeEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_air_change.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_air_change.rs new file mode 100644 index 000000000..d7c0a034d --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_air_change.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityAirChangeEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity's air level changes. +pub struct EntityAirChangeEvent; +impl FromIntoEvent for EntityAirChangeEvent { + const EVENT_TYPE: EventType = EventType::EntityAirChangeEvent; + type Data = EntityAirChangeEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityAirChangeEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityAirChangeEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_breed.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_breed.rs new file mode 100644 index 000000000..ddaf9f374 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_breed.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityBreedEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when two entities breed. +pub struct EntityBreedEvent; +impl FromIntoEvent for EntityBreedEvent { + const EVENT_TYPE: EventType = EventType::EntityBreedEvent; + type Data = EntityBreedEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityBreedEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityBreedEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_dismount.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_dismount.rs new file mode 100644 index 000000000..4d448c8f0 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_dismount.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityDismountEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity dismounts another entity. +pub struct EntityDismountEvent; +impl FromIntoEvent for EntityDismountEvent { + const EVENT_TYPE: EventType = EventType::EntityDismountEvent; + type Data = EntityDismountEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityDismountEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityDismountEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_dye.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_dye.rs new file mode 100644 index 000000000..ec36f1d93 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_dye.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityDyeEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity is dyed. +pub struct EntityDyeEvent; +impl FromIntoEvent for EntityDyeEvent { + const EVENT_TYPE: EventType = EventType::EntityDyeEvent; + type Data = EntityDyeEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityDyeEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityDyeEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_enter_love_mode.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_enter_love_mode.rs new file mode 100644 index 000000000..9ba42575e --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_enter_love_mode.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityEnterLoveModeEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity enters love mode. +pub struct EntityEnterLoveModeEvent; +impl FromIntoEvent for EntityEnterLoveModeEvent { + const EVENT_TYPE: EventType = EventType::EntityEnterLoveModeEvent; + type Data = EntityEnterLoveModeEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityEnterLoveModeEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityEnterLoveModeEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_explode.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_explode.rs new file mode 100644 index 000000000..3f3c18def --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_explode.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityExplodeEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity explodes. +pub struct EntityExplodeEvent; +impl FromIntoEvent for EntityExplodeEvent { + const EVENT_TYPE: EventType = EventType::EntityExplodeEvent; + type Data = EntityExplodeEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityExplodeEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityExplodeEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_mount.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_mount.rs new file mode 100644 index 000000000..adcc11634 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_mount.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityMountEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity mounts another entity. +pub struct EntityMountEvent; +impl FromIntoEvent for EntityMountEvent { + const EVENT_TYPE: EventType = EventType::EntityMountEvent; + type Data = EntityMountEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityMountEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityMountEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_pickup_item.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_pickup_item.rs new file mode 100644 index 000000000..99fa7b81a --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_pickup_item.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityPickupItemEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity picks up an item. +pub struct EntityPickupItemEvent; +impl FromIntoEvent for EntityPickupItemEvent { + const EVENT_TYPE: EventType = EventType::EntityPickupItemEvent; + type Data = EntityPickupItemEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityPickupItemEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityPickupItemEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_portal.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_portal.rs new file mode 100644 index 000000000..28c6709c2 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_portal.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityPortalEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity enters a portal. +pub struct EntityPortalEvent; +impl FromIntoEvent for EntityPortalEvent { + const EVENT_TYPE: EventType = EventType::EntityPortalEvent; + type Data = EntityPortalEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityPortalEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityPortalEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_resurrect.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_resurrect.rs new file mode 100644 index 000000000..7dd979885 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_resurrect.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityResurrectEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity is resurrected. +pub struct EntityResurrectEvent; +impl FromIntoEvent for EntityResurrectEvent { + const EVENT_TYPE: EventType = EventType::EntityResurrectEvent; + type Data = EntityResurrectEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityResurrectEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityResurrectEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_shoot_bow.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_shoot_bow.rs new file mode 100644 index 000000000..e1dc2c2ee --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_shoot_bow.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityShootBowEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity shoots a bow. +pub struct EntityShootBowEvent; +impl FromIntoEvent for EntityShootBowEvent { + const EVENT_TYPE: EventType = EventType::EntityShootBowEvent; + type Data = EntityShootBowEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityShootBowEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityShootBowEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_tame.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_tame.rs new file mode 100644 index 000000000..4e852c93c --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_tame.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityTameEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity is tamed. +pub struct EntityTameEvent; +impl FromIntoEvent for EntityTameEvent { + const EVENT_TYPE: EventType = EventType::EntityTameEvent; + type Data = EntityTameEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityTameEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityTameEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_target.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_target.rs new file mode 100644 index 000000000..645b37c6f --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_target.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityTargetEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity targets another entity. +pub struct EntityTargetEvent; +impl FromIntoEvent for EntityTargetEvent { + const EVENT_TYPE: EventType = EventType::EntityTargetEvent; + type Data = EntityTargetEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityTargetEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityTargetEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_teleport.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_teleport.rs new file mode 100644 index 000000000..23eebc355 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_teleport.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityTeleportEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity teleports. +pub struct EntityTeleportEvent; +impl FromIntoEvent for EntityTeleportEvent { + const EVENT_TYPE: EventType = EventType::EntityTeleportEvent; + type Data = EntityTeleportEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityTeleportEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityTeleportEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_toggle_glide.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_toggle_glide.rs new file mode 100644 index 000000000..af1c9d1f8 --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_toggle_glide.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityToggleGlideEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity toggles gliding. +pub struct EntityToggleGlideEvent; +impl FromIntoEvent for EntityToggleGlideEvent { + const EVENT_TYPE: EventType = EventType::EntityToggleGlideEvent; + type Data = EntityToggleGlideEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityToggleGlideEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityToggleGlideEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/entity_transform.rs b/crates/pumpkin-plugin-api/src/events/entity/entity_transform.rs new file mode 100644 index 000000000..f32c5886d --- /dev/null +++ b/crates/pumpkin-plugin-api/src/events/entity/entity_transform.rs @@ -0,0 +1,21 @@ +use crate::wit::pumpkin::plugin::event::{EntityTransformEventData, Event, EventType}; + +use super::super::FromIntoEvent; + +/// Event triggered when an entity transforms into another entity. +pub struct EntityTransformEvent; +impl FromIntoEvent for EntityTransformEvent { + const EVENT_TYPE: EventType = EventType::EntityTransformEvent; + type Data = EntityTransformEventData; + + fn data_from_event(event: Event) -> Self::Data { + match event { + Event::EntityTransformEvent(data) => data, + _ => panic!("unexpected event"), + } + } + + fn data_into_event(data: Self::Data) -> Event { + Event::EntityTransformEvent(data) + } +} diff --git a/crates/pumpkin-plugin-api/src/events/entity/mod.rs b/crates/pumpkin-plugin-api/src/events/entity/mod.rs index 5065e7571..6c88e513a 100644 --- a/crates/pumpkin-plugin-api/src/events/entity/mod.rs +++ b/crates/pumpkin-plugin-api/src/events/entity/mod.rs @@ -1,16 +1,64 @@ +/// Entity air change event. +pub mod entity_air_change; +/// Entity breeding event. +pub mod entity_breed; /// Entity combust (catch fire) event. pub mod entity_combust; /// Entity damage event. pub mod entity_damage; /// Entity death and player death events. pub mod entity_death; +/// Entity dismount event. +pub mod entity_dismount; +/// Entity dye event. +pub mod entity_dye; +/// Entity enter love mode event. +pub mod entity_enter_love_mode; +/// Entity explode event. +pub mod entity_explode; +/// Entity mount event. +pub mod entity_mount; +/// Entity pickup item event. +pub mod entity_pickup_item; +/// Entity portal travel event. +pub mod entity_portal; /// Entity health regeneration event. pub mod entity_regain_health; +/// Entity resurrect event. +pub mod entity_resurrect; +/// Entity shoot bow event. +pub mod entity_shoot_bow; /// Entity spawn event. pub mod entity_spawn; +/// Entity tame event. +pub mod entity_tame; +/// Entity target event. +pub mod entity_target; +/// Entity teleport event. +pub mod entity_teleport; +/// Entity toggle glide event. +pub mod entity_toggle_glide; +/// Entity transform event. +pub mod entity_transform; +pub use entity_air_change::*; +pub use entity_breed::*; pub use entity_combust::*; pub use entity_damage::*; pub use entity_death::*; +pub use entity_dismount::*; +pub use entity_dye::*; +pub use entity_enter_love_mode::*; +pub use entity_explode::*; +pub use entity_mount::*; +pub use entity_pickup_item::*; +pub use entity_portal::*; pub use entity_regain_health::*; +pub use entity_resurrect::*; +pub use entity_shoot_bow::*; pub use entity_spawn::*; +pub use entity_tame::*; +pub use entity_target::*; +pub use entity_teleport::*; +pub use entity_toggle_glide::*; +pub use entity_transform::*; diff --git a/crates/pumpkin-plugin-wit b/crates/pumpkin-plugin-wit index 6d5dc27d6..12e3fdd3e 160000 --- a/crates/pumpkin-plugin-wit +++ b/crates/pumpkin-plugin-wit @@ -1 +1 @@ -Subproject commit 6d5dc27d6bd7372e072e54af08881db4197a99ef +Subproject commit 12e3fdd3ed121c58d57902450ad40bec538def71 diff --git a/crates/pumpkin/src/block/blocks/farmland.rs b/crates/pumpkin/src/block/blocks/farmland.rs index a09ba6e59..02e5f6292 100644 --- a/crates/pumpkin/src/block/blocks/farmland.rs +++ b/crates/pumpkin/src/block/blocks/farmland.rs @@ -89,6 +89,18 @@ impl BlockBehaviour for FarmlandBlock { .get_block(&args.position.up()) .has_tag(&tag::Block::MINECRAFT_MAINTAINS_FARMLAND) { + let mut event = + crate::plugin::api::events::block::block_fade::BlockFadeEvent::new( + *args.position, + &Block::DIRT, + ); + if let Some(server) = args.world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + //TODO push entities up args.world .set_block_state( diff --git a/crates/pumpkin/src/block/blocks/note.rs b/crates/pumpkin/src/block/blocks/note.rs index 1490d7b85..2e483a8ec 100644 --- a/crates/pumpkin/src/block/blocks/note.rs +++ b/crates/pumpkin/src/block/blocks/note.rs @@ -27,6 +27,17 @@ pub struct NoteBlock; impl NoteBlock { pub async fn play_note(props: &NoteBlockLikeProperties, world: &World, pos: &BlockPos) { if !is_base_block(props.instrument) || world.get_block_state(&pos.up()).is_air() { + let mut event = crate::plugin::api::events::block::note_play::NotePlayEvent::new( + *pos, + format!("{:?}", props.instrument), + props.note, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } world.add_synced_block_event(*pos, 0, 0).await; } } diff --git a/crates/pumpkin/src/block/blocks/piston/piston.rs b/crates/pumpkin/src/block/blocks/piston/piston.rs index fc01bc7b8..3a10db337 100644 --- a/crates/pumpkin/src/block/blocks/piston/piston.rs +++ b/crates/pumpkin/src/block/blocks/piston/piston.rs @@ -168,6 +168,18 @@ impl BlockBehaviour for PistonBlock { // Extend Piston if r#type == 0 { + let mut event = + crate::plugin::api::events::block::block_piston::BlockPistonExtendEvent::new( + *pos, + format!("{dir:?}"), + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return false; + } + if !move_piston(world, dir, pos, true, sticky).await { return false; } @@ -192,6 +204,18 @@ impl BlockBehaviour for PistonBlock { } // Reduce Piston + let mut event = + crate::plugin::api::events::block::block_piston::BlockPistonRetractEvent::new( + *pos, + format!("{dir:?}"), + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return false; + } + let extended_pos = pos.offset(dir.to_offset()); if let Some(block_entity) = world.get_block_entity(&extended_pos) diff --git a/crates/pumpkin/src/block/blocks/plant/sapling.rs b/crates/pumpkin/src/block/blocks/plant/sapling.rs index e6c7b2896..2ba52acbc 100644 --- a/crates/pumpkin/src/block/blocks/plant/sapling.rs +++ b/crates/pumpkin/src/block/blocks/plant/sapling.rs @@ -18,6 +18,15 @@ pub struct SaplingBlock; impl SaplingBlock { async fn generate(&self, world: &Arc, pos: &BlockPos) { + use crate::plugin::api::events::world::structure_grow::{StructureGrowEvent, TreeType}; + let mut event = StructureGrowEvent::new(*pos, TreeType::Oak, false); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + let (block, state) = world.get_block_and_state_id(pos); let mut props = SaplingProperties::from_state_id(state, block); if props.stage == 0 { diff --git a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs index a2d90655c..91b857483 100644 --- a/crates/pumpkin/src/block/blocks/redstone/dispenser.rs +++ b/crates/pumpkin/src/block/blocks/redstone/dispenser.rs @@ -255,6 +255,19 @@ impl DispenserBlock { dispenser: &DispenserBlockEntity, item: &mut ItemStack, ) { + let mut event = crate::plugin::api::events::block::block_dispense::BlockDispenseEvent::new( + *ctx.position, + item.item.registry_key.to_string(), + ); + if let Some(server) = ctx.world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + ctx.world + .sync_world_event(WorldEvent::SoundDispenserFail, *ctx.position, 0); + return; + } + // Still missing some specific dispenser behavior that you can find here: // https://minecraft.wiki/w/Dispenser#Usage let arrows = [ diff --git a/crates/pumpkin/src/block/blocks/sponge.rs b/crates/pumpkin/src/block/blocks/sponge.rs index 157d6f2ae..fb92053a3 100644 --- a/crates/pumpkin/src/block/blocks/sponge.rs +++ b/crates/pumpkin/src/block/blocks/sponge.rs @@ -67,6 +67,15 @@ impl SpongeBlock { if water_blocks.is_empty() { false } else { + let mut event = + crate::plugin::api::events::block::sponge_absorb::SpongeAbsorbEvent::new(*position); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return false; + } + for water_pos in &water_blocks { world .set_block_state(water_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) diff --git a/crates/pumpkin/src/block/blocks/tnt.rs b/crates/pumpkin/src/block/blocks/tnt.rs index 34565909c..e06b6b642 100644 --- a/crates/pumpkin/src/block/blocks/tnt.rs +++ b/crates/pumpkin/src/block/blocks/tnt.rs @@ -24,7 +24,31 @@ pub struct TNTBlock; impl TNTBlock { pub async fn prime(world: &Arc, location: &BlockPos) { + let mut event = crate::plugin::api::events::block::tnt_prime::TNTPrimeEvent::new( + *location, + "REDSTONE".to_string(), + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + let entity = Entity::new(world.clone(), location.to_f64(), &EntityType::TNT); + let mut prime_event = + crate::plugin::api::events::entity::explosion_prime::ExplosionPrimeEvent::new( + entity.entity_id, + DEFAULT_POWER, + false, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut prime_event).await; + } + if prime_event.cancelled { + return; + } + let pos = entity.pos.load(); let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, DEFAULT_FUSE)); world.spawn_entity(tnt).await; diff --git a/crates/pumpkin/src/block/entities/brewing_stand.rs b/crates/pumpkin/src/block/entities/brewing_stand.rs index 0a7e6d000..ff832d5fd 100644 --- a/crates/pumpkin/src/block/entities/brewing_stand.rs +++ b/crates/pumpkin/src/block/entities/brewing_stand.rs @@ -177,6 +177,17 @@ impl BrewingStandBlockEntity { } } + let mut event = crate::plugin::api::events::inventory::brew::BrewEvent::new( + self.position, + self.fuel.load(std::sync::atomic::Ordering::Relaxed) as u8, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + // Consume ingredient let mut items = self.items.write().await; items[3].decrement(1); diff --git a/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs b/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs index bfcf0069c..97f8ea4ab 100644 --- a/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs +++ b/crates/pumpkin/src/block/entities/furnace_like_block_entity.rs @@ -411,41 +411,63 @@ macro_rules! impl_block_entity_for_cooking { base_fuel_ticks }; - self.set_lit_time_remaining(adjusted_fuel_ticks); - self.set_lit_total_time(adjusted_fuel_ticks); + let mut burn_event = $crate::plugin::api::events::inventory::furnace_burn::FurnaceBurnEvent::new( + self.position, + bottom_item.item.registry_key.to_string(), + adjusted_fuel_ticks as u32, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut burn_event).await; + } + if burn_event.cancelled { + self.set_lit_time_remaining(0); + } else { + self.set_lit_time_remaining(adjusted_fuel_ticks); + self.set_lit_total_time(adjusted_fuel_ticks); + } - if self.is_burning() { - is_dirty = true; - let mut items_guard = self.items.write().await; - if !items_guard[1].is_empty() { - items_guard[1].decrement(1); - if let Some(remainder_id) = - pumpkin_data::recipe_remainder::get_recipe_remainder_id( - items_guard[1].item.id, - ) - && items_guard[1].is_empty() - && let Some(remainder_item) = - pumpkin_data::item::Item::from_id(remainder_id) - { - items_guard[1] = ItemStack::new(1, remainder_item); - } - } - } - } + if self.is_burning() { + is_dirty = true; + let mut items_guard = self.items.write().await; + if !items_guard[1].is_empty() { + items_guard[1].decrement(1); + if let Some(remainder_id) = + pumpkin_data::recipe_remainder::get_recipe_remainder_id( + items_guard[1].item.id, + ) + && items_guard[1].is_empty() + && let Some(remainder_item) = + pumpkin_data::item::Item::from_id(remainder_id) + { + items_guard[1] = ItemStack::new(1, remainder_item); + } + } + } + } - if self.is_burning() && can_accept_output { - self.cooking_time_spent.fetch_add(1, Ordering::Relaxed); + if self.is_burning() && can_accept_output { + self.cooking_time_spent.fetch_add(1, Ordering::Relaxed); - if self.get_cooking_time_spent() == self.get_cooking_total_time() { - self.set_cooking_time_spent(0); - if let Some(cooking_recipe) = furnace_recipe { - let cooking_total_time = cooking_recipe.cookingtime; - self.set_cooking_total_time(cooking_total_time as u16); + if self.get_cooking_time_spent() == self.get_cooking_total_time() { + self.set_cooking_time_spent(0); + if let Some(cooking_recipe) = furnace_recipe { + let cooking_total_time = cooking_recipe.cookingtime; + self.set_cooking_total_time(cooking_total_time as u16); - self.craft_recipe(Some(cooking_recipe)).await; - is_dirty = true; - } - } + let mut smelt_event = $crate::plugin::api::events::inventory::furnace_smelt::FurnaceSmeltEvent::new( + self.position, + top_item.item.registry_key.to_string(), + cooking_recipe.result.id.to_string(), + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut smelt_event).await; + } + if !smelt_event.cancelled { + self.craft_recipe(Some(cooking_recipe)).await; + is_dirty = true; + } + } + } } else { self.set_cooking_time_spent(0); } diff --git a/crates/pumpkin/src/block/fluid/flowing_trait.rs b/crates/pumpkin/src/block/fluid/flowing_trait.rs index 1d20eb898..3daf6bc22 100644 --- a/crates/pumpkin/src/block/fluid/flowing_trait.rs +++ b/crates/pumpkin/src/block/fluid/flowing_trait.rs @@ -372,6 +372,18 @@ pub trait FlowingFluid: Send + Sync { } } + let mut event = crate::plugin::api::events::block::block_from_to::BlockFromToEvent::new( + *pos, + *pos, + &pumpkin_data::Block::WATER, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + world .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL) .await; diff --git a/crates/pumpkin/src/entity/ai/goal/bow_attack.rs b/crates/pumpkin/src/entity/ai/goal/bow_attack.rs index 6ef33875c..13a3e2e7b 100644 --- a/crates/pumpkin/src/entity/ai/goal/bow_attack.rs +++ b/crates/pumpkin/src/entity/ai/goal/bow_attack.rs @@ -70,6 +70,19 @@ impl BowAttackGoal { let entity = mob.get_entity(); let world = entity.world.load(); + let mut event = + crate::plugin::api::events::entity::entity_shoot_bow::EntityShootBowEvent::new( + entity.entity_id, + "minecraft:bow".to_string(), + 1.0, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + let arrow_entity = Entity::new(world.clone(), entity.pos.load(), &EntityType::ARROW); let projectile = ItemStack::new(1, &Item::ARROW); let arrow = ArrowEntity::new_shot(arrow_entity, entity, &projectile, ArrowPickup::Allowed); diff --git a/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs b/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs index d685cca6d..e79f36fa9 100644 --- a/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs +++ b/crates/pumpkin/src/entity/ai/goal/pick_up_block.rs @@ -83,6 +83,18 @@ impl Goal for PickUpBlockGoal { let default_state_id = block.default_state.id; + let mut event = crate::plugin::api::events::entity::entity_change_block::EntityChangeBlockEvent::new( + entity.entity_id, + target_pos, + "minecraft:air".to_string(), + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + // TODO: Emit game event (BLOCK_DESTROY) world .set_block_state(&target_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL) diff --git a/crates/pumpkin/src/entity/item.rs b/crates/pumpkin/src/entity/item.rs index ba5f8e28d..04786b234 100644 --- a/crates/pumpkin/src/entity/item.rs +++ b/crates/pumpkin/src/entity/item.rs @@ -345,8 +345,20 @@ impl ItemEntity { let age = self.item_age.fetch_add(1, Ordering::Relaxed) + 1; if age >= 6000 { - entity.remove().await; - return false; + let mut despawn_event = + crate::plugin::api::events::entity::item_despawn::ItemDespawnEvent::new( + entity.entity_id, + ); + if let Some(server) = entity.world.load().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut despawn_event) + .await; + } + if !despawn_event.cancelled { + entity.remove().await; + return false; + } } let n = if entity diff --git a/crates/pumpkin/src/entity/living.rs b/crates/pumpkin/src/entity/living.rs index 64cb087f2..0884fb279 100644 --- a/crates/pumpkin/src/entity/living.rs +++ b/crates/pumpkin/src/entity/living.rs @@ -241,8 +241,25 @@ impl LivingEntity { } } - /// Picks up and Item entity or XP Orb + /// Picks up an Item entity or XP Orb pub fn pickup(&self, item: &Entity, stack_amount: u32) { + let mut pickup_event = + crate::plugin::api::events::entity::entity_pickup_item::EntityPickupItemEvent::new( + self.entity.entity_id, + item.entity_type.id.to_string(), + stack_amount as u8, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + server.plugin_manager.fire(&server, &mut pickup_event).await; + }); + }); + if pickup_event.cancelled { + return; + } + } + let chunk_pos = self.entity.chunk_pos.load(); self.entity.world.load().broadcast_to_chunk_editioned_sync( chunk_pos, @@ -335,6 +352,21 @@ impl LivingEntity { pub fn heal(&self, additional_health: f32) { assert!(additional_health > 0.0); + let mut event = + crate::plugin::api::events::entity::entity_regain_health::EntityRegainHealthEvent::new( + self.entity.entity_id, + additional_health, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + server.plugin_manager.fire(&server, &mut event).await; + }); + }); + if event.cancelled { + return; + } + } self.set_health(self.health.load() + additional_health); } @@ -529,6 +561,20 @@ impl LivingEntity { #[expect(clippy::too_many_lines)] pub async fn add_effect(&self, effect: Effect) { + let mut effect_event = + crate::plugin::api::events::entity::entity_potion_effect::EntityPotionEffectEvent::new( + self.entity.entity_id, + effect.effect_type.translation_key.to_string(), + effect.duration, + effect.amplifier, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut effect_event).await; + } + if effect_event.cancelled { + return; + } + // Apply instant effects immediately before storing if effect.effect_type == &StatusEffect::INSTANT_HEALTH { let heal_amount = 4.0 * (1 << effect.amplifier) as f32; @@ -1800,6 +1846,20 @@ impl LivingEntity { // Clear the stack and use the totem of undying if stack.get_data_component::().is_some() { + let mut resurrect_event = + crate::plugin::api::events::entity::entity_resurrect::EntityResurrectEvent::new( + self.entity.entity_id, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut resurrect_event) + .await; + } + if resurrect_event.cancelled { + return false; + } + stack.clear(); let slot = match hand { Hand::Right => EquipmentSlot::MAIN_HAND, @@ -2197,6 +2257,20 @@ impl EntityBase for LivingEntity { return false; } + let mut damage_event = + crate::plugin::api::events::entity::entity_damage::EntityDamageEvent::new( + self.entity.entity_id, + damage_type.id.to_string(), + amount, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut damage_event).await; + } + if damage_event.cancelled { + return false; + } + amount = damage_event.damage; + let world = self.entity.world.load(); let is_fire_damage = damage_type == DamageType::IN_FIRE || damage_type == DamageType::ON_FIRE @@ -2616,6 +2690,31 @@ impl EntityBase for LivingEntity { if clamped_health <= 0.0 && (bypasses_cooldown_protection || !self.try_use_death_protector(caller).await) { + let mut death_event = + crate::plugin::api::events::entity::entity_death::EntityDeathEvent::new( + self.entity.entity_id, + 0, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut death_event).await; + } + if let Some(player) = caller.get_player() + && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) + { + let mut player_death_event = + crate::plugin::api::events::entity::entity_death::PlayerDeathEvent::new( + player_arc, + pumpkin_util::text::TextComponent::text("Died"), + 0, + ); + if let Some(server) = world.server.upgrade() { + server + .plugin_manager + .fire(&server, &mut player_death_event) + .await; + } + } + self.on_death(damage_type, source, cause).await; } diff --git a/crates/pumpkin/src/entity/mob/mod.rs b/crates/pumpkin/src/entity/mob/mod.rs index 9aec5424c..7cea4e941 100644 --- a/crates/pumpkin/src/entity/mob/mod.rs +++ b/crates/pumpkin/src/entity/mob/mod.rs @@ -572,7 +572,20 @@ pub trait Mob: EntityBase + Send + Sync { /// Set or clear the mob's target. Override to add side effects when targeting changes. fn set_mob_target(&self, target: Option>) -> EntityBaseFuture<'_, ()> { Box::pin(async move { - let mut mob_target = self.get_mob_entity().target.lock().await; + let target_id = target.as_ref().map(|t| t.get_entity().entity_id); + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_target::EntityTargetEvent::new( + mob.living_entity.entity.entity_id, + target_id, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + let mut mob_target = mob.target.lock().await; *mob_target = target; }) } @@ -585,6 +598,138 @@ pub trait Mob: EntityBase + Send + Sync { Box::pin(async move { self.get_mob_entity().mob_interact(player, item_stack).await }) } + fn tame<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_tame::EntityTameEvent::new( + mob.living_entity.entity.entity_id, + player.clone(), + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn breed(&self, father_id: i32, mother_id: i32, child_id: i32) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_breed::EntityBreedEvent::new( + father_id, mother_id, child_id, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn dye<'a>( + &'a self, + color: crate::plugin::api::events::entity::entity_dye::DyeColor, + player: Option<&'a Arc>, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_dye::EntityDyeEvent::new( + mob.living_entity.entity.entity_id, + color, + player.cloned(), + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn enter_love_mode( + &self, + human_entity_id: Option, + ticks_in_love: i32, + ) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_enter_love_mode::EntityEnterLoveModeEvent::new( + mob.living_entity.entity.entity_id, + human_entity_id, + ticks_in_love, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn transform(&self, new_entity_id: i32, transform_reason: String) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_transform::EntityTransformEvent::new( + mob.living_entity.entity.entity_id, + new_entity_id, + transform_reason, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn break_door(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_break_door::EntityBreakDoorEvent::new( + mob.living_entity.entity.entity_id, + block_pos, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn enter_block(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_enter_block::EntityEnterBlockEvent::new( + mob.living_entity.entity.entity_id, + block_pos, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn interact(&self, block_pos: BlockPos) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = + crate::plugin::api::events::entity::entity_interact::EntityInteractEvent::new( + mob.living_entity.entity.entity_id, + block_pos, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + + fn place_block(&self, block_pos: BlockPos, block_name: String) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + let mob = self.get_mob_entity(); + let mut event = crate::plugin::api::events::entity::entity_place::EntityPlaceEvent::new( + mob.living_entity.entity.entity_id, + block_pos, + block_name, + ); + if let Some(server) = mob.living_entity.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + }) + } + fn mob_player_collision<'a>(&'a self, _player: &'a Arc) -> EntityBaseFuture<'a, ()> { Box::pin(async {}) } diff --git a/crates/pumpkin/src/entity/mod.rs b/crates/pumpkin/src/entity/mod.rs index 3ce9936d9..13ef1092c 100644 --- a/crates/pumpkin/src/entity/mod.rs +++ b/crates/pumpkin/src/entity/mod.rs @@ -378,6 +378,20 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any { fn set_on_fire_for_ticks(&self, ticks: u32) { let entity = self.get_entity(); + let mut event = crate::plugin::api::events::entity::entity_combust::EntityCombustEvent::new( + entity.entity_id, + ticks as f32 / 20.0, + ); + if let Some(server) = entity.world.load().server.upgrade() { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + server.plugin_manager.fire(&server, &mut event).await; + }); + }); + if event.cancelled { + return; + } + } if entity.fire_ticks.load(Ordering::Relaxed) < ticks as i32 { entity.fire_ticks.store(ticks as i32, Ordering::Relaxed); } @@ -2418,6 +2432,18 @@ impl Entity { portal_world: Arc, pos: BlockPos, ) { + let mut portal_event = + crate::plugin::api::events::entity::entity_portal::EntityPortalEvent::new( + self.entity_id, + pos, + ); + if let Some(server) = self.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut portal_event).await; + } + if portal_event.cancelled { + return; + } + // Passengers don't teleport independently - they wait for their vehicle if self.has_vehicle().await { return; @@ -2656,10 +2682,21 @@ impl Entity { self.sneaking.load(Ordering::Relaxed) } - pub async fn set_swimming(&self, invisible: bool) { - if self.swimming.load(Ordering::Relaxed) != invisible { - self.swimming.store(invisible, Relaxed); - self.set_flag(Flag::Swimming, invisible).await; + pub async fn set_swimming(&self, swimming: bool) { + if self.swimming.load(Ordering::Relaxed) != swimming { + let mut event = + crate::plugin::api::events::entity::entity_toggle_swim::EntityToggleSwimEvent::new( + self.entity_id, + swimming, + ); + if let Some(server) = self.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + self.swimming.store(event.is_swimming, Relaxed); + self.set_flag(Flag::Swimming, event.is_swimming).await; } } @@ -2949,6 +2986,22 @@ impl Entity { } pub fn set_pose(&self, pose: EntityPose) { + let mut pose_event = + crate::plugin::api::events::entity::entity_pose_change::EntityPoseChangeEvent::new( + self.entity_id, + (pose as u8).to_string(), + ); + if let Some(server) = self.world.load().server.upgrade() { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + server.plugin_manager.fire(&server, &mut pose_event).await; + }); + }); + if pose_event.cancelled { + return; + } + } + let dimension = Self::get_entity_dimensions(pose); let position = self.pos.load(); let aabb = BoundingBox::new_from_pos(position.x, position.y, position.z, &dimension); @@ -3247,6 +3300,27 @@ impl Entity { vehicle: Arc, passenger: Arc, ) { + let mut mount_event = + crate::plugin::api::events::entity::entity_mount::EntityMountEvent::new( + passenger.get_entity().entity_id, + self.entity_id, + ); + let mut vehicle_enter = + crate::plugin::api::events::vehicle::vehicle_enter::VehicleEnterEvent::new( + self.entity_id, + passenger.get_entity().entity_id, + ); + if let Some(server) = self.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut mount_event).await; + server + .plugin_manager + .fire(&server, &mut vehicle_enter) + .await; + } + if mount_event.cancelled || vehicle_enter.cancelled { + return; + } + let passenger_entity = passenger.get_entity(); *passenger_entity.vehicle.lock().await = Some(vehicle); @@ -3290,6 +3364,27 @@ impl Entity { #[allow(clippy::too_many_lines)] pub async fn remove_passenger(&self, passenger_id: i32) { + let mut dismount_event = + crate::plugin::api::events::entity::entity_dismount::EntityDismountEvent::new( + passenger_id, + self.entity_id, + ); + let mut vehicle_exit = + crate::plugin::api::events::vehicle::vehicle_exit::VehicleExitEvent::new( + self.entity_id, + passenger_id, + ); + if let Some(server) = self.world.load().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut dismount_event) + .await; + server.plugin_manager.fire(&server, &mut vehicle_exit).await; + } + if dismount_event.cancelled || vehicle_exit.cancelled { + return; + } + let mut passengers = self.passengers.lock().await; let removed_passenger = if let Some(idx) = passengers .iter() diff --git a/crates/pumpkin/src/entity/passive/chicken.rs b/crates/pumpkin/src/entity/passive/chicken.rs index 1479bc5d1..152edabe3 100644 --- a/crates/pumpkin/src/entity/passive/chicken.rs +++ b/crates/pumpkin/src/entity/passive/chicken.rs @@ -194,7 +194,18 @@ impl Mob for ChickenEntity { let next_time = rand::rng().random_range(6000..12000); let world = entity.world.load_full(); let pos = entity.block_pos.load(); - world.drop_stack(&pos, ItemStack::new(1, &Item::EGG)).await; + let mut drop_event = + crate::plugin::api::events::entity::entity_drop_item::EntityDropItemEvent::new( + entity.entity_id, + "minecraft:egg".to_string(), + 1, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut drop_event).await; + } + if !drop_event.cancelled { + world.drop_stack(&pos, ItemStack::new(1, &Item::EGG)).await; + } self.egg_lay_time.store(next_time, Ordering::Relaxed); } }) diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index fbac4612f..78980b356 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, AtomicI8, AtomicI32, AtomicU8, AtomicU32, Or use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; +use crate::plugin::api::events::enchantment::{EnchantItemEvent, PrepareItemEnchantEvent}; use crate::world::scoreboard::{BedrockScoreboard, Scoreboard}; use advancement::PlayerAdvancement; use arc_swap::ArcSwap; @@ -3416,7 +3417,22 @@ impl Player { if self.abilities.lock().await.invulnerable { return; } - self.hunger_manager.add_exhaustion(exhaustion); + let mut exhaustion_event = + crate::plugin::api::events::entity::entity_exhaustion::EntityExhaustionEvent::new( + self.entity_id(), + exhaustion, + ); + if let Some(server) = self.world().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut exhaustion_event) + .await; + } + if exhaustion_event.cancelled { + return; + } + self.hunger_manager + .add_exhaustion(exhaustion_event.exhaustion); } pub async fn heal(&self, additional_health: f32) { @@ -3563,7 +3579,18 @@ impl Player { } pub async fn set_food_level(&self, food_level: u8) { - self.hunger_manager.set_level(food_level); + let mut food_event = + crate::plugin::api::events::entity::food_level_change::FoodLevelChangeEvent::new( + self.living_entity.entity.entity_id, + food_level, + ); + if let Some(server) = self.world().server.upgrade() { + server.plugin_manager.fire(&server, &mut food_event).await; + } + if food_event.cancelled { + return; + } + self.hunger_manager.set_level(food_event.food_level); self.send_health().await; } @@ -4535,6 +4562,20 @@ impl Player { pub async fn on_rename_item(self: &Arc, packet: SRenameItem<'_>) { self.update_last_action_time(); + + let mut prepare_event = + crate::plugin::api::events::inventory::prepare_anvil::PrepareAnvilEvent::new( + self.clone(), + packet.item_name.to_string(), + 1, + ); + if let Some(server) = self.world().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut prepare_event) + .await; + } + let screen_handler_arc = self.current_screen_handler.lock().await.clone(); let mut screen_handler = screen_handler_arc.lock().await; @@ -4809,7 +4850,7 @@ impl Player { click_type, slot, raw_slot, - clicked_item, + clicked_item.clone(), cursor_item, i32::from(hotbar_button), ); @@ -4820,6 +4861,38 @@ impl Player { } }} + if slot == 0 + && let Some(ref stack) = clicked_item + && !stack.is_empty() + { + let mut craft_event = + crate::plugin::api::events::inventory::craft_item::CraftItemEvent::new( + self.clone(), + stack.item.registry_key.to_string(), + ); + if let Some(server) = self.world().server.upgrade() { + server.plugin_manager.fire(&server, &mut craft_event).await; + } + if craft_event.cancelled { + screen_handler.cancel().await; + return; + } + } + + if packet.mode == SlotActionType::QuickCraft { + let mut drag_event = + crate::plugin::api::events::inventory::inventory_drag::InventoryDragEvent::new( + self.clone(), + ); + if let Some(server) = self.world().server.upgrade() { + server.plugin_manager.fire(&server, &mut drag_event).await; + } + if drag_event.cancelled { + screen_handler.cancel().await; + return; + } + } + // Enforce flags let is_container_slot = slot >= 0 && i32::from(slot) < container_slots as i32; @@ -6271,6 +6344,70 @@ impl InventoryPlayer for Player { self.increment_stat(category, stat_id, amount).await; }) } + + fn fire_prepare_item_enchant_event<'a>( + &'a self, + item: &'a ItemStack, + level_requirements: &'a mut [i32; 3], + enchantment_id: &'a mut [i32; 3], + enchantment_level: &'a mut [i32; 3], + bookshelf_count: i32, + ) -> PlayerFuture<'a, bool> { + Box::pin(async move { + let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) else { + return false; + }; + let Some(server) = self.world().server.upgrade() else { + return false; + }; + let mut event = PrepareItemEnchantEvent::new( + player_arc, + item.clone(), + *level_requirements, + *enchantment_id, + *enchantment_level, + bookshelf_count, + ); + server.plugin_manager.fire(&server, &mut event).await; + if event.cancelled { + return true; + } + *level_requirements = event.level_requirements; + *enchantment_id = event.enchantment_id; + *enchantment_level = event.enchantment_level; + false + }) + } + + fn fire_enchant_item_event<'a>( + &'a self, + item: &'a ItemStack, + option: i32, + exp_level_cost: i32, + enchantments_to_add: &'a mut Vec<(&'static pumpkin_data::Enchantment, i32)>, + ) -> PlayerFuture<'a, bool> { + Box::pin(async move { + let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) else { + return false; + }; + let Some(server) = self.world().server.upgrade() else { + return false; + }; + let mut event = EnchantItemEvent::new( + player_arc, + item.clone(), + option, + exp_level_cost, + enchantments_to_add.clone(), + ); + server.plugin_manager.fire(&server, &mut event).await; + if event.cancelled { + return true; + } + *enchantments_to_add = event.enchantments_to_add; + false + }) + } } #[cfg(test)] diff --git a/crates/pumpkin/src/entity/projectile/arrow.rs b/crates/pumpkin/src/entity/projectile/arrow.rs index a463a773d..5aefb7c43 100644 --- a/crates/pumpkin/src/entity/projectile/arrow.rs +++ b/crates/pumpkin/src/entity/projectile/arrow.rs @@ -116,7 +116,18 @@ impl ArrowEntity { let mut owner_pos = shooter.pos.load(); owner_pos.y = owner_pos.y + f64::from(shooter.entity_dimension.load().eye_height) - 0.1; entity.pos.store(owner_pos); - entity.set_velocity(Vector3::new(0.0, 0.1, 0.0)); + let mut launch_event = + crate::plugin::api::events::entity::projectile_launch::ProjectileLaunchEvent::new( + entity.entity_id, + Some(shooter.entity_id), + ); + if let Some(server) = entity.world.load().server.upgrade() { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + server.plugin_manager.fire(&server, &mut launch_event).await; + }); + }); + } Self { entity, @@ -443,6 +454,27 @@ impl EntityBase for ArrowEntity { #[allow(clippy::too_many_lines)] fn on_hit(&self, hit: ProjectileHit) -> EntityBaseFuture<'_, ()> { Box::pin(async move { + let (hit_pos, hit_entity) = match hit { + ProjectileHit::Block { hit_pos, .. } => (hit_pos, None), + ProjectileHit::Entity { + ref entity, + hit_pos, + .. + } => (hit_pos, Some(entity.get_entity().entity_id)), + }; + let mut hit_event = + crate::plugin::api::events::entity::projectile_hit::ProjectileHitEvent::new( + self.entity.entity_id, + hit_pos, + hit_entity, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut hit_event).await; + } + if hit_event.cancelled { + return; + } + let entity = self.get_entity(); let world = entity.world.load(); diff --git a/crates/pumpkin/src/entity/vehicle/vehicle.rs b/crates/pumpkin/src/entity/vehicle/vehicle.rs index 09c4ff93e..ad0c91c38 100644 --- a/crates/pumpkin/src/entity/vehicle/vehicle.rs +++ b/crates/pumpkin/src/entity/vehicle/vehicle.rs @@ -38,6 +38,70 @@ impl VehicleEntity { if current_damage > 0.0 { self.damage.store(current_damage - 1.0); } + + let mut update_event = + crate::plugin::api::events::vehicle::vehicle_update::VehicleUpdateEvent::new( + self.entity.entity_id, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + server.plugin_manager.fire(&server, &mut update_event).await; + }); + }); + } + } + + pub async fn create(&self) { + let mut create_event = + crate::plugin::api::events::vehicle::vehicle_create::VehicleCreateEvent::new( + self.entity.entity_id, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut create_event).await; + } + } + + pub async fn move_vehicle( + &self, + from: pumpkin_util::math::vector3::Vector3, + to: pumpkin_util::math::vector3::Vector3, + ) { + let mut move_event = + crate::plugin::api::events::vehicle::vehicle_move::VehicleMoveEvent::new( + self.entity.entity_id, + from, + to, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut move_event).await; + } + } + + pub async fn collide_entity(&self, collided_entity_id: i32) { + let mut collide_event = crate::plugin::api::events::vehicle::vehicle_entity_collision::VehicleEntityCollisionEvent::new( + self.entity.entity_id, + collided_entity_id, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut collide_event) + .await; + } + } + + pub async fn collide_block(&self, block_pos: pumpkin_util::math::position::BlockPos) { + let mut collide_event = crate::plugin::api::events::vehicle::vehicle_block_collision::VehicleBlockCollisionEvent::new( + self.entity.entity_id, + block_pos, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut collide_event) + .await; + } } pub fn set_damage(&self, damage: f32) { @@ -134,6 +198,20 @@ impl VehicleEntity { return true; } + let attacker_id = source.map(|s| s.get_entity().entity_id); + let mut damage_event = + crate::plugin::api::events::vehicle::vehicle_damage::VehicleDamageEvent::new( + self.entity.entity_id, + amount, + attacker_id, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server.plugin_manager.fire(&server, &mut damage_event).await; + } + if damage_event.cancelled { + return false; + } + let new_strength = self.apply_damage_wobble(amount); let is_creative = source @@ -141,6 +219,21 @@ impl VehicleEntity { .is_some_and(|p| p.gamemode.load() == GameMode::Creative); if is_creative || new_strength > 40.0 { + let mut destroy_event = + crate::plugin::api::events::vehicle::vehicle_destroy::VehicleDestroyEvent::new( + self.entity.entity_id, + attacker_id, + ); + if let Some(server) = self.entity.world.load().server.upgrade() { + server + .plugin_manager + .fire(&server, &mut destroy_event) + .await; + } + if destroy_event.cancelled { + return false; + } + if is_creative { self.entity.remove().await; } else { diff --git a/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs b/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs index df553ccc2..8d4cb4b73 100644 --- a/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs +++ b/crates/pumpkin/src/item/items/ignite/flint_and_steel.rs @@ -15,6 +15,8 @@ use std::sync::Arc; use crate::item::items::ignite::ignition::Ignition; +use crate::plugin::api::events::world::portal_create::{PortalCreateEvent, PortalType}; + pub struct FlintAndSteelItem; impl ItemMetadata for FlintAndSteelItem { @@ -49,7 +51,12 @@ impl ItemBehaviour for FlintAndSteelItem { .plugin_manager .fire(&server_ref, &mut event) .await; - if event.cancelled { + let mut portal_event = PortalCreateEvent::new(location, PortalType::Nether); + server_ref + .plugin_manager + .fire(&server_ref, &mut portal_event) + .await; + if event.cancelled || portal_event.cancelled { return; } } diff --git a/crates/pumpkin/src/net/java/play/player_command.rs b/crates/pumpkin/src/net/java/play/player_command.rs index 0adc87f66..85d1d0a6d 100644 --- a/crates/pumpkin/src/net/java/play/player_command.rs +++ b/crates/pumpkin/src/net/java/play/player_command.rs @@ -48,7 +48,14 @@ impl JavaClient { Action::StartFlyingElytra => { let fall_flying = entity.check_fall_flying(); if entity.is_fall_flying() != fall_flying { - entity.set_fall_flying(fall_flying).await; + let mut event = crate::plugin::api::events::entity::entity_toggle_glide::EntityToggleGlideEvent::new( + entity.entity_id, + fall_flying, + ); + server.plugin_manager.fire(server, &mut event).await; + if !event.cancelled { + entity.set_fall_flying(event.is_gliding).await; + } } } // <= 1.21.5 diff --git a/crates/pumpkin/src/net/java/play/select_trade.rs b/crates/pumpkin/src/net/java/play/select_trade.rs index 798ca40d7..fb44d1584 100644 --- a/crates/pumpkin/src/net/java/play/select_trade.rs +++ b/crates/pumpkin/src/net/java/play/select_trade.rs @@ -3,6 +3,17 @@ use super::*; impl JavaClient { pub async fn handle_select_trade(&self, player: &Arc, packet: SSelectTrade) { + let mut event = crate::plugin::api::events::inventory::trade_select::TradeSelectEvent::new( + player.clone(), + packet.selected_slot.0 as u8, + ); + if let Some(server) = player.world().server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + let screen_handler = player.current_screen_handler.lock().await; let mut screen_handler = screen_handler.lock().await; if let Some(merchant) = screen_handler diff --git a/crates/pumpkin/src/net/java/play/update_sign.rs b/crates/pumpkin/src/net/java/play/update_sign.rs index c096b7317..7045eb28d 100644 --- a/crates/pumpkin/src/net/java/play/update_sign.rs +++ b/crates/pumpkin/src/net/java/play/update_sign.rs @@ -14,6 +14,27 @@ impl JavaClient { return; } + let lines = vec![ + sign_data.line_1.to_string(), + sign_data.line_2.to_string(), + sign_data.line_3.to_string(), + sign_data.line_4.to_string(), + ]; + + if let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) { + let mut event = crate::plugin::api::events::block::sign_change::SignChangeEvent::new( + player_arc, + sign_data.location, + lines.clone(), + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + } + let text = if sign_data.is_front_text { &sign_entity.front_text } else { diff --git a/crates/pumpkin/src/net/java/play/use_item.rs b/crates/pumpkin/src/net/java/play/use_item.rs index 8164b60f2..5a5b424c3 100644 --- a/crates/pumpkin/src/net/java/play/use_item.rs +++ b/crates/pumpkin/src/net/java/play/use_item.rs @@ -22,6 +22,16 @@ impl JavaClient { let mut item_in_hand = inventory.get_stack_in_hand(hand).await; + let mut consume_event = + crate::plugin::api::events::player::player_item_consume::PlayerItemConsumeEvent::new( + player.clone(), + item_in_hand.item.registry_key.to_string(), + ); + server.plugin_manager.fire(server, &mut consume_event).await; + if consume_event.cancelled { + return; + } + let (item_id, _item) = (item_in_hand.item.id, item_in_hand.item); player .increment_stat(StatisticCategory::Used, item_id as i32, 1) diff --git a/crates/pumpkin/src/plugin/api/events/enchantment/enchant_item.rs b/crates/pumpkin/src/plugin/api/events/enchantment/enchant_item.rs new file mode 100644 index 000000000..5ebdf128d --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/enchantment/enchant_item.rs @@ -0,0 +1,46 @@ +use pumpkin_data::Enchantment; +use pumpkin_data::item_stack::ItemStack; +use pumpkin_macros::{Event, cancellable}; +use std::sync::Arc; + +use crate::entity::player::Player; + +/// An event triggered when an item is enchanted at an enchanting table. +#[cancellable] +#[derive(Event, Clone)] +pub struct EnchantItemEvent { + /// The player enchanting the item. + pub player: Arc, + + /// The item being enchanted. + pub item: ItemStack, + + /// The button index selected (0, 1, or 2). + pub option: i32, + + /// The cost in experience levels for the enchantment. + pub exp_level_cost: i32, + + /// The list of enchantments and levels to apply. + pub enchantments_to_add: Vec<(&'static Enchantment, i32)>, +} + +impl EnchantItemEvent { + #[must_use] + pub const fn new( + player: Arc, + item: ItemStack, + option: i32, + exp_level_cost: i32, + enchantments_to_add: Vec<(&'static Enchantment, i32)>, + ) -> Self { + Self { + player, + item, + option, + exp_level_cost, + enchantments_to_add, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/enchantment/mod.rs b/crates/pumpkin/src/plugin/api/events/enchantment/mod.rs new file mode 100644 index 000000000..df423ee7f --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/enchantment/mod.rs @@ -0,0 +1,5 @@ +pub mod enchant_item; +pub mod prepare_item_enchant; + +pub use enchant_item::EnchantItemEvent; +pub use prepare_item_enchant::PrepareItemEnchantEvent; diff --git a/crates/pumpkin/src/plugin/api/events/enchantment/prepare_item_enchant.rs b/crates/pumpkin/src/plugin/api/events/enchantment/prepare_item_enchant.rs new file mode 100644 index 000000000..d53af1f40 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/enchantment/prepare_item_enchant.rs @@ -0,0 +1,50 @@ +use pumpkin_data::item_stack::ItemStack; +use pumpkin_macros::{Event, cancellable}; +use std::sync::Arc; + +use crate::entity::player::Player; + +/// An event triggered when an item is prepared for enchanting in an enchanting table. +#[cancellable] +#[derive(Event, Clone)] +pub struct PrepareItemEnchantEvent { + /// The player preparing the enchantment. + pub player: Arc, + + /// The item being enchanted. + pub item: ItemStack, + + /// The required level costs for each of the 3 slots. + pub level_requirements: [i32; 3], + + /// The enchantment clue ID for each of the 3 slots (-1 if none). + pub enchantment_id: [i32; 3], + + /// The enchantment clue level for each of the 3 slots (-1 if none). + pub enchantment_level: [i32; 3], + + /// The bookshelf count surrounding the enchanting table. + pub bookshelf_count: i32, +} + +impl PrepareItemEnchantEvent { + #[must_use] + pub const fn new( + player: Arc, + item: ItemStack, + level_requirements: [i32; 3], + enchantment_id: [i32; 3], + enchantment_level: [i32; 3], + bookshelf_count: i32, + ) -> Self { + Self { + player, + item, + level_requirements, + enchantment_id, + enchantment_level, + bookshelf_count, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_break_door.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_break_door.rs new file mode 100644 index 000000000..1707a93b5 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_break_door.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when an entity breaks a door. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityBreakDoorEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Position of the door block. + pub block_pos: BlockPos, +} + +impl EntityBreakDoorEvent { + #[must_use] + pub const fn new(entity_id: i32, block_pos: BlockPos) -> Self { + Self { + entity_id, + block_pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_change_block.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_change_block.rs new file mode 100644 index 000000000..55d854992 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_change_block.rs @@ -0,0 +1,26 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when an entity changes a block in the world. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityChangeBlockEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Position of the block. + pub block_pos: BlockPos, + /// The new block state identifier. + pub new_block: String, +} + +impl EntityChangeBlockEvent { + #[must_use] + pub const fn new(entity_id: i32, block_pos: BlockPos, new_block: String) -> Self { + Self { + entity_id, + block_pos, + new_block, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_dismount.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_dismount.rs new file mode 100644 index 000000000..e3c4ab9c0 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_dismount.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity dismounts another entity. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityDismountEvent { + /// The ID of the dismounting entity. + pub entity_id: i32, + + /// The ID of the vehicle entity being dismounted. + pub dismounted_id: i32, +} + +impl EntityDismountEvent { + #[must_use] + pub const fn new(entity_id: i32, dismounted_id: i32) -> Self { + Self { + entity_id, + dismounted_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_drop_item.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_drop_item.rs new file mode 100644 index 000000000..1e1ba54f0 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_drop_item.rs @@ -0,0 +1,25 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity drops an item. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityDropItemEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Registry name of the item. + pub item_name: String, + /// Amount dropped. + pub count: u8, +} + +impl EntityDropItemEvent { + #[must_use] + pub const fn new(entity_id: i32, item_name: String, count: u8) -> Self { + Self { + entity_id, + item_name, + count, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_enter_block.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_enter_block.rs new file mode 100644 index 000000000..b1419a17d --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_enter_block.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when an entity enters a block. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityEnterBlockEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Position of the block entered. + pub block_pos: BlockPos, +} + +impl EntityEnterBlockEvent { + #[must_use] + pub const fn new(entity_id: i32, block_pos: BlockPos) -> Self { + Self { + entity_id, + block_pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_enter_love_mode.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_enter_love_mode.rs new file mode 100644 index 000000000..1a9ed285e --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_enter_love_mode.rs @@ -0,0 +1,27 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an animal enters love mode for breeding. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityEnterLoveModeEvent { + /// The ID of the animal entering love mode. + pub entity_id: i32, + + /// The ID of the human player that fed the animal, if any. + pub human_entity_id: Option, + + /// The duration of love mode in ticks. + pub ticks_in_love: i32, +} + +impl EntityEnterLoveModeEvent { + #[must_use] + pub const fn new(entity_id: i32, human_entity_id: Option, ticks_in_love: i32) -> Self { + Self { + entity_id, + human_entity_id, + ticks_in_love, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_exhaustion.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_exhaustion.rs new file mode 100644 index 000000000..6a2fc63bd --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_exhaustion.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity experiences hunger exhaustion. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityExhaustionEvent { + /// The ID of the entity. + pub entity_id: i32, + /// The amount of exhaustion added. + pub exhaustion: f32, +} + +impl EntityExhaustionEvent { + #[must_use] + pub const fn new(entity_id: i32, exhaustion: f32) -> Self { + Self { + entity_id, + exhaustion, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_interact.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_interact.rs new file mode 100644 index 000000000..f050f843b --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_interact.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when an entity interacts with a block. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityInteractEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Position of the interacted block. + pub block_pos: BlockPos, +} + +impl EntityInteractEvent { + #[must_use] + pub const fn new(entity_id: i32, block_pos: BlockPos) -> Self { + Self { + entity_id, + block_pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_pickup_item.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_pickup_item.rs new file mode 100644 index 000000000..b61bea74c --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_pickup_item.rs @@ -0,0 +1,27 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity picks up an item stack. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityPickupItemEvent { + /// The ID of the picking entity. + pub entity_id: i32, + + /// The registry name of the item. + pub item_name: String, + + /// The count of items picked up. + pub count: u8, +} + +impl EntityPickupItemEvent { + #[must_use] + pub const fn new(entity_id: i32, item_name: String, count: u8) -> Self { + Self { + entity_id, + item_name, + count, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_place.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_place.rs new file mode 100644 index 000000000..a87cc8051 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_place.rs @@ -0,0 +1,26 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when an entity places a block. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityPlaceEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Position of the placed block. + pub block_pos: BlockPos, + /// The placed block state identifier. + pub block_name: String, +} + +impl EntityPlaceEvent { + #[must_use] + pub const fn new(entity_id: i32, block_pos: BlockPos, block_name: String) -> Self { + Self { + entity_id, + block_pos, + block_name, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_portal.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_portal.rs new file mode 100644 index 000000000..81ddc8cec --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_portal.rs @@ -0,0 +1,24 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when an entity enters a portal to travel between dimensions. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityPortalEvent { + /// The ID of the entity entering the portal. + pub entity_id: i32, + + /// The position of the portal block. + pub portal_pos: BlockPos, +} + +impl EntityPortalEvent { + #[must_use] + pub const fn new(entity_id: i32, portal_pos: BlockPos) -> Self { + Self { + entity_id, + portal_pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_pose_change.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_pose_change.rs new file mode 100644 index 000000000..9e267fd87 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_pose_change.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity's pose changes. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityPoseChangeEvent { + /// The ID of the entity. + pub entity_id: i32, + /// The new pose name. + pub pose: String, +} + +impl EntityPoseChangeEvent { + #[must_use] + pub const fn new(entity_id: i32, pose: String) -> Self { + Self { + entity_id, + pose, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_potion_effect.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_potion_effect.rs new file mode 100644 index 000000000..a5620184b --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_potion_effect.rs @@ -0,0 +1,28 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a potion status effect is applied to an entity. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityPotionEffectEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Effect name. + pub effect_name: String, + /// Duration in ticks. + pub duration: i32, + /// Amplifier. + pub amplifier: u8, +} + +impl EntityPotionEffectEvent { + #[must_use] + pub const fn new(entity_id: i32, effect_name: String, duration: i32, amplifier: u8) -> Self { + Self { + entity_id, + effect_name, + duration, + amplifier, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_resurrect.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_resurrect.rs new file mode 100644 index 000000000..8bb84eab9 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_resurrect.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity is saved from death by Totem of Undying. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityResurrectEvent { + /// The ID of the resurrected entity. + pub entity_id: i32, +} + +impl EntityResurrectEvent { + #[must_use] + pub const fn new(entity_id: i32) -> Self { + Self { + entity_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_shoot_bow.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_shoot_bow.rs new file mode 100644 index 000000000..66b950a6b --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_shoot_bow.rs @@ -0,0 +1,27 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity shoots a bow or crossbow. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityShootBowEvent { + /// The ID of the shooting entity. + pub entity_id: i32, + + /// The registry name of the weapon item. + pub weapon_name: String, + + /// The shot force/velocity factor. + pub force: f32, +} + +impl EntityShootBowEvent { + #[must_use] + pub const fn new(entity_id: i32, weapon_name: String, force: f32) -> Self { + Self { + entity_id, + weapon_name, + force, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_toggle_glide.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_toggle_glide.rs new file mode 100644 index 000000000..32d76308a --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_toggle_glide.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity starts or stops gliding with an elytra. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityToggleGlideEvent { + /// The ID of the entity. + pub entity_id: i32, + + /// Whether the entity is now gliding. + pub is_gliding: bool, +} + +impl EntityToggleGlideEvent { + #[must_use] + pub const fn new(entity_id: i32, is_gliding: bool) -> Self { + Self { + entity_id, + is_gliding, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_toggle_swim.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_toggle_swim.rs new file mode 100644 index 000000000..04f35326b --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_toggle_swim.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity starts or stops swimming. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityToggleSwimEvent { + /// The ID of the entity. + pub entity_id: i32, + /// Whether the entity is swimming. + pub is_swimming: bool, +} + +impl EntityToggleSwimEvent { + #[must_use] + pub const fn new(entity_id: i32, is_swimming: bool) -> Self { + Self { + entity_id, + is_swimming, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/entity_transform.rs b/crates/pumpkin/src/plugin/api/events/entity/entity_transform.rs new file mode 100644 index 000000000..705c25389 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/entity_transform.rs @@ -0,0 +1,27 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity transforms into another entity. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntityTransformEvent { + /// The ID of the original entity. + pub entity_id: i32, + + /// The ID of the new transformed entity. + pub new_entity_id: i32, + + /// The reason for transformation. + pub transform_reason: String, +} + +impl EntityTransformEvent { + #[must_use] + pub const fn new(entity_id: i32, new_entity_id: i32, transform_reason: String) -> Self { + Self { + entity_id, + new_entity_id, + transform_reason, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/explosion_prime.rs b/crates/pumpkin/src/plugin/api/events/entity/explosion_prime.rs new file mode 100644 index 000000000..a31022607 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/explosion_prime.rs @@ -0,0 +1,25 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity is primed to explode. +#[cancellable] +#[derive(Event, Clone)] +pub struct ExplosionPrimeEvent { + /// The ID of the entity. + pub entity_id: i32, + /// The explosion radius. + pub radius: f32, + /// Whether it creates fire. + pub fire: bool, +} + +impl ExplosionPrimeEvent { + #[must_use] + pub const fn new(entity_id: i32, radius: f32, fire: bool) -> Self { + Self { + entity_id, + radius, + fire, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/food_level_change.rs b/crates/pumpkin/src/plugin/api/events/entity/food_level_change.rs new file mode 100644 index 000000000..a17b34551 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/food_level_change.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity's food level changes. +#[cancellable] +#[derive(Event, Clone)] +pub struct FoodLevelChangeEvent { + /// Entity ID. + pub entity_id: i32, + /// The new food level. + pub food_level: u8, +} + +impl FoodLevelChangeEvent { + #[must_use] + pub const fn new(entity_id: i32, food_level: u8) -> Self { + Self { + entity_id, + food_level, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/item_despawn.rs b/crates/pumpkin/src/plugin/api/events/entity/item_despawn.rs new file mode 100644 index 000000000..d3bbcb8fa --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/item_despawn.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an item entity despawns after aging. +#[cancellable] +#[derive(Event, Clone)] +pub struct ItemDespawnEvent { + /// The ID of the item entity. + pub entity_id: i32, +} + +impl ItemDespawnEvent { + #[must_use] + pub const fn new(entity_id: i32) -> Self { + Self { + entity_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/item_spawn.rs b/crates/pumpkin/src/plugin/api/events/entity/item_spawn.rs new file mode 100644 index 000000000..a652443fb --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/item_spawn.rs @@ -0,0 +1,26 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::vector3::Vector3; + +/// An event that occurs when an item entity spawns in the world. +#[cancellable] +#[derive(Event, Clone)] +pub struct ItemSpawnEvent { + /// The ID of the item entity. + pub entity_id: i32, + /// Position of the item entity. + pub position: Vector3, + /// Item registry name. + pub item_name: String, +} + +impl ItemSpawnEvent { + #[must_use] + pub const fn new(entity_id: i32, position: Vector3, item_name: String) -> Self { + Self { + entity_id, + position, + item_name, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/mod.rs b/crates/pumpkin/src/plugin/api/events/entity/mod.rs index 096553b4a..60906add3 100644 --- a/crates/pumpkin/src/plugin/api/events/entity/mod.rs +++ b/crates/pumpkin/src/plugin/api/events/entity/mod.rs @@ -1,27 +1,75 @@ pub mod entity_air_change; +pub mod entity_break_door; pub mod entity_breed; +pub mod entity_change_block; pub mod entity_combust; pub mod entity_damage; pub mod entity_death; +pub mod entity_dismount; +pub mod entity_drop_item; pub mod entity_dye; +pub mod entity_enter_block; +pub mod entity_enter_love_mode; +pub mod entity_exhaustion; pub mod entity_explode; +pub mod entity_interact; pub mod entity_mount; +pub mod entity_pickup_item; +pub mod entity_place; +pub mod entity_portal; +pub mod entity_pose_change; +pub mod entity_potion_effect; pub mod entity_regain_health; +pub mod entity_resurrect; +pub mod entity_shoot_bow; pub mod entity_spawn; pub mod entity_tame; pub mod entity_target; pub mod entity_teleport; +pub mod entity_toggle_glide; +pub mod entity_toggle_swim; +pub mod entity_transform; +pub mod explosion_prime; +pub mod food_level_change; +pub mod item_despawn; +pub mod item_spawn; +pub mod projectile_hit; +pub mod projectile_launch; pub use entity_air_change::*; +pub use entity_break_door::*; pub use entity_breed::*; +pub use entity_change_block::*; pub use entity_combust::*; pub use entity_damage::*; pub use entity_death::*; +pub use entity_dismount::*; +pub use entity_drop_item::*; pub use entity_dye::*; +pub use entity_enter_block::*; +pub use entity_enter_love_mode::*; +pub use entity_exhaustion::*; pub use entity_explode::*; +pub use entity_interact::*; pub use entity_mount::*; +pub use entity_pickup_item::*; +pub use entity_place::*; +pub use entity_portal::*; +pub use entity_pose_change::*; +pub use entity_potion_effect::*; pub use entity_regain_health::*; +pub use entity_resurrect::*; +pub use entity_shoot_bow::*; pub use entity_spawn::*; pub use entity_tame::*; pub use entity_target::*; pub use entity_teleport::*; +pub use entity_toggle_glide::*; +pub use entity_toggle_swim::*; +pub use entity_transform::*; +pub use explosion_prime::*; +pub use food_level_change::*; +pub use item_despawn::*; +pub use item_spawn::*; +pub use projectile_hit::*; +pub use projectile_launch::*; diff --git a/crates/pumpkin/src/plugin/api/events/entity/projectile_hit.rs b/crates/pumpkin/src/plugin/api/events/entity/projectile_hit.rs new file mode 100644 index 000000000..6755a1247 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/projectile_hit.rs @@ -0,0 +1,30 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::vector3::Vector3; + +/// An event that occurs when a projectile hits an entity or block. +#[cancellable] +#[derive(Event, Clone)] +pub struct ProjectileHitEvent { + /// The ID of the projectile entity. + pub entity_id: i32, + /// Hit position. + pub hit_position: Vector3, + /// ID of hit entity if applicable. + pub hit_entity_id: Option, +} + +impl ProjectileHitEvent { + #[must_use] + pub const fn new( + entity_id: i32, + hit_position: Vector3, + hit_entity_id: Option, + ) -> Self { + Self { + entity_id, + hit_position, + hit_entity_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/entity/projectile_launch.rs b/crates/pumpkin/src/plugin/api/events/entity/projectile_launch.rs new file mode 100644 index 000000000..72f95ace7 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/entity/projectile_launch.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a projectile is launched. +#[cancellable] +#[derive(Event, Clone)] +pub struct ProjectileLaunchEvent { + /// The ID of the projectile entity. + pub entity_id: i32, + /// The ID of the shooter entity if applicable. + pub shooter_id: Option, +} + +impl ProjectileLaunchEvent { + #[must_use] + pub const fn new(entity_id: i32, shooter_id: Option) -> Self { + Self { + entity_id, + shooter_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/mod.rs b/crates/pumpkin/src/plugin/api/events/mod.rs index b2add4b85..6eb211c3d 100644 --- a/crates/pumpkin/src/plugin/api/events/mod.rs +++ b/crates/pumpkin/src/plugin/api/events/mod.rs @@ -2,10 +2,13 @@ use std::any::Any; use std::sync::Arc; pub mod block; +pub mod enchantment; pub mod entity; pub mod inventory; pub mod player; +pub mod raid; pub mod server; +pub mod vehicle; pub mod world; /// A trait representing an event in the system. diff --git a/crates/pumpkin/src/plugin/api/events/raid/mod.rs b/crates/pumpkin/src/plugin/api/events/raid/mod.rs new file mode 100644 index 000000000..2f768ef2f --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/raid/mod.rs @@ -0,0 +1,9 @@ +pub mod raid_finish; +pub mod raid_spawn_wave; +pub mod raid_stop; +pub mod raid_trigger; + +pub use raid_finish::*; +pub use raid_spawn_wave::*; +pub use raid_stop::*; +pub use raid_trigger::*; diff --git a/crates/pumpkin/src/plugin/api/events/raid/raid_finish.rs b/crates/pumpkin/src/plugin/api/events/raid/raid_finish.rs new file mode 100644 index 000000000..fbecd1d3e --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/raid/raid_finish.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a raid finishes. +#[cancellable] +#[derive(Event, Clone)] +pub struct RaidFinishEvent { + /// Whether victory was achieved by players. + pub victory: bool, +} + +impl RaidFinishEvent { + #[must_use] + pub const fn new(victory: bool) -> Self { + Self { + victory, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/raid/raid_spawn_wave.rs b/crates/pumpkin/src/plugin/api/events/raid/raid_spawn_wave.rs new file mode 100644 index 000000000..13716a6f7 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/raid/raid_spawn_wave.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when a wave of a raid spawns. +#[cancellable] +#[derive(Event, Clone)] +pub struct RaidSpawnWaveEvent { + /// Wave number. + pub wave: u32, + /// Spawn position. + pub pos: BlockPos, +} + +impl RaidSpawnWaveEvent { + #[must_use] + pub const fn new(wave: u32, pos: BlockPos) -> Self { + Self { + wave, + pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/raid/raid_stop.rs b/crates/pumpkin/src/plugin/api/events/raid/raid_stop.rs new file mode 100644 index 000000000..297c0c364 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/raid/raid_stop.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a raid stops. +#[cancellable] +#[derive(Event, Clone)] +pub struct RaidStopEvent { + /// Reason for raid stopping. + pub reason: String, +} + +impl RaidStopEvent { + #[must_use] + pub const fn new(reason: String) -> Self { + Self { + reason, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/raid/raid_trigger.rs b/crates/pumpkin/src/plugin/api/events/raid/raid_trigger.rs new file mode 100644 index 000000000..93a44128b --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/raid/raid_trigger.rs @@ -0,0 +1,20 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when a raid is triggered. +#[cancellable] +#[derive(Event, Clone)] +pub struct RaidTriggerEvent { + /// Raid center block position. + pub pos: BlockPos, +} + +impl RaidTriggerEvent { + #[must_use] + pub const fn new(pos: BlockPos) -> Self { + Self { + pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/server/mod.rs b/crates/pumpkin/src/plugin/api/events/server/mod.rs index fe797ddf9..82ee82da7 100644 --- a/crates/pumpkin/src/plugin/api/events/server/mod.rs +++ b/crates/pumpkin/src/plugin/api/events/server/mod.rs @@ -1,7 +1,27 @@ pub mod list_ping; pub mod packet; +pub mod plugin_disable; +pub mod plugin_enable; +pub mod remote_server_command; pub mod server_broadcast; pub mod server_command; pub mod server_load; pub mod server_tick_end; pub mod server_tick_start; +pub mod service_register; +pub mod service_unregister; +pub mod tab_complete; + +pub use list_ping::*; +pub use packet::*; +pub use plugin_disable::*; +pub use plugin_enable::*; +pub use remote_server_command::*; +pub use server_broadcast::*; +pub use server_command::*; +pub use server_load::*; +pub use server_tick_end::*; +pub use server_tick_start::*; +pub use service_register::*; +pub use service_unregister::*; +pub use tab_complete::*; diff --git a/crates/pumpkin/src/plugin/api/events/server/plugin_disable.rs b/crates/pumpkin/src/plugin/api/events/server/plugin_disable.rs new file mode 100644 index 000000000..b92cd2bd9 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/server/plugin_disable.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a plugin is disabled. +#[cancellable] +#[derive(Event, Clone)] +pub struct PluginDisableEvent { + /// Name of the plugin being disabled. + pub plugin_name: String, +} + +impl PluginDisableEvent { + #[must_use] + pub const fn new(plugin_name: String) -> Self { + Self { + plugin_name, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/server/plugin_enable.rs b/crates/pumpkin/src/plugin/api/events/server/plugin_enable.rs new file mode 100644 index 000000000..23bc4df05 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/server/plugin_enable.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a plugin is enabled. +#[cancellable] +#[derive(Event, Clone)] +pub struct PluginEnableEvent { + /// Name of the plugin being enabled. + pub plugin_name: String, +} + +impl PluginEnableEvent { + #[must_use] + pub const fn new(plugin_name: String) -> Self { + Self { + plugin_name, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/server/remote_server_command.rs b/crates/pumpkin/src/plugin/api/events/server/remote_server_command.rs new file mode 100644 index 000000000..3114af8c8 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/server/remote_server_command.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a command is executed via remote console (RCON). +#[cancellable] +#[derive(Event, Clone)] +pub struct RemoteServerCommandEvent { + /// Command line executed. + pub command: String, +} + +impl RemoteServerCommandEvent { + #[must_use] + pub const fn new(command: String) -> Self { + Self { + command, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/server/service_register.rs b/crates/pumpkin/src/plugin/api/events/server/service_register.rs new file mode 100644 index 000000000..248eb4499 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/server/service_register.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a service is registered. +#[cancellable] +#[derive(Event, Clone)] +pub struct ServiceRegisterEvent { + /// Name of the service. + pub service_name: String, +} + +impl ServiceRegisterEvent { + #[must_use] + pub const fn new(service_name: String) -> Self { + Self { + service_name, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/server/service_unregister.rs b/crates/pumpkin/src/plugin/api/events/server/service_unregister.rs new file mode 100644 index 000000000..36d187765 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/server/service_unregister.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a service is unregistered. +#[cancellable] +#[derive(Event, Clone)] +pub struct ServiceUnregisterEvent { + /// Name of the service. + pub service_name: String, +} + +impl ServiceUnregisterEvent { + #[must_use] + pub const fn new(service_name: String) -> Self { + Self { + service_name, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/server/tab_complete.rs b/crates/pumpkin/src/plugin/api/events/server/tab_complete.rs new file mode 100644 index 000000000..ee3480c73 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/server/tab_complete.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when tab completions are requested. +#[cancellable] +#[derive(Event, Clone)] +pub struct TabCompleteEvent { + /// Buffer / text being completed. + pub buffer: String, + /// Completion suggestions. + pub completions: Vec, +} + +impl TabCompleteEvent { + #[must_use] + pub const fn new(buffer: String, completions: Vec) -> Self { + Self { + buffer, + completions, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/mod.rs b/crates/pumpkin/src/plugin/api/events/vehicle/mod.rs new file mode 100644 index 000000000..436c9734e --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/mod.rs @@ -0,0 +1,19 @@ +pub mod vehicle_block_collision; +pub mod vehicle_create; +pub mod vehicle_damage; +pub mod vehicle_destroy; +pub mod vehicle_enter; +pub mod vehicle_entity_collision; +pub mod vehicle_exit; +pub mod vehicle_move; +pub mod vehicle_update; + +pub use vehicle_block_collision::*; +pub use vehicle_create::*; +pub use vehicle_damage::*; +pub use vehicle_destroy::*; +pub use vehicle_enter::*; +pub use vehicle_entity_collision::*; +pub use vehicle_exit::*; +pub use vehicle_move::*; +pub use vehicle_update::*; diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_block_collision.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_block_collision.rs new file mode 100644 index 000000000..b2befb4c0 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_block_collision.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::position::BlockPos; + +/// An event that occurs when a vehicle collides with a block. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleBlockCollisionEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, + /// Position of the collided block. + pub block_pos: BlockPos, +} + +impl VehicleBlockCollisionEvent { + #[must_use] + pub const fn new(vehicle_id: i32, block_pos: BlockPos) -> Self { + Self { + vehicle_id, + block_pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_create.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_create.rs new file mode 100644 index 000000000..d577c9682 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_create.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a vehicle is created. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleCreateEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, +} + +impl VehicleCreateEvent { + #[must_use] + pub const fn new(vehicle_id: i32) -> Self { + Self { + vehicle_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_damage.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_damage.rs new file mode 100644 index 000000000..102f35d93 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_damage.rs @@ -0,0 +1,25 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a vehicle takes damage. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleDamageEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, + /// The damage amount. + pub damage: f32, + /// ID of attacker if applicable. + pub attacker_id: Option, +} + +impl VehicleDamageEvent { + #[must_use] + pub const fn new(vehicle_id: i32, damage: f32, attacker_id: Option) -> Self { + Self { + vehicle_id, + damage, + attacker_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_destroy.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_destroy.rs new file mode 100644 index 000000000..6f28ab0f6 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_destroy.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a vehicle is destroyed. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleDestroyEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, + /// ID of attacker if applicable. + pub attacker_id: Option, +} + +impl VehicleDestroyEvent { + #[must_use] + pub const fn new(vehicle_id: i32, attacker_id: Option) -> Self { + Self { + vehicle_id, + attacker_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_enter.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_enter.rs new file mode 100644 index 000000000..897b84fe6 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_enter.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity enters a vehicle. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleEnterEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, + /// The ID of the entering entity. + pub entered_id: i32, +} + +impl VehicleEnterEvent { + #[must_use] + pub const fn new(vehicle_id: i32, entered_id: i32) -> Self { + Self { + vehicle_id, + entered_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_entity_collision.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_entity_collision.rs new file mode 100644 index 000000000..86999f7e7 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_entity_collision.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a vehicle collides with another entity. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleEntityCollisionEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, + /// The ID of the collided entity. + pub collided_entity_id: i32, +} + +impl VehicleEntityCollisionEvent { + #[must_use] + pub const fn new(vehicle_id: i32, collided_entity_id: i32) -> Self { + Self { + vehicle_id, + collided_entity_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_exit.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_exit.rs new file mode 100644 index 000000000..b95208aa3 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_exit.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when an entity exits a vehicle. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleExitEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, + /// The ID of the exiting entity. + pub exited_id: i32, +} + +impl VehicleExitEvent { + #[must_use] + pub const fn new(vehicle_id: i32, exited_id: i32) -> Self { + Self { + vehicle_id, + exited_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_move.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_move.rs new file mode 100644 index 000000000..7b67badde --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_move.rs @@ -0,0 +1,26 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::vector3::Vector3; + +/// An event that occurs when a vehicle moves. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleMoveEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, + /// Starting position. + pub from: Vector3, + /// Target position. + pub to: Vector3, +} + +impl VehicleMoveEvent { + #[must_use] + pub const fn new(vehicle_id: i32, from: Vector3, to: Vector3) -> Self { + Self { + vehicle_id, + from, + to, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_update.rs b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_update.rs new file mode 100644 index 000000000..86e9d5ef7 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/vehicle/vehicle_update.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a vehicle is updated per tick. +#[cancellable] +#[derive(Event, Clone)] +pub struct VehicleUpdateEvent { + /// The ID of the vehicle entity. + pub vehicle_id: i32, +} + +impl VehicleUpdateEvent { + #[must_use] + pub const fn new(vehicle_id: i32) -> Self { + Self { + vehicle_id, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/world/chunk_populate.rs b/crates/pumpkin/src/plugin/api/events/world/chunk_populate.rs new file mode 100644 index 000000000..c0d43835b --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/world/chunk_populate.rs @@ -0,0 +1,20 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::vector2::Vector2; + +/// An event that occurs when a chunk is populated / generated. +#[cancellable] +#[derive(Event, Clone)] +pub struct ChunkPopulateEvent { + /// Chunk coordinates. + pub chunk_pos: Vector2, +} + +impl ChunkPopulateEvent { + #[must_use] + pub const fn new(chunk_pos: Vector2) -> Self { + Self { + chunk_pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/world/chunk_unload.rs b/crates/pumpkin/src/plugin/api/events/world/chunk_unload.rs new file mode 100644 index 000000000..1e30ea5a3 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/world/chunk_unload.rs @@ -0,0 +1,20 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::vector2::Vector2; + +/// An event that occurs when a chunk is unloaded. +#[cancellable] +#[derive(Event, Clone)] +pub struct ChunkUnloadEvent { + /// Chunk coordinates. + pub chunk_pos: Vector2, +} + +impl ChunkUnloadEvent { + #[must_use] + pub const fn new(chunk_pos: Vector2) -> Self { + Self { + chunk_pos, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/world/entities_load.rs b/crates/pumpkin/src/plugin/api/events/world/entities_load.rs new file mode 100644 index 000000000..ad460aad4 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/world/entities_load.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::vector2::Vector2; + +/// An event that occurs when entities in a chunk are loaded. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntitiesLoadEvent { + /// Chunk coordinates. + pub chunk_pos: Vector2, + /// Entity count. + pub entity_count: usize, +} + +impl EntitiesLoadEvent { + #[must_use] + pub const fn new(chunk_pos: Vector2, entity_count: usize) -> Self { + Self { + chunk_pos, + entity_count, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/world/entities_unload.rs b/crates/pumpkin/src/plugin/api/events/world/entities_unload.rs new file mode 100644 index 000000000..999ed5d29 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/world/entities_unload.rs @@ -0,0 +1,23 @@ +use pumpkin_macros::{Event, cancellable}; +use pumpkin_util::math::vector2::Vector2; + +/// An event that occurs when entities in a chunk are unloaded. +#[cancellable] +#[derive(Event, Clone)] +pub struct EntitiesUnloadEvent { + /// Chunk coordinates. + pub chunk_pos: Vector2, + /// Entity count. + pub entity_count: usize, +} + +impl EntitiesUnloadEvent { + #[must_use] + pub const fn new(chunk_pos: Vector2, entity_count: usize) -> Self { + Self { + chunk_pos, + entity_count, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/world/loot_generate.rs b/crates/pumpkin/src/plugin/api/events/world/loot_generate.rs new file mode 100644 index 000000000..e34dda158 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/world/loot_generate.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when loot is generated. +#[cancellable] +#[derive(Event, Clone)] +pub struct LootGenerateEvent { + /// Loot table identifier. + pub loot_table: String, +} + +impl LootGenerateEvent { + #[must_use] + pub const fn new(loot_table: String) -> Self { + Self { + loot_table, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/world/mod.rs b/crates/pumpkin/src/plugin/api/events/world/mod.rs index 74052f15d..2d5ba941e 100644 --- a/crates/pumpkin/src/plugin/api/events/world/mod.rs +++ b/crates/pumpkin/src/plugin/api/events/world/mod.rs @@ -1,23 +1,37 @@ pub mod chunk_load; +pub mod chunk_populate; pub mod chunk_save; pub mod chunk_send; +pub mod chunk_unload; +pub mod entities_load; +pub mod entities_unload; pub mod generic_game; pub mod lightning_strike; +pub mod loot_generate; pub mod portal_create; pub mod spawn_change; pub mod structure_grow; +pub mod time_skip; pub mod weather_change; pub mod world_init; pub mod world_load; +pub mod world_save; pub use chunk_load::*; +pub use chunk_populate::*; pub use chunk_save::*; pub use chunk_send::*; +pub use chunk_unload::*; +pub use entities_load::*; +pub use entities_unload::*; pub use generic_game::*; pub use lightning_strike::*; +pub use loot_generate::*; pub use portal_create::*; pub use spawn_change::*; pub use structure_grow::*; +pub use time_skip::*; pub use weather_change::*; pub use world_init::*; pub use world_load::*; +pub use world_save::*; diff --git a/crates/pumpkin/src/plugin/api/events/world/time_skip.rs b/crates/pumpkin/src/plugin/api/events/world/time_skip.rs new file mode 100644 index 000000000..4b88286be --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/world/time_skip.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when world time is skipped. +#[cancellable] +#[derive(Event, Clone)] +pub struct TimeSkipEvent { + /// Ticks skipped. + pub skip_amount: i64, +} + +impl TimeSkipEvent { + #[must_use] + pub const fn new(skip_amount: i64) -> Self { + Self { + skip_amount, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/api/events/world/world_save.rs b/crates/pumpkin/src/plugin/api/events/world/world_save.rs new file mode 100644 index 000000000..9c4dc4c44 --- /dev/null +++ b/crates/pumpkin/src/plugin/api/events/world/world_save.rs @@ -0,0 +1,19 @@ +use pumpkin_macros::{Event, cancellable}; + +/// An event that occurs when a world is saved. +#[cancellable] +#[derive(Event, Clone)] +pub struct WorldSaveEvent { + /// World name. + pub world_name: String, +} + +impl WorldSaveEvent { + #[must_use] + pub const fn new(world_name: String) -> Self { + Self { + world_name, + cancelled: false, + } + } +} diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs index f6929eabc..5daf88eaa 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs @@ -240,6 +240,7 @@ impl PluginHostState { } } +#[allow(clippy::too_many_lines)] async fn register_entity_event( resource: &ContextResource, handler: &Arc, @@ -248,11 +249,27 @@ async fn register_entity_event( event_type: EventType, ) { use crate::plugin::entity::{ + entity_air_change::EntityAirChangeEvent, + entity_breed::EntityBreedEvent, entity_combust::EntityCombustEvent, entity_damage::EntityDamageEvent, entity_death::{EntityDeathEvent, PlayerDeathEvent}, + entity_dismount::EntityDismountEvent, + entity_dye::EntityDyeEvent, + entity_enter_love_mode::EntityEnterLoveModeEvent, + entity_explode::EntityExplodeEvent, + entity_mount::EntityMountEvent, + entity_pickup_item::EntityPickupItemEvent, + entity_portal::EntityPortalEvent, entity_regain_health::EntityRegainHealthEvent, + entity_resurrect::EntityResurrectEvent, + entity_shoot_bow::EntityShootBowEvent, entity_spawn::EntitySpawnEvent, + entity_tame::EntityTameEvent, + entity_target::EntityTargetEvent, + entity_teleport::EntityTeleportEvent, + entity_toggle_glide::EntityToggleGlideEvent, + entity_transform::EntityTransformEvent, }; match event_type { @@ -275,6 +292,63 @@ async fn register_entity_event( register_typed_event::(resource, handler, priority, blocking) .await; } + EventType::EntityAirChangeEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityBreedEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + EventType::EntityDismountEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityDyeEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + EventType::EntityEnterLoveModeEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityExplodeEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + EventType::EntityMountEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + EventType::EntityPickupItemEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityPortalEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + EventType::EntityResurrectEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityShootBowEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityTameEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + EventType::EntityTargetEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + EventType::EntityTeleportEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityToggleGlideEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EntityTransformEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } _ => { tracing::error!("non-entity event should not be routed to register_entity_event"); } @@ -312,6 +386,33 @@ async fn register_inventory_event( } } +async fn register_enchantment_event( + resource: &ContextResource, + handler: &Arc, + priority: crate::plugin::EventPriority, + blocking: bool, + event_type: EventType, +) { + use crate::plugin::enchantment::{ + enchant_item::EnchantItemEvent, prepare_item_enchant::PrepareItemEnchantEvent, + }; + + match event_type { + EventType::PrepareItemEnchantEvent => { + register_typed_event::(resource, handler, priority, blocking) + .await; + } + EventType::EnchantItemEvent => { + register_typed_event::(resource, handler, priority, blocking).await; + } + _ => { + tracing::error!( + "non-enchantment event should not be routed to register_enchantment_event" + ); + } + } +} + async fn register_world_event( resource: &ContextResource, handler: &Arc, @@ -367,6 +468,7 @@ async fn register_world_event( } } +#[allow(clippy::too_many_lines)] async fn register_block_event( resource: &ContextResource, handler: &Arc, @@ -429,6 +531,60 @@ async fn register_block_event( ) .await; } + EventType::BlockDispenseEvent => { + register_typed_event::< + crate::plugin::api::events::block::block_dispense::BlockDispenseEvent, + >(resource, handler, priority, blocking) + .await; + } + EventType::BlockExplodeEvent => { + register_typed_event::< + crate::plugin::api::events::block::block_explode::BlockExplodeEvent, + >(resource, handler, priority, blocking) + .await; + } + EventType::BlockPhysicsEvent => { + register_typed_event::< + crate::plugin::api::events::block::block_physics::BlockPhysicsEvent, + >(resource, handler, priority, blocking) + .await; + } + EventType::BlockPistonExtendEvent => { + register_typed_event::< + crate::plugin::api::events::block::block_piston::BlockPistonExtendEvent, + >(resource, handler, priority, blocking) + .await; + } + EventType::BlockPistonRetractEvent => { + register_typed_event::< + crate::plugin::api::events::block::block_piston::BlockPistonRetractEvent, + >(resource, handler, priority, blocking) + .await; + } + EventType::NotePlayEvent => { + register_typed_event::( + resource, handler, priority, blocking, + ) + .await; + } + EventType::SignChangeEvent => { + register_typed_event::< + crate::plugin::api::events::block::sign_change::SignChangeEvent, + >(resource, handler, priority, blocking) + .await; + } + EventType::SpongeAbsorbEvent => { + register_typed_event::< + crate::plugin::api::events::block::sponge_absorb::SpongeAbsorbEvent, + >(resource, handler, priority, blocking) + .await; + } + EventType::TntPrimeEvent => { + register_typed_event::( + resource, handler, priority, blocking, + ) + .await; + } _ => { tracing::error!("non-block event should not be routed to register_block_event"); @@ -571,7 +727,23 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { | EventType::PlayerDeathEvent | EventType::EntitySpawnEvent | EventType::EntityCombustEvent - | EventType::EntityRegainHealthEvent) => { + | EventType::EntityRegainHealthEvent + | EventType::EntityAirChangeEvent + | EventType::EntityBreedEvent + | EventType::EntityDismountEvent + | EventType::EntityDyeEvent + | EventType::EntityEnterLoveModeEvent + | EventType::EntityExplodeEvent + | EventType::EntityMountEvent + | EventType::EntityPickupItemEvent + | EventType::EntityPortalEvent + | EventType::EntityResurrectEvent + | EventType::EntityShootBowEvent + | EventType::EntityTameEvent + | EventType::EntityTargetEvent + | EventType::EntityTeleportEvent + | EventType::EntityToggleGlideEvent + | EventType::EntityTransformEvent) => { register_entity_event(resource, &handler, priority, blocking, event_type).await; } event_type @ (EventType::BlockRedstoneEvent @@ -584,7 +756,16 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { | EventType::BlockIgniteEvent | EventType::BlockFromToEvent | EventType::BlockFormEvent - | EventType::BlockFadeEvent) => { + | EventType::BlockFadeEvent + | EventType::BlockDispenseEvent + | EventType::BlockExplodeEvent + | EventType::BlockPhysicsEvent + | EventType::BlockPistonExtendEvent + | EventType::BlockPistonRetractEvent + | EventType::NotePlayEvent + | EventType::SignChangeEvent + | EventType::SpongeAbsorbEvent + | EventType::TntPrimeEvent) => { register_block_event(resource, &handler, priority, blocking, event_type).await; } event_type @ (EventType::InventoryOpenEvent @@ -593,6 +774,10 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { | EventType::FurnaceSmeltEvent) => { register_inventory_event(resource, &handler, priority, blocking, event_type).await; } + event_type @ (EventType::PrepareItemEnchantEvent | EventType::EnchantItemEvent) => { + register_enchantment_event(resource, &handler, priority, blocking, event_type) + .await; + } event_type => { register_player_event(resource, &handler, priority, blocking, event_type).await; } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/block.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/block.rs index 2b3d4cee0..6c523cc88 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/block.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/block.rs @@ -2,9 +2,20 @@ use pumpkin_data::BlockStateId; use crate::plugin::{ block::{ - block_break::BlockBreakEvent, block_burn::BlockBurnEvent, - block_can_build::BlockCanBuildEvent, block_grow::BlockGrowEvent, - block_place::BlockPlaceEvent, block_redstone::BlockRedstoneEvent, + block_break::BlockBreakEvent, + block_burn::BlockBurnEvent, + block_can_build::BlockCanBuildEvent, + block_dispense::BlockDispenseEvent, + block_explode::BlockExplodeEvent, + block_grow::BlockGrowEvent, + block_physics::BlockPhysicsEvent, + block_piston::{BlockPistonExtendEvent, BlockPistonRetractEvent}, + block_place::BlockPlaceEvent, + block_redstone::BlockRedstoneEvent, + note_play::NotePlayEvent, + sign_change::SignChangeEvent, + sponge_absorb::SpongeAbsorbEvent, + tnt_prime::TNTPrimeEvent, }, loader::wasm::wasm_host::{ state::PluginHostState, @@ -15,9 +26,11 @@ use crate::plugin::{ }, pumpkin::plugin::event::{ BlockBreakEventData, BlockBurnEventData, BlockCanBuildEventData, - BlockDamageEventData, BlockFadeEventData, BlockFormEventData, BlockFromToEventData, - BlockGrowEventData, BlockIgniteEventData, BlockPlaceEventData, - BlockRedstoneEventData, Event, + BlockDamageEventData, BlockDispenseEventData, BlockExplodeEventData, + BlockFadeEventData, BlockFormEventData, BlockFromToEventData, BlockGrowEventData, + BlockIgniteEventData, BlockPhysicsEventData, BlockPistonExtendEventData, + BlockPistonRetractEventData, BlockPlaceEventData, BlockRedstoneEventData, Event, + NotePlayEventData, SignChangeEventData, SpongeAbsorbEventData, TntPrimeEventData, }, }, }, @@ -311,3 +324,197 @@ impl ToFromWasmEvent for crate::plugin::api::events::block::block_fade::BlockFad } } } + +impl ToFromWasmEvent for BlockDispenseEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::BlockDispenseEvent(BlockDispenseEventData { + block_pos: to_wasm_block_position(self.block_pos), + item_name: self.item_name.clone(), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::BlockDispenseEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + item_name: data.item_name, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for BlockExplodeEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::BlockExplodeEvent(BlockExplodeEventData { + block_pos: to_wasm_block_position(self.block_pos), + yield_rate: self.yield_rate, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::BlockExplodeEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + yield_rate: data.yield_rate, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for BlockPhysicsEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::BlockPhysicsEvent(BlockPhysicsEventData { + block_pos: to_wasm_block_position(self.block_pos), + changed_pos: to_wasm_block_position(self.changed_pos), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::BlockPhysicsEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + changed_pos: from_wasm_block_position(data.changed_pos), + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for BlockPistonExtendEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::BlockPistonExtendEvent(BlockPistonExtendEventData { + block_pos: to_wasm_block_position(self.block_pos), + direction: self.direction.clone(), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::BlockPistonExtendEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + direction: data.direction, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for BlockPistonRetractEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::BlockPistonRetractEvent(BlockPistonRetractEventData { + block_pos: to_wasm_block_position(self.block_pos), + direction: self.direction.clone(), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::BlockPistonRetractEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + direction: data.direction, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for NotePlayEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::NotePlayEvent(NotePlayEventData { + block_pos: to_wasm_block_position(self.block_pos), + instrument: self.instrument.clone(), + note: self.note, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::NotePlayEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + instrument: data.instrument, + note: data.note, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for SignChangeEvent { + fn to_wasm_event(&self, state: &mut PluginHostState) -> Event { + let player = state + .add_player(self.player.clone()) + .expect("failed to add player resource"); + Event::SignChangeEvent(SignChangeEventData { + player, + block_pos: to_wasm_block_position(self.block_pos), + lines: self.lines.clone(), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self { + match event { + Event::SignChangeEvent(data) => Self { + player: consume_player(state, &data.player), + block_pos: from_wasm_block_position(data.block_pos), + lines: data.lines, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for SpongeAbsorbEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::SpongeAbsorbEvent(SpongeAbsorbEventData { + block_pos: to_wasm_block_position(self.block_pos), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::SpongeAbsorbEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for TNTPrimeEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::TntPrimeEvent(TntPrimeEventData { + block_pos: to_wasm_block_position(self.block_pos), + prime_reason: self.prime_reason.clone(), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::TntPrimeEvent(data) => Self { + block_pos: from_wasm_block_position(data.block_pos), + prime_reason: data.prime_reason, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/enchantment.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/enchantment.rs new file mode 100644 index 000000000..f65ae4dfb --- /dev/null +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/enchantment.rs @@ -0,0 +1,134 @@ +use std::sync::Arc; +use tokio::sync::Mutex; +use wasmtime::component::Resource; + +use crate::plugin::{ + enchantment::{enchant_item::EnchantItemEvent, prepare_item_enchant::PrepareItemEnchantEvent}, + loader::wasm::wasm_host::{ + state::{ItemStackResource, PluginHostState}, + wit::v0_1::{ + events::{ToFromWasmEvent, consume_player}, + item_stack::{from_wit_enchantment, to_wit_enchantment}, + pumpkin::plugin::event::{ + EnchantItemEventData, EnchantmentOffer, EnchantmentValue as WitEnchantmentValue, + Event, PrepareItemEnchantEventData, + }, + }, + }, +}; + +fn consume_item_stack( + state: &mut PluginHostState, + item: &Resource< + crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::item_stack::ItemStack, + >, +) -> pumpkin_data::item_stack::ItemStack { + let mutex = state + .resource_table + .delete::(Resource::new_own(item.rep())) + .expect("invalid item stack resource handle") + .provider; + mutex.try_lock().expect("lock item stack").clone() +} + +impl ToFromWasmEvent for PrepareItemEnchantEvent { + fn to_wasm_event(&self, state: &mut PluginHostState) -> Event { + let player = state + .add_player(self.player.clone()) + .expect("failed to add player resource"); + let item = state + .add_item_stack(Arc::new(Mutex::new(self.item.clone()))) + .expect("failed to add item stack resource"); + + let offers = (0..3) + .map(|i| EnchantmentOffer { + cost: self.level_requirements[i], + enchantment_id: self.enchantment_id[i], + enchantment_level: self.enchantment_level[i], + }) + .collect(); + + Event::PrepareItemEnchantEvent(PrepareItemEnchantEventData { + player, + item, + offers, + bookshelf_count: self.bookshelf_count, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self { + match event { + Event::PrepareItemEnchantEvent(data) => { + let mut level_requirements = [0; 3]; + let mut enchantment_id = [-1; 3]; + let mut enchantment_level = [-1; 3]; + for (i, offer) in data.offers.iter().enumerate().take(3) { + level_requirements[i] = offer.cost; + enchantment_id[i] = offer.enchantment_id; + enchantment_level[i] = offer.enchantment_level; + } + Self { + player: consume_player(state, &data.player), + item: consume_item_stack(state, &data.item), + level_requirements, + enchantment_id, + enchantment_level, + bookshelf_count: data.bookshelf_count, + cancelled: data.cancelled, + } + } + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for EnchantItemEvent { + fn to_wasm_event(&self, state: &mut PluginHostState) -> Event { + let player = state + .add_player(self.player.clone()) + .expect("failed to add player resource"); + let item = state + .add_item_stack(Arc::new(Mutex::new(self.item.clone()))) + .expect("failed to add item stack resource"); + + let enchantments_to_add = self + .enchantments_to_add + .iter() + .map(|(enc, level)| WitEnchantmentValue { + enchantment: to_wit_enchantment(enc), + level: *level as u32, + }) + .collect(); + + Event::EnchantItemEvent(EnchantItemEventData { + player, + item, + option: self.option, + cost: self.exp_level_cost, + enchantments_to_add, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self { + match event { + Event::EnchantItemEvent(data) => { + let enchantments_to_add = data + .enchantments_to_add + .into_iter() + .map(|ev| (from_wit_enchantment(ev.enchantment), ev.level as i32)) + .collect(); + Self { + player: consume_player(state, &data.player), + item: consume_item_stack(state, &data.item), + option: data.option, + exp_level_cost: data.cost, + enchantments_to_add, + cancelled: data.cancelled, + } + } + _ => panic!("unexpected event type"), + } + } +} diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/entity.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/entity.rs index f268d25f2..fdead9cf7 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/entity.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/entity.rs @@ -11,11 +11,17 @@ use crate::plugin::{ wit::v0_1::{ events::{ ToFromWasmEvent, consume_player, consume_text_component, consume_world, - to_wasm_position, + from_wasm_block_position, to_wasm_block_position, to_wasm_position, }, pumpkin::plugin::event::{ - EntityCombustEventData, EntityDamageEventData, EntityDeathEventData, - EntityRegainHealthEventData, EntitySpawnEventData, Event, PlayerDeathEventData, + EntityAirChangeEventData, EntityBreedEventData, EntityCombustEventData, + EntityDamageEventData, EntityDeathEventData, EntityDismountEventData, + EntityDyeEventData, EntityEnterLoveModeEventData, EntityExplodeEventData, + EntityMountEventData, EntityPickupItemEventData, EntityPortalEventData, + EntityRegainHealthEventData, EntityResurrectEventData, EntityShootBowEventData, + EntitySpawnEventData, EntityTameEventData, EntityTargetEventData, + EntityTeleportEventData, EntityToggleGlideEventData, EntityTransformEventData, + Event, PlayerDeathEventData, }, }, }, @@ -172,3 +178,384 @@ impl ToFromWasmEvent for EntityRegainHealthEvent { } } } + +impl ToFromWasmEvent + for crate::plugin::api::events::entity::entity_air_change::EntityAirChangeEvent +{ + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityAirChangeEvent(EntityAirChangeEventData { + entity_id: self.entity_id, + amount: self.amount, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityAirChangeEvent(data) => Self { + entity_id: data.entity_id, + amount: data.amount, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_breed::EntityBreedEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityBreedEvent(EntityBreedEventData { + father_id: self.father_id, + mother_id: self.mother_id, + child_id: self.child_id, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityBreedEvent(data) => Self { + father_id: data.father_id, + mother_id: data.mother_id, + child_id: data.child_id, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_dismount::EntityDismountEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityDismountEvent(EntityDismountEventData { + entity_id: self.entity_id, + dismounted_id: self.dismounted_id, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityDismountEvent(data) => Self { + entity_id: data.entity_id, + dismounted_id: data.dismounted_id, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_dye::EntityDyeEvent { + fn to_wasm_event(&self, state: &mut PluginHostState) -> Event { + let player = self + .player + .as_ref() + .and_then(|p| state.add_player(p.clone()).ok()); + Event::EntityDyeEvent(EntityDyeEventData { + entity_id: self.entity_id, + color: format!("{:?}", self.color), + player, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self { + match event { + Event::EntityDyeEvent(data) => Self { + entity_id: data.entity_id, + color: crate::plugin::api::events::entity::entity_dye::DyeColor::White, + player: data.player.map(|p| consume_player(state, &p)), + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent + for crate::plugin::api::events::entity::entity_enter_love_mode::EntityEnterLoveModeEvent +{ + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityEnterLoveModeEvent(EntityEnterLoveModeEventData { + entity_id: self.entity_id, + human_entity_id: self.human_entity_id, + ticks_in_love: self.ticks_in_love, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityEnterLoveModeEvent(data) => Self { + entity_id: data.entity_id, + human_entity_id: data.human_entity_id, + ticks_in_love: data.ticks_in_love, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_explode::EntityExplodeEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityExplodeEvent(EntityExplodeEventData { + entity_id: self.entity_id, + position: to_wasm_position(self.position), + yield_rate: self.yield_rate, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityExplodeEvent(data) => Self { + entity_id: data.entity_id, + position: pumpkin_util::math::vector3::Vector3::new( + data.position.0, + data.position.1, + data.position.2, + ), + yield_rate: data.yield_rate, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_mount::EntityMountEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityMountEvent(EntityMountEventData { + entity_id: self.entity_id, + mounted_id: self.mount_id, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityMountEvent(data) => Self { + entity_id: data.entity_id, + mount_id: data.mounted_id, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent + for crate::plugin::api::events::entity::entity_pickup_item::EntityPickupItemEvent +{ + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityPickupItemEvent(EntityPickupItemEventData { + entity_id: self.entity_id, + item_name: self.item_name.clone(), + count: self.count, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityPickupItemEvent(data) => Self { + entity_id: data.entity_id, + item_name: data.item_name, + count: data.count, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_portal::EntityPortalEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityPortalEvent(EntityPortalEventData { + entity_id: self.entity_id, + portal_pos: to_wasm_block_position(self.portal_pos), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityPortalEvent(data) => Self { + entity_id: data.entity_id, + portal_pos: from_wasm_block_position(data.portal_pos), + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent + for crate::plugin::api::events::entity::entity_resurrect::EntityResurrectEvent +{ + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityResurrectEvent(EntityResurrectEventData { + entity_id: self.entity_id, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityResurrectEvent(data) => Self { + entity_id: data.entity_id, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_shoot_bow::EntityShootBowEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityShootBowEvent(EntityShootBowEventData { + entity_id: self.entity_id, + weapon_name: self.weapon_name.clone(), + force: self.force, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityShootBowEvent(data) => Self { + entity_id: data.entity_id, + weapon_name: data.weapon_name, + force: data.force, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_tame::EntityTameEvent { + fn to_wasm_event(&self, state: &mut PluginHostState) -> Event { + let owner = state + .add_player(self.owner.clone()) + .expect("failed to add player resource"); + Event::EntityTameEvent(EntityTameEventData { + entity_id: self.entity_id, + owner, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self { + match event { + Event::EntityTameEvent(data) => Self { + entity_id: data.entity_id, + owner: consume_player(state, &data.owner), + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_target::EntityTargetEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityTargetEvent(EntityTargetEventData { + entity_id: self.entity_id, + target_id: self.target_id, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityTargetEvent(data) => Self { + entity_id: data.entity_id, + target_id: data.target_id, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent for crate::plugin::api::events::entity::entity_teleport::EntityTeleportEvent { + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityTeleportEvent(EntityTeleportEventData { + entity_id: self.entity_id, + from_position: to_wasm_position(self.from_position), + to_position: to_wasm_position(self.to_position), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityTeleportEvent(data) => Self { + entity_id: data.entity_id, + from_position: pumpkin_util::math::vector3::Vector3::new( + data.from_position.0, + data.from_position.1, + data.from_position.2, + ), + to_position: pumpkin_util::math::vector3::Vector3::new( + data.to_position.0, + data.to_position.1, + data.to_position.2, + ), + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent + for crate::plugin::api::events::entity::entity_toggle_glide::EntityToggleGlideEvent +{ + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityToggleGlideEvent(EntityToggleGlideEventData { + entity_id: self.entity_id, + is_gliding: self.is_gliding, + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityToggleGlideEvent(data) => Self { + entity_id: data.entity_id, + is_gliding: data.is_gliding, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} + +impl ToFromWasmEvent + for crate::plugin::api::events::entity::entity_transform::EntityTransformEvent +{ + fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event { + Event::EntityTransformEvent(EntityTransformEventData { + entity_id: self.entity_id, + new_entity_id: self.new_entity_id, + transform_reason: self.transform_reason.clone(), + cancelled: self.cancelled, + }) + } + + fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self { + match event { + Event::EntityTransformEvent(data) => Self { + entity_id: data.entity_id, + new_entity_id: data.new_entity_id, + transform_reason: data.transform_reason, + cancelled: data.cancelled, + }, + _ => panic!("unexpected event type"), + } + } +} diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/mod.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/mod.rs index 220ef9091..40e009e0b 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/mod.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/mod.rs @@ -24,6 +24,7 @@ use crate::{ }; pub mod block; +pub mod enchantment; pub mod entity; pub mod inventory; pub mod player; diff --git a/crates/pumpkin/src/server/mod.rs b/crates/pumpkin/src/server/mod.rs index 4e6a3ff8a..1a30bab3e 100644 --- a/crates/pumpkin/src/server/mod.rs +++ b/crates/pumpkin/src/server/mod.rs @@ -370,6 +370,22 @@ impl Server { worlds_vec.push(world); } + for world in &worlds_vec { + let mut world_init_event = + crate::plugin::api::events::world::world_init::WorldInitEvent::new(world.clone()); + server + .plugin_manager + .fire(&server, &mut world_init_event) + .await; + + let mut world_load_event = + crate::plugin::api::events::world::world_load::WorldLoadEvent::new(world.clone()); + server + .plugin_manager + .fire(&server, &mut world_load_event) + .await; + } + server.worlds.store(Arc::new(worlds_vec)); if let Ok(k) = keys { server.mojang_public_keys.store(Arc::new(k)); @@ -1210,4 +1226,49 @@ impl Server { } } } + + pub async fn execute_remote_command(self: &Arc, command: String) { + let mut remote_event = crate::plugin::api::events::server::remote_server_command::RemoteServerCommandEvent::new( + command, + ); + self.plugin_manager.fire(self, &mut remote_event).await; + } + + pub async fn register_service(self: &Arc, service_name: String) { + let mut service_event = + crate::plugin::api::events::server::service_register::ServiceRegisterEvent::new( + service_name, + ); + self.plugin_manager.fire(self, &mut service_event).await; + } + + pub async fn unregister_service(self: &Arc, service_name: String) { + let mut service_event = + crate::plugin::api::events::server::service_unregister::ServiceUnregisterEvent::new( + service_name, + ); + self.plugin_manager.fire(self, &mut service_event).await; + } + + pub async fn tab_complete(self: &Arc, buffer: String, completions: Vec) { + let mut tab_event = crate::plugin::api::events::server::tab_complete::TabCompleteEvent::new( + buffer, + completions, + ); + self.plugin_manager.fire(self, &mut tab_event).await; + } + + pub async fn enable_plugin(self: &Arc, plugin_name: String) { + let mut enable_event = + crate::plugin::api::events::server::plugin_enable::PluginEnableEvent::new(plugin_name); + self.plugin_manager.fire(self, &mut enable_event).await; + } + + pub async fn disable_plugin(self: &Arc, plugin_name: String) { + let mut disable_event = + crate::plugin::api::events::server::plugin_disable::PluginDisableEvent::new( + plugin_name, + ); + self.plugin_manager.fire(self, &mut disable_event).await; + } } diff --git a/crates/pumpkin/src/world/explosion.rs b/crates/pumpkin/src/world/explosion.rs index 19317b671..1d466626f 100644 --- a/crates/pumpkin/src/world/explosion.rs +++ b/crates/pumpkin/src/world/explosion.rs @@ -291,6 +291,22 @@ impl Explosion { /// Returns the removed block count pub async fn explode(&self, world: &Arc) -> u32 { + let center_pos = BlockPos::floored(self.pos.x, self.pos.y, self.pos.z); + let mut event = crate::plugin::api::events::block::block_explode::BlockExplodeEvent::new( + center_pos, + if self.power > 0.0 { + 1.0 / self.power + } else { + 1.0 + }, + ); + if let Some(server) = world.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return 0; + } + let blocks = self.get_blocks_to_destroy(world); self.damage_entities(world).await; for (pos, (block, state)) in &blocks { diff --git a/crates/pumpkin/src/world/mod.rs b/crates/pumpkin/src/world/mod.rs index 6de031657..7de96dd32 100644 --- a/crates/pumpkin/src/world/mod.rs +++ b/crates/pumpkin/src/world/mod.rs @@ -3593,6 +3593,16 @@ impl World { position: Vector3, power: f32, ) { + let mut event = crate::plugin::api::events::entity::entity_explode::EntityExplodeEvent::new( + 0, position, power, + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + let block_count = explosion.explode(self).await; let particle = if power < 2.0 { Particle::Explosion @@ -4470,7 +4480,20 @@ impl World { }); } - pub async fn spawn_entity(&self, entity: Arc) { + pub async fn spawn_entity(self: &Arc, entity: Arc) { + let mut event = crate::plugin::api::events::entity::entity_spawn::EntitySpawnEvent::new( + entity.get_entity().entity_id, + entity.get_entity().entity_type.id.to_string(), + entity.get_entity().pos.load(), + self.clone(), + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + self.broadcast_entity_spawn(&entity); entity.init_data_tracker().await; self.add_entity_silent(entity).await; @@ -5023,6 +5046,18 @@ impl World { }; let entity = Entity::new(self.clone(), spawn_pos, &EntityType::ITEM); + let mut item_event = crate::plugin::api::events::entity::item_spawn::ItemSpawnEvent::new( + entity.entity_id, + spawn_pos, + stack.item.registry_key.to_string(), + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut item_event).await; + } + if item_event.cancelled { + return; + } + let item_entity = Arc::new(ItemEntity::new(entity, stack)); self.spawn_entity(item_entity).await; } @@ -5310,6 +5345,18 @@ impl World { let neighbor_pos = block_pos.offset(direction.to_offset()); let (neighbor_block, neighbor_fluid) = self.get_block_and_fluid(&neighbor_pos); + let mut event = + crate::plugin::api::events::block::block_physics::BlockPhysicsEvent::new( + neighbor_pos, + *block_pos, + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + continue; + } + if let Some(neighbor_pumpkin_block) = self.block_registry.get_pumpkin_block(neighbor_block.id) { @@ -5341,6 +5388,17 @@ impl World { ) { let neighbor_block = self.get_block(neighbor_block_pos); + let mut event = crate::plugin::api::events::block::block_physics::BlockPhysicsEvent::new( + *neighbor_block_pos, + *neighbor_block_pos, + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + if event.cancelled { + return; + } + if let Some(neighbor_pumpkin_block) = self.block_registry.get_pumpkin_block(neighbor_block.id) { @@ -5948,6 +6006,122 @@ impl World { recipient.enqueue_packet(be_packet).await; } } + + pub async fn emit_game_event(&self, event_key: impl Into, position: Vector3) { + let mut event = crate::plugin::api::events::world::generic_game::GenericGameEvent::new( + event_key.into(), + position, + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + } + + pub async fn unload(self: &Arc) { + let mut event = + crate::plugin::api::events::world::world_load::WorldUnloadEvent::new(self.clone()); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut event).await; + } + } + + pub async fn save(&self) { + let mut save_event = crate::plugin::api::events::world::world_save::WorldSaveEvent::new( + format!("{:?}", self.dimension), + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut save_event).await; + } + } + + pub async fn populate_chunk(&self, chunk_pos: Vector2) { + let mut populate_event = + crate::plugin::api::events::world::chunk_populate::ChunkPopulateEvent::new(chunk_pos); + if let Some(server) = self.server.upgrade() { + server + .plugin_manager + .fire(&server, &mut populate_event) + .await; + } + } + + pub async fn unload_chunk(&self, chunk_pos: Vector2) { + let mut unload_event = + crate::plugin::api::events::world::chunk_unload::ChunkUnloadEvent::new(chunk_pos); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut unload_event).await; + } + } + + pub async fn load_entities(&self, chunk_pos: Vector2, entity_count: usize) { + let mut load_event = + crate::plugin::api::events::world::entities_load::EntitiesLoadEvent::new( + chunk_pos, + entity_count, + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut load_event).await; + } + } + + pub async fn unload_entities(&self, chunk_pos: Vector2, entity_count: usize) { + let mut unload_event = + crate::plugin::api::events::world::entities_unload::EntitiesUnloadEvent::new( + chunk_pos, + entity_count, + ); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut unload_event).await; + } + } + + pub async fn generate_loot(&self, loot_table: String) { + let mut loot_event = + crate::plugin::api::events::world::loot_generate::LootGenerateEvent::new(loot_table); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut loot_event).await; + } + } + + pub async fn skip_time(&self, skip_amount: i64) { + let mut time_event = + crate::plugin::api::events::world::time_skip::TimeSkipEvent::new(skip_amount); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut time_event).await; + } + } + + pub async fn trigger_raid(&self, pos: BlockPos) { + let mut raid_event = + crate::plugin::api::events::raid::raid_trigger::RaidTriggerEvent::new(pos); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut raid_event).await; + } + } + + pub async fn spawn_raid_wave(&self, wave: u32, pos: BlockPos) { + let mut wave_event = + crate::plugin::api::events::raid::raid_spawn_wave::RaidSpawnWaveEvent::new(wave, pos); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut wave_event).await; + } + } + + pub async fn finish_raid(&self, victory: bool) { + let mut raid_event = + crate::plugin::api::events::raid::raid_finish::RaidFinishEvent::new(victory); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut raid_event).await; + } + } + + pub async fn stop_raid(&self, reason: String) { + let mut raid_event = + crate::plugin::api::events::raid::raid_stop::RaidStopEvent::new(reason); + if let Some(server) = self.server.upgrade() { + server.plugin_manager.fire(&server, &mut raid_event).await; + } + } } impl BlockAccessor for World {