chore: move to crates folder

This commit is contained in:
Alexander Medvedev
2026-08-06 12:56:40 +02:00
parent 5d10c0d8a8
commit cb3660bcd2
3393 changed files with 1270 additions and 741 deletions

View File

@@ -0,0 +1,45 @@
use crate::wit::pumpkin::plugin::context::Server;
use crate::wit::pumpkin::plugin::world::Entity;
use std::collections::BTreeMap;
use std::sync::Mutex;
/// Represents a custom entity AI goal for mobs.
#[allow(unused_variables)]
pub trait AiGoal: Send + Sync {
/// Returns `true` if the goal should start executing.
fn can_start(&mut self, server: Server, entity: Entity) -> bool {
false
}
/// Returns `true` if the goal should continue executing on subsequent ticks.
fn should_continue(&mut self, server: Server, entity: Entity) -> bool {
false
}
/// Executed when the goal starts.
fn start(&mut self, server: Server, entity: Entity) {}
/// Executed on every server tick while the goal is active.
fn tick(&mut self, server: Server, entity: Entity) {}
/// Executed when the goal stops executing.
fn stop(&mut self, server: Server, entity: Entity) {}
}
pub(crate) static AI_GOAL_HANDLERS: Mutex<LazyAiGoalHandlers> = Mutex::new(LazyAiGoalHandlers {
handlers: BTreeMap::new(),
next_id: 0,
});
#[allow(dead_code)]
pub(crate) struct LazyAiGoalHandlers {
pub handlers: BTreeMap<u32, Box<dyn AiGoal>>,
pub next_id: u32,
}
#[allow(dead_code)]
impl LazyAiGoalHandlers {
#[must_use]
pub fn register(&mut self, goal: Box<dyn AiGoal>) -> u32 {
let id = self.next_id;
self.next_id += 1;
self.handlers.insert(id, goal);
id
}
}

View File

@@ -0,0 +1,77 @@
use std::{
collections::BTreeMap,
sync::{
Mutex,
atomic::{AtomicU32, Ordering},
},
};
pub use crate::wit::pumpkin::plugin::command::Command;
use crate::{
Result, Server,
command::CommandNode,
wit::pumpkin::plugin::command::{CommandError, CommandSender, ConsumedArgs},
};
pub(crate) static NEXT_COMMAND_ID: AtomicU32 = AtomicU32::new(0);
pub(crate) static COMMAND_HANDLERS: Mutex<BTreeMap<u32, Box<dyn CommandHandler>>> =
Mutex::new(BTreeMap::new());
/// Handles the execution of a registered command.
///
/// Implement this trait to define the logic that runs when a command is invoked.
/// The return value is the exit code passed back to the server; return `Ok(0)` for
/// success or an [`Err`] variant to report a failure message to the sender.
pub trait CommandHandler: Send + Sync {
/// Executes the command.
///
/// # Arguments
/// - `sender` — who invoked the command (player or console).
/// - `server` — handle to the server.
/// - `args` — the parsed argument map for this command invocation.
fn handle(
&self,
sender: CommandSender,
server: Server,
args: ConsumedArgs,
) -> Result<i32, CommandError>;
}
impl Command {
/// Attaches an execution handler to this command.
///
/// Registers `handler` so that it is called whenever this command is invoked.
/// Returns `self` to allow builder-style chaining.
pub fn execute<H: CommandHandler + Send + Sync + 'static>(self, handler: H) -> Self {
let id = NEXT_COMMAND_ID.fetch_add(1, Ordering::Relaxed);
COMMAND_HANDLERS
.lock()
.unwrap()
.insert(id, Box::new(handler));
self.execute_with_handler_id(id);
self
}
}
impl CommandNode {
/// Attaches an execution handler to this command node.
///
/// Registers `handler` so that it is called when this specific node (subcommand
/// or argument branch) is the final node matched during command dispatch.
/// Returns `self` to allow builder-style chaining.
pub fn execute<H: CommandHandler + Send + Sync + 'static>(self, handler: H) -> Self {
let id = NEXT_COMMAND_ID.fetch_add(1, Ordering::Relaxed);
COMMAND_HANDLERS
.lock()
.unwrap()
.insert(id, Box::new(handler));
self.execute_with_handler_id(id);
self
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{BlockBreakEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a block is broken.
///
/// The associated [`BlockBreakEventData`] contains the player (if any), the block
/// identifier, its position, the experience to drop, and whether the block should
/// drop items. This event is cancellable.
pub struct BlockBreakEvent;
impl FromIntoEvent for BlockBreakEvent {
const EVENT_TYPE: EventType = EventType::BlockBreakEvent;
type Data = BlockBreakEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockBreakEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockBreakEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{BlockBurnEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a block is destroyed by fire.
///
/// The associated [`BlockBurnEventData`] contains the block that caught fire and
/// the igniting block that caused it to burn. This event is cancellable.
pub struct BlockBurnEvent;
impl FromIntoEvent for BlockBurnEvent {
const EVENT_TYPE: EventType = EventType::BlockBurnEvent;
type Data = BlockBurnEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockBurnEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockBurnEvent(data)
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{BlockCanBuildEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a player attempts to place a block, checking if it can be built.
///
/// The associated [`BlockCanBuildEventData`] contains the player, the block being
/// placed, the block it is being placed against, and a `buildable` flag that can be
/// overridden. This event is cancellable.
pub struct BlockCanBuildEvent;
impl FromIntoEvent for BlockCanBuildEvent {
const EVENT_TYPE: EventType = EventType::BlockCanBuildEvent;
type Data = BlockCanBuildEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockCanBuildEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockCanBuildEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{BlockDamageEventData, Event, EventType};
/// Event triggered when a block is damaged by a player.
pub struct BlockDamageEvent;
impl FromIntoEvent for BlockDamageEvent {
const EVENT_TYPE: EventType = EventType::BlockDamageEvent;
type Data = BlockDamageEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockDamageEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockDamageEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{BlockFadeEventData, Event, EventType};
/// Event triggered when a block fades or melts away.
pub struct BlockFadeEvent;
impl FromIntoEvent for BlockFadeEvent {
const EVENT_TYPE: EventType = EventType::BlockFadeEvent;
type Data = BlockFadeEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockFadeEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockFadeEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{BlockFormEventData, Event, EventType};
/// Event triggered when a block is formed.
pub struct BlockFormEvent;
impl FromIntoEvent for BlockFormEvent {
const EVENT_TYPE: EventType = EventType::BlockFormEvent;
type Data = BlockFormEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockFormEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockFormEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{BlockFromToEventData, Event, EventType};
/// Event triggered when a fluid flows from one block position to another.
pub struct BlockFromToEvent;
impl FromIntoEvent for BlockFromToEvent {
const EVENT_TYPE: EventType = EventType::BlockFromToEvent;
type Data = BlockFromToEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockFromToEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockFromToEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{BlockGrowEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a block grows or changes state naturally (e.g. crops, saplings).
///
/// The associated [`BlockGrowEventData`] contains the world, the old and new block
/// identifiers with their state IDs, and the block position. This event is cancellable.
pub struct BlockGrowEvent;
impl FromIntoEvent for BlockGrowEvent {
const EVENT_TYPE: EventType = EventType::BlockGrowEvent;
type Data = BlockGrowEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockGrowEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockGrowEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{BlockIgniteEventData, Event, EventType};
/// Event triggered when a block is ignited.
pub struct BlockIgniteEvent;
impl FromIntoEvent for BlockIgniteEvent {
const EVENT_TYPE: EventType = EventType::BlockIgniteEvent;
type Data = BlockIgniteEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockIgniteEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockIgniteEvent(data)
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{BlockPlaceEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a player places a block.
///
/// The associated [`BlockPlaceEventData`] contains the player, the block being placed,
/// the block it is placed against, the position, and a `can-build` flag. This event
/// is cancellable.
pub struct BlockPlaceEvent;
impl FromIntoEvent for BlockPlaceEvent {
const EVENT_TYPE: EventType = EventType::BlockPlaceEvent;
type Data = BlockPlaceEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockPlaceEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockPlaceEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{BlockRedstoneEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a redstone component changes its power level.
///
/// The associated [`BlockRedstoneEventData`] contains the world, the block state ID,
/// the block position, and the old and new current values. This event is cancellable.
pub struct BlockRedstoneEvent;
impl FromIntoEvent for BlockRedstoneEvent {
const EVENT_TYPE: EventType = EventType::BlockRedstoneEvent;
type Data = BlockRedstoneEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BlockRedstoneEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BlockRedstoneEvent(data)
}
}

View File

@@ -0,0 +1,34 @@
/// Block break event.
pub mod block_break;
/// Block burn event.
pub mod block_burn;
/// Block can build check event.
pub mod block_can_build;
/// Block damage event.
pub mod block_damage;
/// Block fade event.
pub mod block_fade;
/// Block form event.
pub mod block_form;
/// Block fluid flow event.
pub mod block_from_to;
/// Block growth event.
pub mod block_grow;
/// Block ignite event.
pub mod block_ignite;
/// Block place event.
pub mod block_place;
/// Block redstone signal event.
pub mod block_redstone;
pub use block_break::*;
pub use block_burn::*;
pub use block_can_build::*;
pub use block_damage::*;
pub use block_fade::*;
pub use block_form::*;
pub use block_from_to::*;
pub use block_grow::*;
pub use block_ignite::*;
pub use block_place::*;
pub use block_redstone::*;

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{EntityCombustEventData, Event, EventType};
use super::super::FromIntoEvent;
/// Event triggered when an entity catches fire.
pub struct EntityCombustEvent;
impl FromIntoEvent for EntityCombustEvent {
const EVENT_TYPE: EventType = EventType::EntityCombustEvent;
type Data = EntityCombustEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::EntityCombustEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::EntityCombustEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{EntityDamageEventData, Event, EventType};
use super::super::FromIntoEvent;
/// Event triggered when an entity receives damage.
pub struct EntityDamageEvent;
impl FromIntoEvent for EntityDamageEvent {
const EVENT_TYPE: EventType = EventType::EntityDamageEvent;
type Data = EntityDamageEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::EntityDamageEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::EntityDamageEvent(data)
}
}

View File

@@ -0,0 +1,41 @@
use crate::wit::pumpkin::plugin::event::{
EntityDeathEventData, Event, EventType, PlayerDeathEventData,
};
use super::super::FromIntoEvent;
/// Event triggered when an entity dies.
pub struct EntityDeathEvent;
impl FromIntoEvent for EntityDeathEvent {
const EVENT_TYPE: EventType = EventType::EntityDeathEvent;
type Data = EntityDeathEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::EntityDeathEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::EntityDeathEvent(data)
}
}
/// Event triggered when a player dies.
pub struct PlayerDeathEvent;
impl FromIntoEvent for PlayerDeathEvent {
const EVENT_TYPE: EventType = EventType::PlayerDeathEvent;
type Data = PlayerDeathEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerDeathEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerDeathEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{EntityRegainHealthEventData, Event, EventType};
use super::super::FromIntoEvent;
/// Event triggered when an entity regains health.
pub struct EntityRegainHealthEvent;
impl FromIntoEvent for EntityRegainHealthEvent {
const EVENT_TYPE: EventType = EventType::EntityRegainHealthEvent;
type Data = EntityRegainHealthEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::EntityRegainHealthEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::EntityRegainHealthEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{EntitySpawnEventData, Event, EventType};
use super::super::FromIntoEvent;
/// Event triggered when an entity spawns in the world.
pub struct EntitySpawnEvent;
impl FromIntoEvent for EntitySpawnEvent {
const EVENT_TYPE: EventType = EventType::EntitySpawnEvent;
type Data = EntitySpawnEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::EntitySpawnEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::EntitySpawnEvent(data)
}
}

View File

@@ -0,0 +1,16 @@
/// 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 health regeneration event.
pub mod entity_regain_health;
/// Entity spawn event.
pub mod entity_spawn;
pub use entity_combust::*;
pub use entity_damage::*;
pub use entity_death::*;
pub use entity_regain_health::*;
pub use entity_spawn::*;

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{CraftItemEventData, Event, EventType};
/// Event triggered when an item is crafted.
pub struct CraftItemEvent;
impl FromIntoEvent for CraftItemEvent {
const EVENT_TYPE: EventType = EventType::CraftItemEvent;
type Data = CraftItemEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::CraftItemEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::CraftItemEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{Event, EventType, FurnaceSmeltEventData};
/// Event triggered when an item is smelted in a furnace.
pub struct FurnaceSmeltEvent;
impl FromIntoEvent for FurnaceSmeltEvent {
const EVENT_TYPE: EventType = EventType::FurnaceSmeltEvent;
type Data = FurnaceSmeltEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::FurnaceSmeltEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::FurnaceSmeltEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{Event, EventType, InventoryDragEventData};
/// Event triggered when items are dragged in an inventory.
pub struct InventoryDragEvent;
impl FromIntoEvent for InventoryDragEvent {
const EVENT_TYPE: EventType = EventType::InventoryDragEvent;
type Data = InventoryDragEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::InventoryDragEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::InventoryDragEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{Event, EventType, InventoryOpenEventData};
/// Event triggered when an inventory container is opened.
pub struct InventoryOpenEvent;
impl FromIntoEvent for InventoryOpenEvent {
const EVENT_TYPE: EventType = EventType::InventoryOpenEvent;
type Data = InventoryOpenEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::InventoryOpenEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::InventoryOpenEvent(data)
}
}

View File

@@ -0,0 +1,13 @@
/// Craft item event.
pub mod craft_item;
/// Furnace smelt event.
pub mod furnace_smelt;
/// Inventory drag event.
pub mod inventory_drag;
/// Inventory open event.
pub mod inventory_open;
pub use craft_item::*;
pub use furnace_smelt::*;
pub use inventory_drag::*;
pub use inventory_open::*;

View File

@@ -0,0 +1,127 @@
use std::{
collections::BTreeMap,
marker::PhantomData,
pin::Pin,
sync::{
Mutex,
atomic::{AtomicU32, Ordering},
},
};
pub use crate::wit::pumpkin::plugin::event::{
BedrockClientboundPacket, BedrockServerboundPacket, ClientboundPacket, Event, EventPriority,
InteractAction, JavaClientboundPacket, JavaServerboundPacket, ServerboundPacket,
};
use crate::{Context, Result, Server, wit::pumpkin::plugin::event::EventType};
/// Block events.
pub mod block;
/// Entity events.
pub mod entity;
/// Inventory events.
pub mod inventory;
/// Network packet events.
pub mod packet;
/// Player events.
pub mod player;
/// Server lifecycle events.
pub mod server;
/// World events.
pub mod world;
pub use block::*;
pub use entity::*;
pub use inventory::*;
pub use packet::*;
pub use player::*;
pub use server::*;
pub use world::*;
pub(crate) static NEXT_HANDLER_ID: AtomicU32 = AtomicU32::new(0);
pub(crate) static EVENT_HANDLERS: Mutex<BTreeMap<u32, Box<dyn ErasedEventHandler>>> =
Mutex::new(BTreeMap::new());
/// Connects an event marker type to its WIT-generated data type and [`EventType`] discriminant.
///
/// Implement this trait for a unit struct to define a new event. The [`EventType`] constant
/// tells the host which event to subscribe to, while `data_from_event` and `data_into_event`
/// provide the conversions between the opaque [`Event`] variant and the concrete data type.
pub trait FromIntoEvent: Sized {
/// The discriminant used by the host to identify this event.
const EVENT_TYPE: EventType;
/// The WIT-generated data record carried by this event.
type Data;
/// Extracts the event data from an [`Event`] variant.
///
/// # Panics
/// Panics if the [`Event`] variant does not match [`Self::EVENT_TYPE`].
fn data_from_event(event: Event) -> Self::Data;
/// Wraps event data back into the corresponding [`Event`] variant.
fn data_into_event(data: Self::Data) -> Event;
}
/// A convenience alias for the data type associated with an event.
pub type EventData<E> = <E as FromIntoEvent>::Data;
/// A type alias for a pinned, boxed, dynamically-dispatched future.
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
/// A handler for a specific event type.
///
/// Implement this trait to process an event. The `handle` method receives the server
/// handle and the event data, and returns the (potentially modified) event data.
pub trait EventHandler<E: FromIntoEvent> {
/// Processes the event and returns the (potentially modified) data.
fn handle(&self, server: Server, event: E::Data) -> E::Data;
}
pub(crate) trait ErasedEventHandler: Send + Sync {
fn handle_erased(&self, server: Server, event: Event) -> Event;
}
struct HandlerWrapper<E: FromIntoEvent, H> {
handler: H,
_phantom: PhantomData<E>,
}
impl<E: FromIntoEvent + Send + Sync, H: EventHandler<E> + Send + Sync> ErasedEventHandler
for HandlerWrapper<E, H>
{
fn handle_erased(&self, server: Server, event: Event) -> Event {
let data = E::data_from_event(event);
let result = self.handler.handle(server, data);
E::data_into_event(result)
}
}
impl Context {
/// Registers an event handler with the plugin.
///
/// The handler must implement the [`EventHandler`] trait.
/// If the event is blocking, returning an event from the handler will modify the event.
pub fn register_event_handler<
E: FromIntoEvent + Send + Sync + 'static,
H: EventHandler<E> + Send + Sync + 'static,
>(
&self,
handler: H,
event_priority: EventPriority,
blocking: bool,
) -> Result<u32> {
let id = NEXT_HANDLER_ID.fetch_add(1, Ordering::Relaxed);
let wrapped = HandlerWrapper {
handler,
_phantom: PhantomData::<E>,
};
EVENT_HANDLERS
.lock()
.map_err(|e| e.to_string())?
.insert(id, Box::new(wrapped));
self.register_event(id, E::EVENT_TYPE, event_priority, blocking);
Ok(id)
}
}

View File

@@ -0,0 +1,42 @@
use super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{
Event, EventType, PacketReceivedEventData, PacketSentEventData,
};
/// An event fired when a packet is received from a client
pub struct PacketReceivedEvent;
impl FromIntoEvent for PacketReceivedEvent {
const EVENT_TYPE: EventType = EventType::PacketReceivedEvent;
type Data = PacketReceivedEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PacketReceivedEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PacketReceivedEvent(data)
}
}
/// An event fired when a packet is sent to a client
pub struct PacketSentEvent;
impl FromIntoEvent for PacketSentEvent {
const EVENT_TYPE: EventType = EventType::PacketSentEvent;
type Data = PacketSentEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PacketSentEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PacketSentEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{BedrockFormResponseEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a player responds to a Bedrock custom form.
pub struct BedrockFormResponseEvent;
impl FromIntoEvent for BedrockFormResponseEvent {
const EVENT_TYPE: EventType = EventType::BedrockFormResponseEvent;
type Data = BedrockFormResponseEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::BedrockFormResponseEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::BedrockFormResponseEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerChangedMainHandEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player changes their main hand in the settings.
///
/// The associated [`PlayerChangedMainHandEventData`] contains the player and their
/// newly selected main hand.
pub struct PlayerChangedMainHandEvent;
impl FromIntoEvent for PlayerChangedMainHandEvent {
const EVENT_TYPE: EventType = EventType::PlayerChangedMainHandEvent;
type Data = PlayerChangedMainHandEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerChangedMainHandEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerChangedMainHandEvent(data)
}
}

View File

@@ -0,0 +1,22 @@
use crate::wit::pumpkin::plugin::event::{CustomClickActionEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a player clicks a custom dialog button.
pub struct CustomClickActionEvent;
impl FromIntoEvent for CustomClickActionEvent {
const EVENT_TYPE: EventType = EventType::CustomClickActionEvent;
type Data = CustomClickActionEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::CustomClickActionEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::CustomClickActionEvent(data)
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerEggThrowEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a thrown egg resolves.
///
/// The associated [`PlayerEggThrowEventData`] contains the player, the egg entity UUID,
/// whether the egg hatched, how many entities hatched, and the entity type to hatch.
/// This event is cancellable.
pub struct PlayerEggThrowEvent;
impl FromIntoEvent for PlayerEggThrowEvent {
const EVENT_TYPE: EventType = EventType::PlayerEggThrowEvent;
type Data = PlayerEggThrowEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerEggThrowEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerEggThrowEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerExpChangeEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player's experience changes.
///
/// The associated [`PlayerExpChangeEventData`] contains the player and the amount of
/// experience being added (can be negative).
pub struct PlayerExpChangeEvent;
impl FromIntoEvent for PlayerExpChangeEvent {
const EVENT_TYPE: EventType = EventType::PlayerExpChangeEvent;
type Data = PlayerExpChangeEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerExpChangeEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerExpChangeEvent(data)
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerFishEventData};
use super::super::FromIntoEvent;
/// An event that occurs during a fishing action.
///
/// The associated [`PlayerFishEventData`] contains the player, the fishing state,
/// the hand used, the hook entity, optionally the caught entity, and the experience
/// to drop. This event is cancellable.
pub struct PlayerFishEvent;
impl FromIntoEvent for PlayerFishEvent {
const EVENT_TYPE: EventType = EventType::PlayerFishEvent;
type Data = PlayerFishEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerFishEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerFishEvent(data)
}
}

View File

@@ -0,0 +1,22 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, InventoryClickEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player clicks a slot in an inventory.
pub struct InventoryClickEvent;
impl FromIntoEvent for InventoryClickEvent {
const EVENT_TYPE: EventType = EventType::InventoryClickEvent;
type Data = InventoryClickEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::InventoryClickEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::InventoryClickEvent(data)
}
}

View File

@@ -0,0 +1,22 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, InventoryCloseEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player closes an inventory.
pub struct InventoryCloseEvent;
impl FromIntoEvent for InventoryCloseEvent {
const EVENT_TYPE: EventType = EventType::InventoryCloseEvent;
type Data = InventoryCloseEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::InventoryCloseEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::InventoryCloseEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerItemHeldEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player changes the selected hotbar slot.
///
/// The associated [`PlayerItemHeldEventData`] contains the player, the previous slot index,
/// and the new slot index. This event is cancellable.
pub struct PlayerItemHeldEvent;
impl FromIntoEvent for PlayerItemHeldEvent {
const EVENT_TYPE: EventType = EventType::PlayerItemHeldEvent;
type Data = PlayerItemHeldEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerItemHeldEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerItemHeldEvent(data)
}
}

View File

@@ -0,0 +1,97 @@
/// Bedrock form response event.
pub mod bedrock_form_response;
/// Player main hand change event.
pub mod changed_main_hand;
/// Custom inventory click action event.
pub mod custom_click_action;
/// Egg throw event.
pub mod egg_throw;
/// Experience change event.
pub mod exp_change;
/// Player fish event.
pub mod fish;
/// Inventory click event.
pub mod inventory_click;
/// Inventory close event.
pub mod inventory_close;
/// Item held slot change event.
pub mod item_held;
/// Player bed enter and leave events.
pub mod player_bed;
/// Player bucket empty and fill events.
pub mod player_bucket;
/// Player world change event.
pub mod player_change_world;
/// Player chat event.
pub mod player_chat;
/// Player command send event.
pub mod player_command_send;
/// Player custom payload event.
pub mod player_custom_payload;
/// Player drop item event.
pub mod player_drop_item;
/// Player gamemode change event.
pub mod player_gamemode_change;
/// Player interact block event.
pub mod player_interact;
/// Player interact entity event.
pub mod player_interact_entity;
/// Player interact unknown entity event.
pub mod player_interact_unknown_entity;
/// Player item consume event.
pub mod player_item_consume;
/// Player item damage event.
pub mod player_item_damage;
/// Player join event.
pub mod player_join;
/// Player leave event.
pub mod player_leave;
/// Player login event.
pub mod player_login;
/// Player move event.
pub mod player_move;
/// Player permission check event.
pub mod player_permission_check;
/// Player respawn event.
pub mod player_respawn;
/// Player teleport event.
pub mod player_teleport;
/// Player toggle flight event.
pub mod player_toggle_flight;
/// Player toggle sneak event.
pub mod player_toggle_sneak;
/// Player toggle sprint event.
pub mod player_toggle_sprint;
pub use bedrock_form_response::*;
pub use changed_main_hand::*;
pub use custom_click_action::*;
pub use egg_throw::*;
pub use exp_change::*;
pub use fish::*;
pub use inventory_click::*;
pub use inventory_close::*;
pub use item_held::*;
pub use player_bed::*;
pub use player_bucket::*;
pub use player_change_world::*;
pub use player_chat::*;
pub use player_command_send::*;
pub use player_custom_payload::*;
pub use player_drop_item::*;
pub use player_gamemode_change::*;
pub use player_interact::*;
pub use player_interact_entity::*;
pub use player_interact_unknown_entity::*;
pub use player_item_consume::*;
pub use player_item_damage::*;
pub use player_join::*;
pub use player_leave::*;
pub use player_login::*;
pub use player_move::*;
pub use player_permission_check::*;
pub use player_respawn::*;
pub use player_teleport::*;
pub use player_toggle_flight::*;
pub use player_toggle_sneak::*;
pub use player_toggle_sprint::*;

View File

@@ -0,0 +1,40 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{
Event, EventType, PlayerBedEnterEventData, PlayerBedLeaveEventData,
};
/// Event triggered when a player enters a bed.
pub struct PlayerBedEnterEvent;
impl FromIntoEvent for PlayerBedEnterEvent {
const EVENT_TYPE: EventType = EventType::PlayerBedEnterEvent;
type Data = PlayerBedEnterEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerBedEnterEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerBedEnterEvent(data)
}
}
/// Event triggered when a player leaves a bed.
pub struct PlayerBedLeaveEvent;
impl FromIntoEvent for PlayerBedLeaveEvent {
const EVENT_TYPE: EventType = EventType::PlayerBedLeaveEvent;
type Data = PlayerBedLeaveEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerBedLeaveEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerBedLeaveEvent(data)
}
}

View File

@@ -0,0 +1,40 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{
Event, EventType, PlayerBucketEmptyEventData, PlayerBucketFillEventData,
};
/// Event triggered when a player empties a bucket.
pub struct PlayerBucketEmptyEvent;
impl FromIntoEvent for PlayerBucketEmptyEvent {
const EVENT_TYPE: EventType = EventType::PlayerBucketEmptyEvent;
type Data = PlayerBucketEmptyEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerBucketEmptyEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerBucketEmptyEvent(data)
}
}
/// Event triggered when a player fills a bucket.
pub struct PlayerBucketFillEvent;
impl FromIntoEvent for PlayerBucketFillEvent {
const EVENT_TYPE: EventType = EventType::PlayerBucketFillEvent;
type Data = PlayerBucketFillEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerBucketFillEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerBucketFillEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerChangeWorldEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player changes worlds.
///
/// The associated [`PlayerChangeWorldEventData`] contains the player, the previous world,
/// the new world, and the destination position, yaw, and pitch. This event is cancellable.
pub struct PlayerChangeWorldEvent;
impl FromIntoEvent for PlayerChangeWorldEvent {
const EVENT_TYPE: EventType = EventType::PlayerChangeWorldEvent;
type Data = PlayerChangeWorldEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerChangeWorldEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerChangeWorldEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerChatEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player sends a chat message.
///
/// The associated [`PlayerChatEventData`] contains the player, the message, and
/// the list of recipients. The message can be modified. This event is cancellable.
pub struct PlayerChatEvent;
impl FromIntoEvent for PlayerChatEvent {
const EVENT_TYPE: EventType = EventType::PlayerChatEvent;
type Data = PlayerChatEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerChatEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerChatEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerCommandSendEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player sends a command.
///
/// The associated [`PlayerCommandSendEventData`] contains the player and the command
/// string (without the leading `/`). This event is cancellable.
pub struct PlayerCommandSendEvent;
impl FromIntoEvent for PlayerCommandSendEvent {
const EVENT_TYPE: EventType = EventType::PlayerCommandSendEvent;
type Data = PlayerCommandSendEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerCommandSendEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerCommandSendEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerCustomPayloadEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player sends a custom plugin channel payload.
///
/// The associated [`PlayerCustomPayloadEventData`] contains the player, the channel
/// identifier, and the raw payload bytes.
pub struct PlayerCustomPayloadEvent;
impl FromIntoEvent for PlayerCustomPayloadEvent {
const EVENT_TYPE: EventType = EventType::PlayerCustomPayloadEvent;
type Data = PlayerCustomPayloadEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerCustomPayloadEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerCustomPayloadEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerDropItemEventData};
/// Event triggered when a player drops an item.
pub struct PlayerDropItemEvent;
impl FromIntoEvent for PlayerDropItemEvent {
const EVENT_TYPE: EventType = EventType::PlayerDropItemEvent;
type Data = PlayerDropItemEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerDropItemEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerDropItemEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerGamemodeChangeEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player's game mode changes.
///
/// The associated [`PlayerGamemodeChangeEventData`] contains the player, the previous
/// game mode, and the new game mode. This event is cancellable.
pub struct PlayerGamemodeChangeEvent;
impl FromIntoEvent for PlayerGamemodeChangeEvent {
const EVENT_TYPE: EventType = EventType::PlayerGamemodeChangeEvent;
type Data = PlayerGamemodeChangeEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerGamemodeChangeEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerGamemodeChangeEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerInteractEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player interacts.
pub struct PlayerInteractEvent;
impl FromIntoEvent for PlayerInteractEvent {
const EVENT_TYPE: EventType = EventType::PlayerInteractEvent;
type Data = PlayerInteractEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerInteractEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerInteractEvent(data)
}
}

View File

@@ -0,0 +1,22 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerInteractEntityEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player interacts with an entity.
pub struct PlayerInteractEntityEvent;
impl FromIntoEvent for PlayerInteractEntityEvent {
const EVENT_TYPE: EventType = EventType::PlayerInteractEntityEvent;
type Data = PlayerInteractEntityEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerInteractEntityEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerInteractEntityEvent(data)
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerInteractUnknownEntityEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player interacts with an entity ID that the server cannot resolve.
///
/// The associated [`PlayerInteractUnknownEntityEventData`] contains the player, the unknown
/// entity ID, and the attempted interaction action. This event is cancellable.
pub struct PlayerInteractUnknownEntityEvent;
impl FromIntoEvent for PlayerInteractUnknownEntityEvent {
const EVENT_TYPE: EventType = EventType::PlayerInteractUnknownEntityEvent;
type Data = PlayerInteractUnknownEntityEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerInteractUnknownEntityEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerInteractUnknownEntityEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerItemConsumeEventData};
/// Event triggered when a player consumes an item.
pub struct PlayerItemConsumeEvent;
impl FromIntoEvent for PlayerItemConsumeEvent {
const EVENT_TYPE: EventType = EventType::PlayerItemConsumeEvent;
type Data = PlayerItemConsumeEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerItemConsumeEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerItemConsumeEvent(data)
}
}

View File

@@ -0,0 +1,20 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerItemDamageEventData};
/// Event triggered when a player's held item receives damage.
pub struct PlayerItemDamageEvent;
impl FromIntoEvent for PlayerItemDamageEvent {
const EVENT_TYPE: EventType = EventType::PlayerItemDamageEvent;
type Data = PlayerItemDamageEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerItemDamageEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerItemDamageEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerJoinEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player joins the server.
///
/// The associated [`PlayerJoinEventData`] contains the player and a join message
/// that can be modified or suppressed. This event is cancellable.
pub struct PlayerJoinEvent;
impl FromIntoEvent for PlayerJoinEvent {
const EVENT_TYPE: EventType = EventType::PlayerJoinEvent;
type Data = PlayerJoinEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerJoinEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerJoinEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerLeaveEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player leaves the server.
///
/// The associated [`PlayerLeaveEventData`] contains the player and a leave message
/// that can be modified or suppressed. This event is cancellable.
pub struct PlayerLeaveEvent;
impl FromIntoEvent for PlayerLeaveEvent {
const EVENT_TYPE: EventType = EventType::PlayerLeaveEvent;
type Data = PlayerLeaveEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerLeaveEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerLeaveEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerLoginEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player attempts to log in to the server.
///
/// The associated [`PlayerLoginEventData`] contains the player and a kick message
/// used if the login is cancelled. This event is cancellable.
pub struct PlayerLoginEvent;
impl FromIntoEvent for PlayerLoginEvent {
const EVENT_TYPE: EventType = EventType::PlayerLoginEvent;
type Data = PlayerLoginEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerLoginEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerLoginEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerMoveEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player moves.
///
/// The associated [`PlayerMoveEventData`] contains the player, the position moved from,
/// and the position moved to. This event is cancellable.
pub struct PlayerMoveEvent;
impl FromIntoEvent for PlayerMoveEvent {
const EVENT_TYPE: EventType = EventType::PlayerMoveEvent;
type Data = PlayerMoveEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerMoveEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerMoveEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerPermissionCheckEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a permission check is performed for a player.
///
/// The associated [`PlayerPermissionCheckEventData`] contains the player, the permission
/// node being checked, and the current result which can be overridden.
pub struct PlayerPermissionCheckEvent;
impl FromIntoEvent for PlayerPermissionCheckEvent {
const EVENT_TYPE: EventType = EventType::PlayerPermissionCheckEvent;
type Data = PlayerPermissionCheckEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerPermissionCheckEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerPermissionCheckEvent(data)
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerRespawnEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player respawns.
///
/// The associated [`PlayerRespawnEventData`] contains the player, the world they
/// respawned from, the world they respawned into, and the destination position,
/// yaw and pitch. This event is not cancellable.
pub struct PlayerRespawnEvent;
impl FromIntoEvent for PlayerRespawnEvent {
const EVENT_TYPE: EventType = EventType::PlayerRespawnEvent;
type Data = PlayerRespawnEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerRespawnEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerRespawnEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerTeleportEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player is teleported.
///
/// The associated [`PlayerTeleportEventData`] contains the player, the origin position,
/// and the destination position. This event is cancellable.
pub struct PlayerTeleportEvent;
impl FromIntoEvent for PlayerTeleportEvent {
const EVENT_TYPE: EventType = EventType::PlayerTeleportEvent;
type Data = PlayerTeleportEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerTeleportEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerTeleportEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerToggleFlightEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player toggles flight.
pub struct PlayerToggleFlightEvent;
impl FromIntoEvent for PlayerToggleFlightEvent {
const EVENT_TYPE: EventType = EventType::PlayerToggleFlightEvent;
type Data = PlayerToggleFlightEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerToggleFlightEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerToggleFlightEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerToggleSneakEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player toggles sneak.
pub struct PlayerToggleSneakEvent;
impl FromIntoEvent for PlayerToggleSneakEvent {
const EVENT_TYPE: EventType = EventType::PlayerToggleSneakEvent;
type Data = PlayerToggleSneakEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerToggleSneakEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerToggleSneakEvent(data)
}
}

View File

@@ -0,0 +1,21 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, PlayerToggleSprintEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a player toggles sprint.
pub struct PlayerToggleSprintEvent;
impl FromIntoEvent for PlayerToggleSprintEvent {
const EVENT_TYPE: EventType = EventType::PlayerToggleSprintEvent;
type Data = PlayerToggleSprintEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::PlayerToggleSprintEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::PlayerToggleSprintEvent(data)
}
}

View File

@@ -0,0 +1,19 @@
/// Server broadcast event.
pub mod server_broadcast;
/// Server command execution event.
pub mod server_command;
/// Server initialization load event.
pub mod server_load;
/// Server tick completion event.
pub mod server_tick_end;
/// Server tick start event.
pub mod server_tick_start;
/// Server spawn point change event.
pub mod spawn_change;
pub use server_broadcast::*;
pub use server_command::*;
pub use server_load::*;
pub use server_tick_end::*;
pub use server_tick_start::*;
pub use spawn_change::*;

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, ServerBroadcastEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a message is broadcast to all players on the server.
///
/// The associated [`ServerBroadcastEventData`] contains the message and the sender.
/// This event is cancellable.
pub struct ServerBroadcastEvent;
impl FromIntoEvent for ServerBroadcastEvent {
const EVENT_TYPE: EventType = EventType::ServerBroadcastEvent;
type Data = ServerBroadcastEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ServerBroadcastEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ServerBroadcastEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, ServerCommandEventData};
use super::super::FromIntoEvent;
/// An event that occurs when a command is executed from the server console.
///
/// The associated [`ServerCommandEventData`] contains the command string.
/// This event is cancellable.
pub struct ServerCommandEvent;
impl FromIntoEvent for ServerCommandEvent {
const EVENT_TYPE: EventType = EventType::ServerCommandEvent;
type Data = ServerCommandEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ServerCommandEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ServerCommandEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, ServerLoadEventData};
use super::super::FromIntoEvent;
/// An event that fires once the server has finished loading.
///
/// The associated [`ServerLoadEventData`] contains the load reason:
/// startup or a full server reload.
pub struct ServerLoadEvent;
impl FromIntoEvent for ServerLoadEvent {
const EVENT_TYPE: EventType = EventType::ServerLoadEvent;
type Data = ServerLoadEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ServerLoadEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ServerLoadEvent(data)
}
}

View File

@@ -0,0 +1,28 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, ServerTickEndEventData};
use super::super::FromIntoEvent;
/// An event that fires at the end of every server tick.
///
/// The associated [`ServerTickEndEventData`] carries:
/// - `tick`: the 0-indexed number of the tick that just finished.
/// - `duration_nanos`: how long the tick took, measured from the start of
/// the ticker iteration to just after `Server::tick` returned.
///
/// This event is non-cancellable.
pub struct ServerTickEndEvent;
impl FromIntoEvent for ServerTickEndEvent {
const EVENT_TYPE: EventType = EventType::ServerTickEndEvent;
type Data = ServerTickEndEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ServerTickEndEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ServerTickEndEvent(data)
}
}

View File

@@ -0,0 +1,25 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, ServerTickStartEventData};
use super::super::FromIntoEvent;
/// An event that fires at the start of every server tick (~20 Hz under the
/// default tick rate).
///
/// The associated [`ServerTickStartEventData`] carries the 0-indexed `tick`
/// number of the tick about to run. This event is non-cancellable.
pub struct ServerTickStartEvent;
impl FromIntoEvent for ServerTickStartEvent {
const EVENT_TYPE: EventType = EventType::ServerTickStartEvent;
type Data = ServerTickStartEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ServerTickStartEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ServerTickStartEvent(data)
}
}

View File

@@ -0,0 +1,24 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, SpawnChangeEventData};
use super::super::FromIntoEvent;
/// An event that occurs when the world spawn point changes.
///
/// The associated [`SpawnChangeEventData`] contains the world, the previous spawn
/// position, yaw and pitch, and the new spawn position, yaw and pitch.
pub struct SpawnChangeEvent;
impl FromIntoEvent for SpawnChangeEvent {
const EVENT_TYPE: EventType = EventType::SpawnChangeEvent;
type Data = SpawnChangeEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::SpawnChangeEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::SpawnChangeEvent(data)
}
}

View File

@@ -0,0 +1,22 @@
use crate::wit::pumpkin::plugin::event::{ChunkLoadEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a chunk is loaded in a world.
pub struct ChunkLoadEvent;
impl FromIntoEvent for ChunkLoadEvent {
const EVENT_TYPE: EventType = EventType::ChunkLoadEvent;
type Data = ChunkLoadEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ChunkLoadEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ChunkLoadEvent(data)
}
}

View File

@@ -0,0 +1,22 @@
use crate::wit::pumpkin::plugin::event::{ChunkSaveEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a chunk is saved in a world.
pub struct ChunkSaveEvent;
impl FromIntoEvent for ChunkSaveEvent {
const EVENT_TYPE: EventType = EventType::ChunkSaveEvent;
type Data = ChunkSaveEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ChunkSaveEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ChunkSaveEvent(data)
}
}

View File

@@ -0,0 +1,22 @@
use crate::wit::pumpkin::plugin::event::{ChunkSendEventData, Event, EventType};
use super::super::FromIntoEvent;
/// An event that occurs when a chunk is sent to a client.
pub struct ChunkSendEvent;
impl FromIntoEvent for ChunkSendEvent {
const EVENT_TYPE: EventType = EventType::ChunkSendEvent;
type Data = ChunkSendEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ChunkSendEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ChunkSendEvent(data)
}
}

View File

@@ -0,0 +1,16 @@
/// Chunk load event.
pub mod chunk_load;
/// Chunk save event.
pub mod chunk_save;
/// Chunk send packet event.
pub mod chunk_send;
/// Weather and thunder change events.
pub mod weather_change;
/// World load and unload events.
pub mod world_load;
pub use chunk_load::*;
pub use chunk_save::*;
pub use chunk_send::*;
pub use weather_change::*;
pub use world_load::*;

View File

@@ -0,0 +1,40 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{
Event, EventType, ThunderChangeEventData, WeatherChangeEventData,
};
/// Event triggered when world weather changes.
pub struct WeatherChangeEvent;
impl FromIntoEvent for WeatherChangeEvent {
const EVENT_TYPE: EventType = EventType::WeatherChangeEvent;
type Data = WeatherChangeEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::WeatherChangeEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::WeatherChangeEvent(data)
}
}
/// Event triggered when thunderstorm state changes.
pub struct ThunderChangeEvent;
impl FromIntoEvent for ThunderChangeEvent {
const EVENT_TYPE: EventType = EventType::ThunderChangeEvent;
type Data = ThunderChangeEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ThunderChangeEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ThunderChangeEvent(data)
}
}

View File

@@ -0,0 +1,40 @@
use super::super::FromIntoEvent;
use crate::wit::pumpkin::plugin::event::{
Event, EventType, WorldLoadEventData, WorldUnloadEventData,
};
/// Event triggered when a world is loaded.
pub struct WorldLoadEvent;
impl FromIntoEvent for WorldLoadEvent {
const EVENT_TYPE: EventType = EventType::WorldLoadEvent;
type Data = WorldLoadEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::WorldLoadEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::WorldLoadEvent(data)
}
}
/// Event triggered when a world is unloaded.
pub struct WorldUnloadEvent;
impl FromIntoEvent for WorldUnloadEvent {
const EVENT_TYPE: EventType = EventType::WorldUnloadEvent;
type Data = WorldUnloadEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::WorldUnloadEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::WorldUnloadEvent(data)
}
}

View File

@@ -0,0 +1,3 @@
//! WIT-generated plugin type extensions belong in here. For example [Display](std::fmt::Display) implementations.
mod uuid;

View File

@@ -0,0 +1,32 @@
use std::fmt;
use crate::wit::pumpkin::plugin::uuid::Uuid;
impl fmt::Display for Uuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
(self.high >> 32) as u32,
((self.high >> 16) & 0xffff) as u16,
(self.high & 0xffff) as u16,
(self.low >> 48) as u16,
self.low & 0x0000_ffff_ffff_ffff,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn formats_uuid_correctly() {
let uuid = Uuid {
high: 0x0011223344556677,
low: 0x8899aabbccddeeff,
};
assert_eq!(uuid.to_string(), "00112233-4455-6677-8899-aabbccddeeff");
}
}

View File

@@ -0,0 +1,240 @@
use crate::text::TextComponent;
use crate::wit::pumpkin::plugin::forms::{
CustomForm, CustomFormElement, Form, FormImage, ImageType, ModalForm, SimpleForm,
SimpleFormButton,
};
/// Builder for creating a Bedrock simple form.
pub struct SimpleFormBuilder {
title: TextComponent,
content: TextComponent,
buttons: Vec<SimpleFormButton>,
}
impl SimpleFormBuilder {
/// Creates a new simple form builder with a title and main content text.
pub fn new(title: impl Into<TextComponent>, content: impl Into<TextComponent>) -> Self {
Self {
title: title.into(),
content: content.into(),
buttons: Vec::new(),
}
}
/// Adds a button to the simple form.
pub fn button(mut self, text: impl Into<TextComponent>, image: Option<FormImage>) -> Self {
self.buttons.push(SimpleFormButton {
text: text.into(),
image,
});
self
}
/// Builds the final simple form instance.
pub fn build(self) -> Form {
Form::Simple(SimpleForm {
title: self.title,
content: self.content,
buttons: self.buttons,
})
}
}
/// Builder for creating a Bedrock modal form (two-button dialog).
pub struct ModalFormBuilder {
title: TextComponent,
content: TextComponent,
button1: TextComponent,
button2: TextComponent,
}
impl ModalFormBuilder {
/// Creates a new modal form builder with a title and main content text.
pub fn new(title: impl Into<TextComponent>, content: impl Into<TextComponent>) -> Self {
Self {
title: title.into(),
content: content.into(),
button1: TextComponent::translate("gui.yes", vec![]),
button2: TextComponent::translate("gui.no", vec![]),
}
}
/// Sets the label for the first button (confirm).
pub fn button1(mut self, text: impl Into<TextComponent>) -> Self {
self.button1 = text.into();
self
}
/// Sets the label for the second button (cancel).
pub fn button2(mut self, text: impl Into<TextComponent>) -> Self {
self.button2 = text.into();
self
}
/// Builds the final modal form instance.
pub fn build(self) -> Form {
Form::Modal(ModalForm {
title: self.title,
content: self.content,
button1: self.button1,
button2: self.button2,
})
}
}
/// Builder for creating a Bedrock custom form with multiple input elements.
pub struct CustomFormBuilder {
title: TextComponent,
elements: Vec<CustomFormElement>,
}
impl CustomFormBuilder {
/// Creates a new custom form builder with a title.
pub fn new(title: impl Into<TextComponent>) -> Self {
Self {
title: title.into(),
elements: Vec::new(),
}
}
/// Adds a text label element to the form.
pub fn label(mut self, text: impl Into<TextComponent>) -> Self {
self.elements.push(CustomFormElement::Label(text.into()));
self
}
/// Adds a toggle (switch) element to the form.
pub fn toggle(mut self, text: impl Into<TextComponent>, default: bool) -> Self {
self.elements
.push(CustomFormElement::Toggle((text.into(), default)));
self
}
/// Adds a numeric slider element to the form.
pub fn slider(
mut self,
text: impl Into<TextComponent>,
min: f32,
max: f32,
step: f32,
default: f32,
) -> Self {
self.elements.push(CustomFormElement::Slider((
text.into(),
min,
max,
step,
default,
)));
self
}
/// Adds a step slider element to the form.
pub fn step_slider(
mut self,
text: impl Into<TextComponent>,
steps: Vec<String>,
default: u32,
) -> Self {
self.elements
.push(CustomFormElement::StepSlider((text.into(), steps, default)));
self
}
/// Adds a dropdown selector element to the form.
pub fn dropdown(
mut self,
text: impl Into<TextComponent>,
options: Vec<String>,
default: u32,
) -> Self {
self.elements
.push(CustomFormElement::Dropdown((text.into(), options, default)));
self
}
/// Adds a text input field element to the form.
pub fn input(
mut self,
text: impl Into<TextComponent>,
placeholder: impl Into<String>,
default: impl Into<String>,
) -> Self {
self.elements.push(CustomFormElement::Input((
text.into(),
placeholder.into(),
default.into(),
)));
self
}
/// Builds the final custom form instance.
pub fn build(self) -> Form {
Form::Custom(CustomForm {
title: self.title,
elements: self.elements,
})
}
}
/// Creates a `FormImage` pointing to an HTTP(S) URL.
pub fn url_image(url: impl Into<String>) -> FormImage {
FormImage {
type_: ImageType::Url,
data: url.into(),
}
}
/// Creates a `FormImage` pointing to a local file path.
pub fn path_image(path: impl Into<String>) -> FormImage {
FormImage {
type_: ImageType::Path,
data: path.into(),
}
}
/// Represents the response received from a submitted UI form.
pub enum FormResponse {
/// Response from a simple button form containing selected button index.
Simple(u32),
/// Response from a modal dialog containing boolean outcome.
Modal(bool),
/// Response from a custom form containing element values.
Custom(Vec<serde_json::Value>),
/// Form was closed by player without submission.
Closed,
}
impl FormResponse {
/// Parses a JSON response payload string into a `FormResponse`.
#[must_use]
pub fn parse(data: Option<String>) -> Self {
match data {
None => Self::Closed,
Some(s) => {
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&s) {
if val.is_u64() {
Self::Simple(val.as_u64().unwrap() as u32)
} else if val.is_boolean() {
Self::Modal(val.as_bool().unwrap())
} else if val.is_array() {
Self::Custom(val.as_array().unwrap().clone())
} else {
Self::Closed // Or some error state
}
} else {
// Fallback for some clients that might send raw strings for simple/modal
if s == "true" {
Self::Modal(true)
} else if s == "false" {
Self::Modal(false)
} else if let Ok(idx) = s.parse::<u32>() {
Self::Simple(idx)
} else {
Self::Closed
}
}
}
}
}
}

View File

@@ -0,0 +1,292 @@
//! Pumpkin plugin API.
#![warn(missing_docs)]
//!
//! This crate provides everything needed to write a Pumpkin server plugin compiled
//! to WebAssembly. A plugin consists of a type that implements [`Plugin`], registered
//! with the [`register_plugin!`] macro.
//!
//! # Quick start
//!
//! ```rust,ignore
//! use pumpkin_plugin_api::{Plugin, PluginMetadata, Context, register_plugin, permissions::permissions};
//!
//! struct MyPlugin;
//!
//! impl Plugin for MyPlugin {
//! fn new() -> Self { MyPlugin }
//! fn metadata(&self) -> PluginMetadata {
//! PluginMetadata {
//! name: "my-plugin".into(),
//! version: "0.1.0".into(),
//! authors: vec!["you".into()],
//! description: "An example plugin.".into(),
//! dependencies: vec![],
//! permissions: vec![permissions::NETWORK_DNS.into()],
//! }
//! }
//! }
//!
//! register_plugin!(MyPlugin);
//! ```
use crate::{
commands::COMMAND_HANDLERS, events::EVENT_HANDLERS, logging::WitSubscriber,
scheduler::TASK_HANDLERS, text::TextComponent,
};
/// Plugin command registration and handling utilities.
pub mod commands;
/// Event system and event handlers.
pub mod events;
mod ext;
/// Bedrock UI form builders.
pub mod forms;
/// Constants for plugin permissions.
///
/// Use these in your `PluginMetadata` to request access to specific host features.
pub mod permissions;
/// Scheduler utilities.
pub mod scheduler;
/// Command WIT API re-exports.
pub mod command {
pub use crate::wit::pumpkin::plugin::command::{
Arg, ArgumentType, Command, CommandError, CommandNode, CommandSender, ConsumedArgs,
StringType,
};
}
pub use wit::pumpkin::plugin::{
bedrock_packets, block_entity, boss_bar, command as command_wit, common,
context::{Context, Server},
data_components, entity,
entity_types::EntityType,
event::{self as events_wit, EventType},
gui, i18n, item_stack, java_dialogs, java_packets, particles, permission, player, scoreboard,
server, text, uuid, world,
};
// Convenience re-exports of commonly-used plugin types so plugin authors can
// name them directly (e.g. build an `ItemStack` for a GUI or `/give`).
pub use events::{EventHandler, FromIntoEvent};
pub use wit::pumpkin::plugin::item_stack::ItemStack;
/// Java dialog WIT API re-exports.
pub mod java_dialog {
pub use crate::wit::pumpkin::plugin::java_dialogs::{ActionButton, DialogBody, DialogType};
}
/// WIT-based logging subscriber.
pub mod logging;
#[allow(clippy::too_many_arguments, missing_docs)]
mod wit {
wit_bindgen::generate!({
skip: ["init-plugin"],
path: "../pumpkin-plugin-wit/v0.1",
world: "plugin",
enable_method_chaining: true
});
use super::Component;
export!(Component);
}
struct Component;
/// Metadata that describes a plugin to the server.
pub struct PluginMetadata {
/// The human-readable name of the plugin.
pub name: String,
/// The plugin's version string (e.g. `"1.0.0"`).
pub version: String,
/// The list of plugin authors.
pub authors: Vec<String>,
/// A short description of what the plugin does.
pub description: String,
/// The list of plugin dependencies.
pub dependencies: Vec<String>,
/// The list of permissions requested by the plugin.
pub permissions: Vec<String>,
}
impl wit::exports::pumpkin::plugin::metadata::Guest for Component {
/// Returns the plugin metadata to the host.
fn get_metadata() -> wit::exports::pumpkin::plugin::metadata::PluginMetadata {
let metadata = plugin().metadata();
wit::exports::pumpkin::plugin::metadata::PluginMetadata {
name: metadata.name,
version: metadata.version,
authors: metadata.authors,
description: metadata.description,
dependencies: metadata.dependencies,
permissions: metadata.permissions,
}
}
}
impl wit::Guest for Component {
/// WIT entry point — delegates to [`Plugin::on_load`].
fn on_load(context: Context) -> Result<(), String> {
plugin().on_load(context)
}
/// WIT entry point — delegates to [`Plugin::on_unload`].
fn on_unload(context: Context) -> Result<(), String> {
plugin().on_unload(context)
}
/// WIT entry point — dispatches an incoming event to the registered handler for `event_id`.
///
/// Returns the event unchanged if no handler is registered for the given id.
fn handle_event(event_id: u32, server: Server, event: events::Event) -> events::Event {
let handlers = EVENT_HANDLERS.lock().unwrap();
if let Some(handler) = handlers.get(&event_id) {
handler.handle_erased(server, event)
} else {
event
}
}
/// WIT entry point — dispatches an incoming command invocation to the registered handler for `command_id`.
///
/// Returns a [`CommandError`](command::CommandError) if no handler is registered for the given id.
fn handle_command(
command_id: u32,
sender: command::CommandSender,
server: Server,
args: command::ConsumedArgs,
) -> Result<i32, command::CommandError> {
let handlers = COMMAND_HANDLERS.lock().unwrap();
handlers.get(&command_id).map_or_else(
|| {
Err(command::CommandError::CommandFailed(TextComponent::text(
&format!("no handler registered for command id {command_id}"),
)))
},
|handler| handler.handle(sender, server, args),
)
}
/// WIT entry point — dispatches a scheduled task invocation to the registered handler for `handler_id`.
fn handle_task(handler_id: u32, server: Server) {
let mut handlers = TASK_HANDLERS.lock().unwrap();
handlers.handle(handler_id, server);
}
fn handle_ai_goal_can_start(goal_id: u32, server: Server, entity: entity::Entity) -> bool {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.can_start(server, entity)
} else {
false
}
}
fn handle_ai_goal_should_continue(
goal_id: u32,
server: Server,
entity: entity::Entity,
) -> bool {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.should_continue(server, entity)
} else {
false
}
}
fn handle_ai_goal_start(goal_id: u32, server: Server, entity: entity::Entity) {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.start(server, entity);
}
}
fn handle_ai_goal_tick(goal_id: u32, server: Server, entity: entity::Entity) {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.tick(server, entity);
}
}
fn handle_ai_goal_stop(goal_id: u32, server: Server, entity: entity::Entity) {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.stop(server, entity);
}
}
}
/// Convenience alias for `core::result::Result<T, String>` used throughout the plugin API.
pub type Result<T, E = String> = core::result::Result<T, E>;
/// The trait that every Pumpkin plugin must implement.
///
/// Use the [`register_plugin!`] macro to register your implementation with the runtime.
pub trait Plugin: Send + Sync {
/// Creates a new instance of the plugin.
///
/// Called once by the runtime before [`on_load`](Plugin::on_load).
fn new() -> Self
where
Self: Sized;
/// Returns the metadata for this plugin.
fn metadata(&self) -> PluginMetadata;
/// Called when the plugin is loaded by the server.
///
/// Use this to register event handlers, commands, and perform any setup work.
fn on_load(&mut self, _context: Context) -> Result<()> {
Ok(())
}
/// Called when the plugin is unloaded by the server.
///
/// Use this to clean up any resources acquired during [`on_load`](Plugin::on_load).
fn on_unload(&mut self, _context: Context) -> Result<()> {
Ok(())
}
}
#[doc(hidden)]
pub fn register_plugin(build_plugin: fn() -> Box<dyn Plugin>) {
let _ = tracing::subscriber::set_global_default(WitSubscriber::new());
unsafe { PLUGIN = Some(build_plugin()) }
}
/// Returns a mutable reference to the currently loaded plugin instance.
///
/// # Panics
/// If called before [`register_plugin`] has initialized `PLUGIN`.
fn plugin() -> &'static mut dyn Plugin {
#[expect(static_mut_refs)]
unsafe {
PLUGIN.as_deref_mut().unwrap()
}
}
/// The singleton plugin instance, initialised by [`register_plugin`].
static mut PLUGIN: Option<Box<dyn Plugin>> = None;
/// Registers the provided type as a Pumpkin plugin.
///
/// This macro generates the WebAssembly export entry point that the server uses to
/// instantiate the plugin. The type must implement the [`Plugin`] trait.
///
/// # Example
/// ```rust,ignore
/// register_plugin!(MyPlugin);
/// ```
#[macro_export]
macro_rules! register_plugin {
($plugin_type:ty) => {
#[unsafe(export_name = "init-plugin")]
pub extern "C" fn __init_plugin() {
$crate::register_plugin(|| Box::new(<$plugin_type as $crate::Plugin>::new()));
}
};
}
/// AI and mob goal utilities.
pub mod ai;

View File

@@ -0,0 +1,89 @@
use std::sync::atomic::{AtomicU64, Ordering};
use tracing_serde_structured::AsSerde;
use crate::wit;
/// A [`tracing::Subscriber`] that forwards events to the host server over WIT.
///
/// Installed automatically as the global subscriber when the plugin is loaded.
pub(crate) struct WitSubscriber {
next_id: AtomicU64,
}
impl WitSubscriber {
/// Creates a new `WitSubscriber` with the span ID counter starting at `1`.
pub const fn new() -> Self {
Self {
next_id: AtomicU64::new(1),
}
}
}
impl tracing::Subscriber for WitSubscriber {
/// Always returns `true` — all log levels are forwarded to the host.
fn enabled(&self, _metadata: &tracing::Metadata<'_>) -> bool {
true
}
/// Allocates a new monotonically increasing span ID.
fn new_span(&self, _attrs: &tracing::span::Attributes<'_>) -> tracing::span::Id {
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
tracing::span::Id::from_u64(id)
}
/// No-op — span field recording is not forwarded to the host.
fn record(&self, _span: &tracing::span::Id, _values: &tracing::span::Record<'_>) {}
/// No-op — causality links are not forwarded to the host.
fn record_follows_from(&self, _span: &tracing::span::Id, _follows: &tracing::span::Id) {}
/// Serialises the tracing event with `postcard` and sends it to the host via WIT.
fn event(&self, event: &tracing::Event<'_>) {
let serialized =
postcard::to_allocvec(&event.as_serde()).expect("failed to serialize tracing event");
wit::pumpkin::plugin::logging::log_tracing(&serialized);
}
/// No-op — span entry is not tracked.
fn enter(&self, _span: &tracing::span::Id) {}
/// No-op — span exit is not tracked.
fn exit(&self, _span: &tracing::span::Id) {}
}
/// The log severity level used with [`log`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
/// Very fine-grained diagnostic information.
Trace,
/// Diagnostic information useful during development.
Debug,
/// General informational messages.
Info,
/// Potentially harmful situations that do not stop execution.
Warn,
/// Errors that may require attention.
Error,
}
impl LogLevel {
/// Converts this level to the WIT-generated `Level` type expected by the host.
const fn to_wit(self) -> wit::pumpkin::plugin::logging::Level {
match self {
Self::Trace => wit::pumpkin::plugin::logging::Level::Trace,
Self::Debug => wit::pumpkin::plugin::logging::Level::Debug,
Self::Info => wit::pumpkin::plugin::logging::Level::Info,
Self::Warn => wit::pumpkin::plugin::logging::Level::Warn,
Self::Error => wit::pumpkin::plugin::logging::Level::Error,
}
}
}
/// Sends a log message to the server at the given severity level.
///
/// Prefer the standard `tracing` macros (`tracing::info!`, `tracing::warn!`, etc.)
/// for structured logging; use this function when you need a direct, low-level log call.
pub fn log(level: LogLevel, message: &str) {
wit::pumpkin::plugin::logging::log(level.to_wit(), message);
}

View File

@@ -0,0 +1,64 @@
/// Allows the plugin to perform DNS resolution.
pub const NETWORK_DNS: &str = "network.dns";
/// Allows the plugin to use TCP sockets.
pub const NETWORK_TCP: &str = "network.tcp";
/// Allows the plugin to use UDP sockets.
pub const NETWORK_UDP: &str = "network.udp";
/// Allows the plugin to initiate TCP connections.
pub const NETWORK_TCP_CONNECT: &str = "network.tcp.connect";
/// Allows the plugin to bind TCP listeners (accept inbound connections).
pub const NETWORK_TCP_BIND: &str = "network.tcp.bind";
/// Allows the plugin to send and receive UDP packets to specific destinations.
pub const NETWORK_UDP_CONNECT: &str = "network.udp.connect";
/// Allows the plugin to bind UDP sockets to local ports.
pub const NETWORK_UDP_BIND: &str = "network.udp.bind";
/// Allows the plugin to send datagram on non-connected UDP socket.
pub const NETWORK_UDP_OUTGOING_DATAGRAM: &str = "network.udp.outgoingdatagram";
/// Restricts all networking permissions to loopback addresses (localhost) only.
pub const NETWORK_LOOPBACK: &str = "network.loopback";
/// Allows the plugin to make outbound TCP/UDP connections.
/// **Warning:** This is a powerful permission.
pub const NETWORK_OUTBOUND: &str = "network.outbound";
/// Allows the plugin to make outbound HTTP connections.
///
/// This is separate from `network.outbound`. This allows the use of `wasi:http`; the other allows the more powerful `wasi:sockets`.
pub const HTTP_OUTBOUND: &str = "http.outbound";
/// Allows the plugin to read files within its own data folder (`plugins/data/<name>`).
pub const FS_READ_DATA: &str = "fs.read.data";
/// Allows the plugin to write files within its own data folder (`plugins/data/<name>`).
///
/// Note that even without `FS_READ_DATA`, this will allow the plugin to
/// inspect (e.g. list) the contents of the directory. But it will block
/// reading any file's contents.
pub const FS_WRITE_DATA: &str = "fs.write.data";
/// Allows the plugin to read all environment variables.
pub const SYS_ENV: &str = "sys.env";
/// Allows the plugin to read specific environment variables.
/// Used with a prefix like "sys.env.PATH".
pub const SYS_ENV_PREFIX: &str = "sys.env.";
/// Allows the plugin to read system information (CPU, Memory, OS).
pub const SYS_INFO: &str = "sys.info";
/// Allows the plugin to read CPU information.
pub const SYS_INFO_CPU: &str = "sys.info.cpu";
/// Allows the plugin to read RAM information.
pub const SYS_INFO_RAM: &str = "sys.info.ram";
/// Allows the plugin to read OS information.
pub const SYS_INFO_OS: &str = "sys.info.os";

View File

@@ -0,0 +1,134 @@
//! Task scheduling API for plugins.
//!
//! This module provides the ability for plugins to schedule closures to be
//! executed after a delay or repeatedly in the server's main tick loop.
//!
//! # Example
//!
//! ```rust,ignore
//! use pumpkin_plugin_api::scheduler::SchedulerExt;
//!
//! context.schedule_delayed_task(20, |server| {
//! server.log("One second has passed!");
//! });
//! ```
use crate::wit::pumpkin::plugin::context::Server;
use crate::wit::pumpkin::plugin::scheduler;
use std::collections::BTreeMap;
use std::sync::Mutex;
/// A type alias for a closure that can be scheduled as a task.
pub type TaskHandler = Box<dyn FnMut(Server) + Send>;
pub(crate) struct Task {
handler: TaskHandler,
}
pub(crate) static TASK_HANDLERS: Mutex<LazyTaskHandlers> = Mutex::new(LazyTaskHandlers {
handlers: BTreeMap::new(),
next_id: 0,
});
pub(crate) struct LazyTaskHandlers {
handlers: BTreeMap<u32, Task>,
next_id: u32,
}
impl LazyTaskHandlers {
/// Registers a new task handler and returns its unique ID.
pub fn register(&mut self, handler: TaskHandler) -> u32 {
let id = self.next_id;
self.next_id += 1;
self.handlers.insert(id, Task { handler });
id
}
/// Executes the task handler for the given ID.
pub fn handle(&mut self, id: u32, server: Server) {
if let Some(task) = self.handlers.get_mut(&id) {
(task.handler)(server);
}
}
}
/// Extension trait to provide ergonomic task scheduling on `Context` and `Server`.
pub trait SchedulerExt {
/// Schedules a task to be executed once after the specified number of ticks.
///
/// * `delay_ticks`: Number of game ticks to wait before execution.
/// * `handler`: Closure to execute.
///
/// Returns a unique task ID.
fn schedule_delayed_task<F>(&self, delay_ticks: u64, handler: F) -> u32
where
F: FnMut(Server) + Send + 'static;
/// Schedules a task to be executed repeatedly.
///
/// * `delay_ticks`: Number of game ticks to wait before the first execution.
/// * `period_ticks`: Number of ticks between subsequent executions.
/// * `handler`: Closure to execute.
///
/// Returns a unique task ID.
fn schedule_repeating_task<F>(&self, delay_ticks: u64, period_ticks: u64, handler: F) -> u32
where
F: FnMut(Server) + Send + 'static;
}
impl SchedulerExt for crate::Context {
fn schedule_delayed_task<F>(&self, delay_ticks: u64, handler: F) -> u32
where
F: FnMut(Server) + Send + 'static,
{
schedule_delayed_task(delay_ticks, handler)
}
fn schedule_repeating_task<F>(&self, delay_ticks: u64, period_ticks: u64, handler: F) -> u32
where
F: FnMut(Server) + Send + 'static,
{
schedule_repeating_task(delay_ticks, period_ticks, handler)
}
}
impl SchedulerExt for crate::Server {
fn schedule_delayed_task<F>(&self, delay_ticks: u64, handler: F) -> u32
where
F: FnMut(Self) + Send + 'static,
{
schedule_delayed_task(delay_ticks, handler)
}
fn schedule_repeating_task<F>(&self, delay_ticks: u64, period_ticks: u64, handler: F) -> u32
where
F: FnMut(Self) + Send + 'static,
{
schedule_repeating_task(delay_ticks, period_ticks, handler)
}
}
/// Lower-level function to schedule a delayed task.
/// Prefer using [`SchedulerExt`] for a more ergonomic API.
pub fn schedule_delayed_task<F>(delay_ticks: u64, handler: F) -> u32
where
F: FnMut(Server) + Send + 'static,
{
let handler_id = TASK_HANDLERS.lock().unwrap().register(Box::new(handler));
scheduler::schedule_delayed_task(handler_id, delay_ticks)
}
/// Lower-level function to schedule a repeating task.
/// Prefer using [`SchedulerExt`] for a more ergonomic API.
pub fn schedule_repeating_task<F>(delay_ticks: u64, period_ticks: u64, handler: F) -> u32
where
F: FnMut(Server) + Send + 'static,
{
let handler_id = TASK_HANDLERS.lock().unwrap().register(Box::new(handler));
scheduler::schedule_repeating_task(handler_id, delay_ticks, period_ticks)
}
/// Cancels a scheduled task.
pub fn cancel_task(task_id: u32) {
scheduler::cancel_task(task_id);
}