chore: add some more events

This commit is contained in:
Alexander Medvedev
2026-08-14 15:58:16 +02:00
parent bd47c40708
commit f27e64afbf
121 changed files with 4051 additions and 61 deletions

View File

@@ -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);

View File

@@ -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.

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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::*;

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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::*;

View File

@@ -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(

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -18,6 +18,15 @@ pub struct SaplingBlock;
impl SaplingBlock {
async fn generate(&self, world: &Arc<World>, 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 {

View File

@@ -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 = [

View File

@@ -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)

View File

@@ -24,7 +24,31 @@ pub struct TNTBlock;
impl TNTBlock {
pub async fn prime(world: &Arc<World>, 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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)

View File

@@ -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

View File

@@ -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::<DeathProtectionImpl>().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;
}

View File

@@ -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<Arc<dyn EntityBase>>) -> 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<Player>) -> 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<Player>>,
) -> 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<i32>,
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<Player>) -> EntityBaseFuture<'a, ()> {
Box::pin(async {})
}

View File

@@ -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<World>,
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<dyn EntityBase>,
passenger: Arc<dyn EntityBase>,
) {
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()

View File

@@ -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);
}
})

View File

@@ -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<Self>, 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)]

View File

@@ -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();

View File

@@ -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<f64>,
to: pumpkin_util::math::vector3::Vector3<f64>,
) {
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 {

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -3,6 +3,17 @@ use super::*;
impl JavaClient {
pub async fn handle_select_trade(&self, player: &Arc<Player>, 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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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<Player>,
/// 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<Player>,
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,
}
}
}

View File

@@ -0,0 +1,5 @@
pub mod enchant_item;
pub mod prepare_item_enchant;
pub use enchant_item::EnchantItemEvent;
pub use prepare_item_enchant::PrepareItemEnchantEvent;

View File

@@ -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<Player>,
/// 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<Player>,
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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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<i32>,
/// 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<i32>, ticks_in_love: i32) -> Self {
Self {
entity_id,
human_entity_id,
ticks_in_love,
cancelled: false,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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<f64>,
/// Item registry name.
pub item_name: String,
}
impl ItemSpawnEvent {
#[must_use]
pub const fn new(entity_id: i32, position: Vector3<f64>, item_name: String) -> Self {
Self {
entity_id,
position,
item_name,
cancelled: false,
}
}
}

View File

@@ -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::*;

View File

@@ -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<f64>,
/// ID of hit entity if applicable.
pub hit_entity_id: Option<i32>,
}
impl ProjectileHitEvent {
#[must_use]
pub const fn new(
entity_id: i32,
hit_position: Vector3<f64>,
hit_entity_id: Option<i32>,
) -> Self {
Self {
entity_id,
hit_position,
hit_entity_id,
cancelled: false,
}
}
}

View File

@@ -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<i32>,
}
impl ProjectileLaunchEvent {
#[must_use]
pub const fn new(entity_id: i32, shooter_id: Option<i32>) -> Self {
Self {
entity_id,
shooter_id,
cancelled: false,
}
}
}

View File

@@ -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.

View File

@@ -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::*;

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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::*;

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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<String>,
}
impl TabCompleteEvent {
#[must_use]
pub const fn new(buffer: String, completions: Vec<String>) -> Self {
Self {
buffer,
completions,
cancelled: false,
}
}
}

View File

@@ -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::*;

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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<i32>,
}
impl VehicleDamageEvent {
#[must_use]
pub const fn new(vehicle_id: i32, damage: f32, attacker_id: Option<i32>) -> Self {
Self {
vehicle_id,
damage,
attacker_id,
cancelled: false,
}
}
}

View File

@@ -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<i32>,
}
impl VehicleDestroyEvent {
#[must_use]
pub const fn new(vehicle_id: i32, attacker_id: Option<i32>) -> Self {
Self {
vehicle_id,
attacker_id,
cancelled: false,
}
}
}

Some files were not shown because too many files have changed in this diff Show More