mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Plugin event rewrite (#513)
* simplify all * fmt * Consider using derive macro * Add new events * Add events call * minor fixes * minor fixes * fix * optimize code * big changes * fix skill issue * fix * Consider not specify patch version * make logger shared * i want test one thing * Add one useful function * add little amouth of docs * use config instead * revert changes * fmt --------- Co-authored-by: Alexander Medvedev <lilalexmed@proton.me>
This commit is contained in:
14
Cargo.toml
14
Cargo.toml
@@ -41,16 +41,18 @@ tokio = { version = "1.43", features = [
|
||||
"signal",
|
||||
] }
|
||||
|
||||
thiserror = "2"
|
||||
thiserror = "2.0"
|
||||
|
||||
bytes = "1.9"
|
||||
|
||||
# Concurrency/Parallelism and Synchronization
|
||||
rayon = "1.10.0"
|
||||
parking_lot = { version = "0.12.3", features = ["send_guard"] }
|
||||
crossbeam = "0.8.4"
|
||||
rayon = "1.10"
|
||||
parking_lot = { version = "0.12", features = ["send_guard"] }
|
||||
crossbeam = "0.8"
|
||||
|
||||
uuid = { version = "1.12.1", features = ["serde", "v3", "v4"] }
|
||||
derive_more = { version = "1.0.0", features = ["full"] }
|
||||
uuid = { version = "1.12", features = ["serde", "v3", "v4"] }
|
||||
derive_more = { version = "1.0", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
async-trait = "0.1"
|
||||
|
||||
@@ -7,6 +7,6 @@ edition.workspace = true
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "2.0.89", features = ["full"] }
|
||||
quote = "1.0.37"
|
||||
proc-macro2 = "1.0.92"
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
@@ -108,7 +108,7 @@ pub fn with_runtime(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
method.block = if use_global {
|
||||
parse_quote!({
|
||||
GLOBAL_RUNTIME.block_on(async move {
|
||||
crate::GLOBAL_RUNTIME.block_on(async move {
|
||||
#original_body
|
||||
})
|
||||
})
|
||||
|
||||
@@ -86,6 +86,7 @@ pub struct BasicConfiguration {
|
||||
pub encryption: bool,
|
||||
/// The server's description displayed on the status screen.
|
||||
pub motd: String,
|
||||
/// The server's ticks per second.
|
||||
pub tps: f32,
|
||||
/// The default game mode for players.
|
||||
pub default_gamemode: GameMode,
|
||||
|
||||
@@ -13,7 +13,7 @@ pumpkin-util = { path = "../pumpkin-util" }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
heck = "0.5.0"
|
||||
heck = "0.5"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = "2.0"
|
||||
@@ -2,20 +2,17 @@ use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse::{Nothing, Parser},
|
||||
parse_macro_input, Field, Fields, ItemStruct,
|
||||
parse_macro_input, Block, Expr, Field, Fields, ItemStruct, Stmt,
|
||||
};
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn event(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
#[proc_macro_derive(Event)]
|
||||
pub fn event(item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as ItemStruct);
|
||||
let name = &input.ident;
|
||||
let _ = parse_macro_input!(args as Nothing);
|
||||
|
||||
quote! {
|
||||
#input
|
||||
|
||||
impl crate::plugin::Event for #name {
|
||||
fn get_name_static() -> &'static str {
|
||||
stringify!(#name)
|
||||
@@ -46,7 +43,10 @@ pub fn cancellable(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
if let Fields::Named(ref mut fields) = item_struct.fields {
|
||||
fields.named.push(
|
||||
Field::parse_named
|
||||
.parse2(quote! { pub cancelled: bool })
|
||||
.parse2(quote! {
|
||||
/// A boolean indicating cancel state of the event.
|
||||
pub cancelled: bool
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
@@ -67,6 +67,89 @@ pub fn cancellable(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn send_cancellable(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as Block);
|
||||
|
||||
let mut event = None;
|
||||
let mut after_block = None;
|
||||
let mut cancelled_block = None;
|
||||
|
||||
for stmt in input.stmts {
|
||||
if let Stmt::Expr(expr, _) = stmt {
|
||||
if event.is_none() {
|
||||
event = Some(expr);
|
||||
} else if let Expr::Block(b) = expr {
|
||||
if let Some(ref label) = b.label {
|
||||
if label.name.ident == "after" {
|
||||
after_block = Some(b);
|
||||
} else if label.name.ident == "cancelled" {
|
||||
cancelled_block = Some(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(event) = event {
|
||||
if let Some(after_block) = after_block {
|
||||
if let Some(cancelled_block) = cancelled_block {
|
||||
quote! {
|
||||
let event = crate::PLUGIN_MANAGER
|
||||
.lock()
|
||||
.await
|
||||
.fire(#event)
|
||||
.await;
|
||||
|
||||
if !event.cancelled {
|
||||
#after_block
|
||||
} else {
|
||||
#cancelled_block
|
||||
}
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
quote! {
|
||||
let event = crate::PLUGIN_MANAGER
|
||||
.lock()
|
||||
.await
|
||||
.fire(#event)
|
||||
.await;
|
||||
|
||||
if !event.cancelled {
|
||||
#after_block
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
} else if let Some(cancelled_block) = cancelled_block {
|
||||
quote! {
|
||||
let event = crate::PLUGIN_MANAGER
|
||||
.lock()
|
||||
.await
|
||||
.fire(#event)
|
||||
.await;
|
||||
|
||||
if event.cancelled {
|
||||
#cancelled_block
|
||||
}
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
quote! {
|
||||
let event = crate::PLUGIN_MANAGER
|
||||
.lock()
|
||||
.await
|
||||
.fire(#event)
|
||||
.await;
|
||||
}
|
||||
.into()
|
||||
}
|
||||
} else {
|
||||
panic!("Event must be specified");
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn client_packet(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(item.clone()).unwrap();
|
||||
|
||||
@@ -7,4 +7,5 @@ edition.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
bytes.workspace = true
|
||||
cesu8 = "1.1.0"
|
||||
|
||||
cesu8 = "1.1"
|
||||
|
||||
@@ -25,8 +25,8 @@ tokio.workspace = true
|
||||
bytes.workspace = true
|
||||
|
||||
# encryption
|
||||
aes = "0.8.4"
|
||||
cfb8 = "0.8.1"
|
||||
aes = "0.8"
|
||||
cfb8 = "0.8"
|
||||
|
||||
# compression
|
||||
libdeflater = "1.23.0"
|
||||
libdeflater = "1.23"
|
||||
@@ -8,7 +8,7 @@ pumpkin-protocol = { path = "../pumpkin-protocol" }
|
||||
pumpkin-nbt = { path = "../pumpkin-nbt" }
|
||||
pumpkin-util = { path = "../pumpkin-util" }
|
||||
|
||||
indexmap = { version = "2.7.1", features = ["serde"] }
|
||||
indexmap = { version = "2.7", features = ["serde"] }
|
||||
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -12,5 +12,5 @@ uuid.workspace = true
|
||||
|
||||
num-traits = "0.2"
|
||||
|
||||
colored = "3"
|
||||
md5 = "0.7.0"
|
||||
colored = "3.0"
|
||||
md5 = "0.7"
|
||||
|
||||
@@ -21,29 +21,28 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
log.workspace = true
|
||||
|
||||
num-derive = "0.4.2"
|
||||
num-derive = "0.4"
|
||||
|
||||
dashmap = "6.1.0"
|
||||
dashmap = "6.1"
|
||||
|
||||
num-traits = "0.2"
|
||||
|
||||
# Compression
|
||||
flate2 = "1.0"
|
||||
lz4 = "1.28.1"
|
||||
lz4 = "1.28"
|
||||
|
||||
file-guard = "0.2.0"
|
||||
indexmap = "2.7.1"
|
||||
file-guard = "0.2"
|
||||
indexmap = "2.7"
|
||||
|
||||
enum_dispatch = "0.3.13"
|
||||
enum_dispatch = "0.3"
|
||||
|
||||
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
||||
|
||||
noise = "0.9.0"
|
||||
|
||||
rand = "0.8.5"
|
||||
noise = "0.9"
|
||||
rand = "0.8"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
|
||||
[[bench]]
|
||||
name = "chunk_noise"
|
||||
|
||||
@@ -74,6 +74,7 @@ pub enum CompressionError {
|
||||
LZ4Error(std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChunkData {
|
||||
/// See description in `Subchunks`
|
||||
pub subchunks: Subchunks,
|
||||
@@ -92,7 +93,7 @@ pub struct ChunkData {
|
||||
/// chunk, what filled only air or only water.
|
||||
///
|
||||
/// Multi means a normal chunk, what contains 24 subchunks.
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Subchunks {
|
||||
Single(u16),
|
||||
Multi(Box<[Subchunk; SUBCHUNKS_COUNT]>),
|
||||
|
||||
@@ -235,7 +235,7 @@ impl Level {
|
||||
pub fn fetch_chunks(
|
||||
&self,
|
||||
chunks: &[Vector2<i32>],
|
||||
channel: mpsc::Sender<Arc<RwLock<ChunkData>>>,
|
||||
channel: mpsc::Sender<(Arc<RwLock<ChunkData>>, bool)>,
|
||||
rt: &Handle,
|
||||
) {
|
||||
chunks.par_iter().for_each(|at| {
|
||||
@@ -246,11 +246,14 @@ impl Level {
|
||||
let level_folder = self.level_folder.clone();
|
||||
let world_gen = self.world_gen.clone();
|
||||
let chunk_pos = *at;
|
||||
let mut first_load = false;
|
||||
|
||||
let chunk = loaded_chunks
|
||||
.get(&chunk_pos)
|
||||
.map(|entry| entry.value().clone())
|
||||
.unwrap_or_else(|| {
|
||||
first_load = true;
|
||||
|
||||
let loaded_chunk =
|
||||
match Self::load_chunk_from_save(chunk_reader, &level_folder, chunk_pos) {
|
||||
Ok(chunk) => {
|
||||
@@ -299,7 +302,7 @@ impl Level {
|
||||
|
||||
rt.spawn(async move {
|
||||
let _ = channel
|
||||
.send(chunk)
|
||||
.send((chunk, first_load))
|
||||
.await
|
||||
.inspect_err(|err| log::error!("unable to send chunk to channel: {}", err));
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ uuid.workspace = true
|
||||
tokio.workspace = true
|
||||
rayon.workspace = true
|
||||
thiserror.workspace = true
|
||||
async-trait.workspace = true
|
||||
|
||||
# config
|
||||
serde.workspace = true
|
||||
@@ -40,50 +41,48 @@ serde_json.workspace = true
|
||||
|
||||
bytes.workspace = true
|
||||
|
||||
rand = "0.8.5"
|
||||
rand = "0.8"
|
||||
|
||||
num-bigint = "0.4"
|
||||
|
||||
# Console line reading
|
||||
rustyline = "15.0.0"
|
||||
rustyline = "15.0"
|
||||
|
||||
# encryption
|
||||
rsa = "0.9.7"
|
||||
rsa-der = "0.3.0"
|
||||
rsa = "0.9"
|
||||
rsa-der = "0.3"
|
||||
|
||||
# authentication
|
||||
reqwest = { version = "0.12.12", default-features = false, features = [
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"http2",
|
||||
"json",
|
||||
"macos-system-configuration",
|
||||
"rustls-tls",
|
||||
] }
|
||||
|
||||
sha1 = "0.10.6"
|
||||
sha1 = "0.10"
|
||||
|
||||
# velocity en
|
||||
hmac = "0.12.1"
|
||||
sha2 = "0.10.8"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
|
||||
base64 = "0.22.1"
|
||||
base64 = "0.22"
|
||||
|
||||
# icon loading
|
||||
png = "0.17.16"
|
||||
png = "0.17"
|
||||
|
||||
# logging
|
||||
simple_logger = { version = "5.0.0", features = ["threads"] }
|
||||
simple_logger = { version = "5.0", features = ["threads"] }
|
||||
# Remove time in favor of chrono?
|
||||
time = "0.3.37"
|
||||
time = "0.3"
|
||||
|
||||
chrono = { version = "0.4.39", features = ["serde"]}
|
||||
|
||||
# commands
|
||||
async-trait = "0.1.85"
|
||||
chrono = { version = "0.4", features = ["serde"]}
|
||||
|
||||
# plugins
|
||||
libloading = "0.8.6"
|
||||
libloading = "0.8"
|
||||
|
||||
[build-dependencies]
|
||||
git-version = "0.3.9"
|
||||
git-version = "0.3"
|
||||
# This makes it so the entire project doesn't recompile on each build on linux.
|
||||
[target.'cfg(target_os = "windows")'.build-dependencies]
|
||||
tauri-winres= "0.2.0"
|
||||
tauri-winres= "0.3"
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use core::f32;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
@@ -170,6 +173,20 @@ impl Entity {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns entity rotation as vector
|
||||
pub fn rotation(&self) -> Vector3<f32> {
|
||||
// Convert degrees to radians if necessary
|
||||
let yaw_rad = self.yaw.load() * (PI / 180.0);
|
||||
let pitch_rad = self.pitch.load() * (PI / 180.0);
|
||||
|
||||
Vector3::new(
|
||||
yaw_rad.cos() * pitch_rad.cos(),
|
||||
pitch_rad.sin(),
|
||||
yaw_rad.sin() * pitch_rad.cos(),
|
||||
)
|
||||
.normalize()
|
||||
}
|
||||
|
||||
/// Changes this entity's pitch and yaw to look at target
|
||||
pub async fn look_at(&self, target: Vector3<f64>) {
|
||||
let position = self.pos.load();
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// Not warn event sending macros
|
||||
#![allow(unused_labels)]
|
||||
|
||||
use crate::net::{lan_broadcast, query, rcon::RCONServer, Client};
|
||||
use crate::server::{ticker::Ticker, Server};
|
||||
use log::{LevelFilter, Log};
|
||||
use plugin::PluginManager;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use rustyline::DefaultEditor;
|
||||
use simple_logger::SimpleLogger;
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
sync::{Arc, LazyLock},
|
||||
@@ -29,6 +34,43 @@ const GIT_VERSION: &str = env!("GIT_VERSION");
|
||||
pub static PLUGIN_MANAGER: LazyLock<Mutex<PluginManager>> =
|
||||
LazyLock::new(|| Mutex::new(PluginManager::new()));
|
||||
|
||||
pub static LOGGER_IMPL: LazyLock<Option<(Box<dyn Log>, LevelFilter)>> = LazyLock::new(|| {
|
||||
if ADVANCED_CONFIG.logging.enabled {
|
||||
let mut logger = SimpleLogger::new();
|
||||
|
||||
if ADVANCED_CONFIG.logging.timestamp {
|
||||
logger = logger.with_timestamp_format(time::macros::format_description!(
|
||||
"[year]-[month]-[day] [hour]:[minute]:[second]"
|
||||
));
|
||||
} else {
|
||||
logger = logger.without_timestamps();
|
||||
}
|
||||
|
||||
Some((
|
||||
Box::new(
|
||||
logger
|
||||
.with_level(LevelFilter::Info)
|
||||
.with_colors(ADVANCED_CONFIG.logging.color)
|
||||
.with_threads(ADVANCED_CONFIG.logging.threads)
|
||||
.env(),
|
||||
),
|
||||
LevelFilter::Info,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! init_log {
|
||||
() => {
|
||||
if let Some((logger_impl, level)) = &*pumpkin::LOGGER_IMPL {
|
||||
log::set_max_level(*level);
|
||||
log::set_logger(logger_impl).unwrap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct PumpkinServer {
|
||||
pub server: Arc<Server>,
|
||||
pub listener: TcpListener,
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
#![expect(clippy::missing_errors_doc)]
|
||||
#![expect(clippy::module_name_repetitions)]
|
||||
#![expect(clippy::struct_excessive_bools)]
|
||||
// Not warn event sending macros
|
||||
#![allow(unused_labels)]
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
compile_error!("Compiling for WASI targets is not supported!");
|
||||
|
||||
use log::LevelFilter;
|
||||
|
||||
use plugin::PluginManager;
|
||||
use std::{
|
||||
io::{self},
|
||||
@@ -47,7 +47,7 @@ use tokio::signal::unix::{signal, SignalKind};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::server::CURRENT_MC_VERSION;
|
||||
use pumpkin::PumpkinServer;
|
||||
use pumpkin::{init_log, PumpkinServer};
|
||||
use pumpkin_protocol::CURRENT_MC_PROTOCOL;
|
||||
use pumpkin_util::text::{color::NamedColor, TextComponent};
|
||||
use std::time::Instant;
|
||||
@@ -75,7 +75,8 @@ const GIT_VERSION: &str = env!("GIT_VERSION");
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let time = Instant::now();
|
||||
init_logger();
|
||||
|
||||
init_log!();
|
||||
|
||||
let default_panic = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
@@ -130,30 +131,6 @@ fn handle_interrupt() {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
use pumpkin_config::ADVANCED_CONFIG;
|
||||
if ADVANCED_CONFIG.logging.enabled {
|
||||
let mut logger = simple_logger::SimpleLogger::new();
|
||||
logger = logger.with_timestamp_format(time::macros::format_description!(
|
||||
"[year]-[month]-[day] [hour]:[minute]:[second]"
|
||||
));
|
||||
|
||||
if !ADVANCED_CONFIG.logging.timestamp {
|
||||
logger = logger.without_timestamps();
|
||||
}
|
||||
|
||||
// default
|
||||
logger = logger.with_level(LevelFilter::Info);
|
||||
|
||||
logger = logger.with_colors(ADVANCED_CONFIG.logging.color);
|
||||
logger = logger.with_threads(ADVANCED_CONFIG.logging.threads);
|
||||
|
||||
logger = logger.env();
|
||||
|
||||
logger.init().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Non-UNIX Ctrl-C handling
|
||||
#[cfg(not(unix))]
|
||||
async fn setup_sighandler() -> io::Result<()> {
|
||||
|
||||
@@ -12,12 +12,28 @@ use crate::{
|
||||
|
||||
use super::{Event, EventPriority, PluginMetadata};
|
||||
|
||||
/// The `Context` struct represents the context of a plugin, containing metadata,
|
||||
/// a server reference, and event handlers.
|
||||
///
|
||||
/// # Fields
|
||||
/// - `metadata`: Metadata of the plugin.
|
||||
/// - `server`: A reference to the server on which the plugin operates.
|
||||
/// - `handlers`: A map of event handlers, protected by a read-write lock for safe access across threads.
|
||||
pub struct Context {
|
||||
metadata: PluginMetadata<'static>,
|
||||
pub server: Arc<Server>,
|
||||
handlers: Arc<RwLock<HandlerMap>>,
|
||||
}
|
||||
impl Context {
|
||||
/// Creates a new instance of `Context`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `metadata`: The metadata of the plugin.
|
||||
/// - `server`: A reference to the server.
|
||||
/// - `handlers`: A collection containing the event handlers.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new instance of `Context`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
metadata: PluginMetadata<'static>,
|
||||
@@ -31,6 +47,10 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the data folder path for the plugin, creating it if it does not exist.
|
||||
///
|
||||
/// # Returns
|
||||
/// A string representing the path to the data folder.
|
||||
#[must_use]
|
||||
pub fn get_data_folder(&self) -> String {
|
||||
let path = format!("./plugins/{}", self.metadata.name);
|
||||
@@ -40,10 +60,22 @@ impl Context {
|
||||
path
|
||||
}
|
||||
|
||||
/// Asynchronously retrieves a player by their name.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `player_name`: The name of the player to retrieve.
|
||||
///
|
||||
/// # Returns
|
||||
/// An optional reference to the player if found, or `None` if not.
|
||||
pub async fn get_player_by_name(&self, player_name: String) -> Option<Arc<Player>> {
|
||||
self.server.get_player_by_name(&player_name).await
|
||||
}
|
||||
|
||||
/// Asynchronously registers a command with the server.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `tree`: The command tree to register.
|
||||
/// - `permission`: The permission level required to execute the command.
|
||||
pub async fn register_command(
|
||||
&self,
|
||||
tree: crate::command::tree::CommandTree,
|
||||
@@ -62,6 +94,10 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronously unregisters a command from the server.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `name`: The name of the command to unregister.
|
||||
pub async fn unregister_command(&self, name: &str) {
|
||||
{
|
||||
let mut dispatcher_lock = self.server.command_dispatcher.write().await;
|
||||
@@ -76,9 +112,22 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronously registers an event handler for a specific event type.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// - `E`: The event type that the handler will respond to.
|
||||
/// - `H`: The type of the event handler.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `handler`: A reference to the event handler.
|
||||
/// - `priority`: The priority of the event handler.
|
||||
/// - `blocking`: A boolean indicating whether the handler is blocking.
|
||||
///
|
||||
/// # Constraints
|
||||
/// The handler must implement the `EventHandler<E>` trait.
|
||||
pub async fn register_event<E: Event + 'static, H>(
|
||||
&self,
|
||||
handler: H,
|
||||
handler: Arc<H>,
|
||||
priority: EventPriority,
|
||||
blocking: bool,
|
||||
) where
|
||||
|
||||
@@ -1,24 +1,51 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_macros::{cancellable, event};
|
||||
use pumpkin_macros::{cancellable, Event};
|
||||
use pumpkin_world::block::registry::Block;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
|
||||
/// A trait representing events related to blocks.
|
||||
///
|
||||
/// This trait provides a method to retrieve the block associated with the event.
|
||||
pub trait BlockEvent: Send + Sync {
|
||||
/// Retrieves a reference to the block associated with the event.
|
||||
///
|
||||
/// # Returns
|
||||
/// A reference to the `Block` involved in the event.
|
||||
fn get_block(&self) -> &Block;
|
||||
}
|
||||
|
||||
#[event]
|
||||
/// An event that occurs when a block is broken.
|
||||
///
|
||||
/// This event contains information about the player breaking the block, the block itself,
|
||||
/// the experience gained, and whether the block should drop items.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct BlockBreakEvent {
|
||||
/// The player who is breaking the block, if applicable.
|
||||
pub player: Option<Arc<Player>>,
|
||||
|
||||
/// The block that is being broken.
|
||||
pub block: Block,
|
||||
|
||||
/// The amount of experience gained from breaking the block.
|
||||
pub exp: u32,
|
||||
|
||||
/// A boolean indicating whether the block should drop items.
|
||||
pub drop: bool,
|
||||
}
|
||||
|
||||
impl BlockBreakEvent {
|
||||
/// Creates a new instance of `BlockBreakEvent`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `player`: An optional reference to the player breaking the block.
|
||||
/// - `block`: The block that is being broken.
|
||||
/// - `exp`: The amount of experience gained from breaking the block.
|
||||
/// - `drop`: A boolean indicating whether the block should drop items.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new instance of `BlockBreakEvent`.
|
||||
#[must_use]
|
||||
pub fn new(player: Option<Arc<Player>>, block: Block, exp: u32, drop: bool) -> Self {
|
||||
Self {
|
||||
@@ -37,10 +64,16 @@ impl BlockEvent for BlockBreakEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[event]
|
||||
/// An event that occurs when a block is burned.
|
||||
///
|
||||
/// This event contains information about the block that ignited the fire and the block that is burning.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct BlockBurnEvent {
|
||||
/// The block that is igniting the fire.
|
||||
pub igniting_block: Block,
|
||||
|
||||
/// The block that is burning.
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
@@ -50,12 +83,23 @@ impl BlockEvent for BlockBurnEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[event]
|
||||
/// An event that occurs when a player attempts to build on a block.
|
||||
///
|
||||
/// This event contains information about the block to build, whether building is allowed,
|
||||
/// the player attempting to build, and the block being built upon.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct BlockCanBuildEvent {
|
||||
/// The block that the player is attempting to build.
|
||||
pub block_to_build: Block,
|
||||
|
||||
/// A boolean indicating whether building is allowed.
|
||||
pub buildable: bool,
|
||||
|
||||
/// The player attempting to build.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The block being built upon.
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
@@ -65,12 +109,23 @@ impl BlockEvent for BlockCanBuildEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[event]
|
||||
/// An event that occurs when a block is placed.
|
||||
///
|
||||
/// This event contains information about the player placing the block, the block being placed,
|
||||
/// the block being placed against, and whether the player can build.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct BlockPlaceEvent {
|
||||
/// The player placing the block.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The block that is being placed.
|
||||
pub block_placed: Block,
|
||||
|
||||
/// The block that the new block is being placed against.
|
||||
pub block_placed_against: Block,
|
||||
|
||||
/// A boolean indicating whether the player can build.
|
||||
pub can_build: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -2,27 +2,77 @@ use std::any::Any;
|
||||
|
||||
pub mod block;
|
||||
pub mod player;
|
||||
pub mod world;
|
||||
|
||||
/// A trait representing an event in the system.
|
||||
///
|
||||
/// This trait provides methods for retrieving the event's name and for type-safe downcasting.
|
||||
pub trait Event: Send + Sync {
|
||||
/// Returns the static name of the event type.
|
||||
///
|
||||
/// # Returns
|
||||
/// A static string slice representing the name of the event type.
|
||||
fn get_name_static() -> &'static str
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Returns the name of the event instance.
|
||||
///
|
||||
/// # Returns
|
||||
/// A static string slice representing the name of the event instance.
|
||||
fn get_name(&self) -> &'static str;
|
||||
|
||||
/// Provides a mutable reference to the event as a trait object.
|
||||
///
|
||||
/// This method allows for type-safe downcasting of the event.
|
||||
///
|
||||
/// # Returns
|
||||
/// A mutable reference to the event as a `dyn Any` trait object.
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Provides an immutable reference to the event as a trait object.
|
||||
///
|
||||
/// This method allows for type-safe downcasting of the event.
|
||||
///
|
||||
/// # Returns
|
||||
/// An immutable reference to the event as a `dyn Any` trait object.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
/// A trait for cancellable events.
|
||||
///
|
||||
/// This trait provides methods to check and set the cancellation state of an event.
|
||||
pub trait Cancellable: Send + Sync {
|
||||
/// Checks if the event has been cancelled.
|
||||
///
|
||||
/// # Returns
|
||||
/// A boolean indicating whether the event is cancelled.
|
||||
fn cancelled(&self) -> bool;
|
||||
|
||||
/// Sets the cancellation state of the event.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `cancelled`: A boolean indicating the new cancellation state.
|
||||
fn set_cancelled(&mut self, cancelled: bool);
|
||||
}
|
||||
|
||||
// Lowest priority events are executed first, so that higher priority events can override their changes
|
||||
/// An enumeration representing the priority levels of events.
|
||||
///
|
||||
/// Events with lower priority values are executed first, allowing higher priority events
|
||||
/// to override their changes.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)]
|
||||
pub enum EventPriority {
|
||||
/// Highest priority level.
|
||||
Highest,
|
||||
|
||||
/// High priority level.
|
||||
High,
|
||||
|
||||
/// Normal priority level.
|
||||
Normal,
|
||||
|
||||
/// Low priority level.
|
||||
Low,
|
||||
|
||||
/// Lowest priority level.
|
||||
Lowest,
|
||||
}
|
||||
|
||||
@@ -1,22 +1,42 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_macros::{cancellable, event};
|
||||
use pumpkin_macros::{cancellable, Event};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
|
||||
/// A trait representing events related to players.
|
||||
///
|
||||
/// This trait provides a method to retrieve the player associated with the event.
|
||||
pub trait PlayerEvent: Send + Sync {
|
||||
/// Retrieves a reference to the player associated with the event.
|
||||
///
|
||||
/// # Returns
|
||||
/// A reference to the `Arc<Player>` involved in the event.
|
||||
fn get_player(&self) -> &Arc<Player>;
|
||||
}
|
||||
|
||||
#[event]
|
||||
/// An event that occurs when a player joins the game.
|
||||
///
|
||||
/// This event contains information about the player joining and a message to display upon joining.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerJoinEvent {
|
||||
/// The player who is joining the game.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The message to display when the player joins.
|
||||
pub join_message: TextComponent,
|
||||
}
|
||||
|
||||
impl PlayerJoinEvent {
|
||||
/// Creates a new instance of `PlayerJoinEvent`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `player`: A reference to the player joining the game.
|
||||
/// - `join_message`: The message to display upon joining.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new instance of `PlayerJoinEvent`.
|
||||
pub fn new(player: Arc<Player>, join_message: TextComponent) -> Self {
|
||||
Self {
|
||||
player,
|
||||
@@ -32,14 +52,28 @@ impl PlayerEvent for PlayerJoinEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[event]
|
||||
/// An event that occurs when a player leaves the game.
|
||||
///
|
||||
/// This event contains information about the player leaving and a message to display upon leaving.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerLeaveEvent {
|
||||
/// The player who is leaving the game.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The message to display when the player leaves.
|
||||
pub leave_message: TextComponent,
|
||||
}
|
||||
|
||||
impl PlayerLeaveEvent {
|
||||
/// Creates a new instance of `PlayerLeaveEvent`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `player`: A reference to the player leaving the game.
|
||||
/// - `leave_message`: The message to display upon leaving.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new instance of `PlayerLeaveEvent`.
|
||||
pub fn new(player: Arc<Player>, leave_message: TextComponent) -> Self {
|
||||
Self {
|
||||
player,
|
||||
|
||||
44
pumpkin/src/plugin/api/events/world.rs
Normal file
44
pumpkin/src/plugin/api/events/world.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::world::World;
|
||||
use pumpkin_macros::{cancellable, Event};
|
||||
use pumpkin_world::chunk::ChunkData;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
/// An event that occurs when a chunk is loaded in a world.
|
||||
///
|
||||
/// This event contains information about the world and the chunk being loaded.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct ChunkLoad {
|
||||
/// The world in which the chunk is being loaded.
|
||||
pub world: Arc<World>,
|
||||
|
||||
/// The chunk data being loaded, wrapped in a read-write lock for safe concurrent access.
|
||||
pub chunk: Arc<RwLock<ChunkData>>,
|
||||
}
|
||||
|
||||
/// An event that occurs when a chunk is sent to a client.
|
||||
///
|
||||
/// This event contains information about the world and the chunk being sent.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct ChunkSend {
|
||||
/// The world from which the chunk is being sent.
|
||||
pub world: Arc<World>,
|
||||
|
||||
/// The chunk data being sent, wrapped in a read-write lock for safe concurrent access.
|
||||
pub chunk: Arc<RwLock<ChunkData>>,
|
||||
}
|
||||
|
||||
/// An event that occurs when a chunk is saved in a world.
|
||||
///
|
||||
/// This event contains information about the world and the chunk being saved.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct ChunkSave {
|
||||
/// The world in which the chunk is being saved.
|
||||
pub world: Arc<World>,
|
||||
|
||||
/// The chunk data being saved, wrapped in a read-write lock for safe concurrent access.
|
||||
pub chunk: Arc<RwLock<ChunkData>>,
|
||||
}
|
||||
@@ -5,6 +5,11 @@ use async_trait::async_trait;
|
||||
pub use context::*;
|
||||
pub use events::*;
|
||||
|
||||
/// Struct representing metadata for a plugin.
|
||||
///
|
||||
/// This struct contains essential information about a plugin, including its name,
|
||||
/// version, authors, and a description. It is generic over a lifetime `'s` to allow
|
||||
/// for string slices that are valid for the lifetime of the plugin metadata.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginMetadata<'s> {
|
||||
/// The name of the plugin.
|
||||
@@ -17,14 +22,35 @@ pub struct PluginMetadata<'s> {
|
||||
pub description: &'s str,
|
||||
}
|
||||
|
||||
/// Trait representing a plugin with asynchronous lifecycle methods.
|
||||
///
|
||||
/// This trait defines the required methods for a plugin, including hooks for when
|
||||
/// the plugin is loaded and unloaded. It is marked with `async_trait` to allow
|
||||
/// for asynchronous implementations.
|
||||
#[async_trait]
|
||||
pub trait Plugin: Send + Sync + 'static {
|
||||
/// Called when the plugin is loaded.
|
||||
/// Asynchronous method called when the plugin is loaded.
|
||||
///
|
||||
/// This method initializes the plugin within the server context.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `_server`: Reference to the server's context.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(())` on success, or `Err(String)` on failure.
|
||||
async fn on_load(&mut self, _server: &Context) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called when the plugin is unloaded.
|
||||
/// Asynchronous method called when the plugin is unloaded.
|
||||
///
|
||||
/// This method cleans up resources when the plugin is removed from the server context.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `_server`: Reference to the server's context.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(())` on success, or `Err(String)` on failure.
|
||||
async fn on_unload(&mut self, _server: &Context) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -15,30 +15,67 @@ type PluginData = (
|
||||
bool,
|
||||
);
|
||||
|
||||
/// A trait for handling events dynamically.
|
||||
///
|
||||
/// This trait allows for handling events of any type that implements the `Event` trait.
|
||||
#[async_trait]
|
||||
pub trait DynEventHandler: Send + Sync {
|
||||
/// Asynchronously handles a dynamic event.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `event`: A reference to the event to handle.
|
||||
async fn handle_dyn(&self, event: &(dyn Event + Send + Sync));
|
||||
|
||||
/// Asynchronously handles a blocking dynamic event.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `event`: A mutable reference to the event to handle.
|
||||
async fn handle_blocking_dyn(&self, _event: &mut (dyn Event + Send + Sync));
|
||||
|
||||
/// Checks if the event handler is blocking.
|
||||
///
|
||||
/// # Returns
|
||||
/// A boolean indicating whether the handler is blocking.
|
||||
fn is_blocking(&self) -> bool;
|
||||
|
||||
/// Retrieves the priority of the event handler.
|
||||
///
|
||||
/// # Returns
|
||||
/// The priority of the event handler.
|
||||
fn get_priority(&self) -> EventPriority;
|
||||
}
|
||||
|
||||
/// A trait for handling specific events.
|
||||
///
|
||||
/// This trait allows for handling events of a specific type that implements the `Event` trait.
|
||||
#[async_trait]
|
||||
pub trait EventHandler<E: Event>: Send + Sync {
|
||||
/// Asynchronously handles an event of type `E`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `event`: A reference to the event to handle.
|
||||
async fn handle(&self, _event: &E) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Asynchronously handles a blocking event of type `E`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `event`: A mutable reference to the event to handle.
|
||||
async fn handle_blocking(&self, _event: &mut E) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct representing a typed event handler.
|
||||
///
|
||||
/// This struct holds a reference to an event handler, its priority, and whether it is blocking.
|
||||
struct TypedEventHandler<E, H>
|
||||
where
|
||||
E: Event + Send + Sync + 'static,
|
||||
H: EventHandler<E> + Send + Sync,
|
||||
{
|
||||
handler: H,
|
||||
handler: Arc<H>,
|
||||
priority: EventPriority,
|
||||
blocking: bool,
|
||||
_phantom: std::marker::PhantomData<E>,
|
||||
@@ -50,12 +87,10 @@ where
|
||||
E: Event + Send + Sync + 'static,
|
||||
H: EventHandler<E> + Send + Sync,
|
||||
{
|
||||
/// Asynchronously handles a blocking dynamic event.
|
||||
async fn handle_blocking_dyn(&self, event: &mut (dyn Event + Send + Sync)) {
|
||||
// Check if the event is the same type as E. We can not use the type_id because it is
|
||||
// different in the plugin and the main program
|
||||
if E::get_name_static() == event.get_name() {
|
||||
// This is fully safe as long as the event's get_name() and get_name_static()
|
||||
// functions are correctly implemented and don't conflict with other events
|
||||
// Safely cast the event to the correct type and handle it.
|
||||
let event = unsafe {
|
||||
&mut *std::ptr::from_mut::<dyn std::any::Any>(event.as_any_mut()).cast::<E>()
|
||||
};
|
||||
@@ -63,29 +98,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronously handles a dynamic event.
|
||||
async fn handle_dyn(&self, event: &(dyn Event + Send + Sync)) {
|
||||
// Check if the event is the same type as E. We can not use the type_id because it is
|
||||
// different in the plugin and the main program
|
||||
if E::get_name_static() == event.get_name() {
|
||||
// This is fully safe as long as the event's get_name() and get_name_static()
|
||||
// functions are correctly implemented and don't conflict with other events
|
||||
// Safely cast the event to the correct type and handle it.
|
||||
let event =
|
||||
unsafe { &*std::ptr::from_ref::<dyn std::any::Any>(event.as_any()).cast::<E>() };
|
||||
self.handler.handle(event).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the handler is blocking.
|
||||
fn is_blocking(&self) -> bool {
|
||||
self.blocking
|
||||
}
|
||||
|
||||
/// Retrieves the priority of the handler.
|
||||
fn get_priority(&self) -> EventPriority {
|
||||
self.priority.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// A type alias for a map of event handlers, where the key is a static string
|
||||
/// and the value is a vector of dynamic event handlers.
|
||||
pub type HandlerMap = HashMap<&'static str, Vec<Box<dyn DynEventHandler>>>;
|
||||
|
||||
/// A struct for managing plugins.
|
||||
pub struct PluginManager {
|
||||
plugins: Vec<PluginData>,
|
||||
server: Option<Arc<Server>>,
|
||||
@@ -93,12 +131,17 @@ pub struct PluginManager {
|
||||
}
|
||||
|
||||
impl Default for PluginManager {
|
||||
/// Creates a new instance of `PluginManager` with default values.
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginManager {
|
||||
/// Creates a new instance of `PluginManager`.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new instance of `PluginManager`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -108,16 +151,24 @@ impl PluginManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the server reference for the plugin manager.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `server`: An `Arc` reference to the server to set.
|
||||
pub fn set_server(&mut self, server: Arc<Server>) {
|
||||
self.server = Some(server);
|
||||
}
|
||||
|
||||
/// Asynchronously loads plugins from the specified plugin directory.
|
||||
///
|
||||
/// # Returns
|
||||
/// A result indicating success or failure. If it fails, it returns a `PluginsLoadError`.
|
||||
pub async fn load_plugins(&mut self) -> Result<(), PluginsLoadError> {
|
||||
const PLUGIN_DIR: &str = "./plugins";
|
||||
|
||||
if !Path::new(PLUGIN_DIR).exists() {
|
||||
fs::create_dir(PLUGIN_DIR).map_err(|_| PluginsLoadError::CreatePluginDir)?;
|
||||
// Well we just created the dir, it has to be empty so lets return
|
||||
// If the directory was just created, it should be empty, so we return.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -137,6 +188,13 @@ impl PluginManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to load a plugin from the specified path.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `path`: The path to the plugin to load.
|
||||
///
|
||||
/// # Returns
|
||||
/// A result indicating success or failure. If it fails, it returns a `PluginLoadError`.
|
||||
async fn try_load_plugin(&mut self, path: &Path) -> Result<(), PluginLoadError> {
|
||||
let library = unsafe {
|
||||
libloading::Library::new(path)
|
||||
@@ -154,7 +212,7 @@ impl PluginManager {
|
||||
.map_err(|_| PluginLoadError::GetPluginMeta)?
|
||||
};
|
||||
|
||||
// The chance that this will panic is non-existent, but just in case
|
||||
// Create a context for the plugin.
|
||||
let context = Context::new(
|
||||
metadata.clone(),
|
||||
self.server.clone().expect("Server not set"),
|
||||
@@ -173,6 +231,13 @@ impl PluginManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks if a plugin is loaded by its name.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `name`: The name of the plugin to check.
|
||||
///
|
||||
/// # Returns
|
||||
/// A boolean indicating whether the plugin is loaded.
|
||||
#[must_use]
|
||||
pub fn is_plugin_loaded(&self, name: &str) -> bool {
|
||||
self.plugins
|
||||
@@ -180,6 +245,13 @@ impl PluginManager {
|
||||
.any(|(metadata, _, _, loaded)| metadata.name == name && *loaded)
|
||||
}
|
||||
|
||||
/// Asynchronously loads a plugin by its name.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `name`: The name of the plugin to load.
|
||||
///
|
||||
/// # Returns
|
||||
/// A result indicating success or failure. If it fails, it returns an error message.
|
||||
pub async fn load_plugin(&mut self, name: &str) -> Result<(), String> {
|
||||
let plugin = self
|
||||
.plugins
|
||||
@@ -205,6 +277,13 @@ impl PluginManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronously unloads a plugin by its name.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `name`: The name of the plugin to unload.
|
||||
///
|
||||
/// # Returns
|
||||
/// A result indicating success or failure. If it fails, it returns an error message.
|
||||
pub async fn unload_plugin(&mut self, name: &str) -> Result<(), String> {
|
||||
let plugin = self
|
||||
.plugins
|
||||
@@ -226,6 +305,11 @@ impl PluginManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists all plugins along with their loaded status.
|
||||
///
|
||||
/// # Returns
|
||||
/// A vector of tuples containing references to the plugin metadata and a boolean indicating
|
||||
/// whether each plugin is loaded.
|
||||
#[must_use]
|
||||
pub fn list_plugins(&self) -> Vec<(&PluginMetadata, &bool)> {
|
||||
self.plugins
|
||||
@@ -234,9 +318,22 @@ impl PluginManager {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Asynchronously registers an event handler for a specific event type.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// - `E`: The event type that the handler will respond to.
|
||||
/// - `H`: The type of the event handler.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `handler`: A reference to the event handler.
|
||||
/// - `priority`: The priority of the event handler.
|
||||
/// - `blocking`: A boolean indicating whether the handler is blocking.
|
||||
///
|
||||
/// # Constraints
|
||||
/// The handler must implement the `EventHandler<E>` trait.
|
||||
pub async fn register<E: Event + 'static, H>(
|
||||
&self,
|
||||
handler: H,
|
||||
handler: Arc<H>,
|
||||
priority: EventPriority,
|
||||
blocking: bool,
|
||||
) where
|
||||
@@ -258,6 +355,16 @@ impl PluginManager {
|
||||
handlers_vec.push(Box::new(typed_handler));
|
||||
}
|
||||
|
||||
/// Asynchronously fires an event, invoking all registered handlers for that event type.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// - `E`: The event type to fire.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `event`: The event to fire.
|
||||
///
|
||||
/// # Returns
|
||||
/// The event after all handlers have processed it.
|
||||
pub async fn fire<E: Event + Send + Sync + 'static>(&self, mut event: E) -> E {
|
||||
// Take a snapshot of handlers to avoid lifetime issues
|
||||
let handlers = self.handlers.read().await;
|
||||
@@ -275,6 +382,7 @@ impl PluginManager {
|
||||
.iter()
|
||||
.partition(|handler| handler.is_blocking());
|
||||
|
||||
// Handle blocking handlers first
|
||||
for handler in blocking_handlers {
|
||||
handler.handle_blocking_dyn(&mut event).await;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{atomic::Ordering, Arc},
|
||||
};
|
||||
|
||||
pub mod level_time;
|
||||
pub mod player_chunker;
|
||||
@@ -10,6 +13,7 @@ use crate::{
|
||||
plugin::{
|
||||
block::BlockBreakEvent,
|
||||
player::{PlayerJoinEvent, PlayerLeaveEvent},
|
||||
world::{ChunkLoad, ChunkSave, ChunkSend},
|
||||
},
|
||||
server::Server,
|
||||
PLUGIN_MANAGER,
|
||||
@@ -21,6 +25,7 @@ use pumpkin_data::{
|
||||
sound::{Sound, SoundCategory},
|
||||
world::WorldEvent,
|
||||
};
|
||||
use pumpkin_macros::send_cancellable;
|
||||
use pumpkin_protocol::client::play::{CBlockUpdate, CDisguisedChatMessage, CRespawn, CWorldEvent};
|
||||
use pumpkin_protocol::{client::play::CLevelEvent, codec::identifier::Identifier};
|
||||
use pumpkin_protocol::{
|
||||
@@ -594,11 +599,13 @@ impl World {
|
||||
let level = self.level.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(chunk_data) = receiver.recv().await {
|
||||
let chunk_data = chunk_data.read().await;
|
||||
let packet = CChunkData(&chunk_data);
|
||||
'main: while let Some((chunk, first_load)) = receiver.recv().await {
|
||||
let position = chunk.read().await.position;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if chunk_data.position == (0, 0).into() {
|
||||
if position == (0, 0).into() {
|
||||
let binding = chunk.read().await;
|
||||
let packet = CChunkData(&binding);
|
||||
let mut test = bytes::BytesMut::new();
|
||||
packet.write(&mut test);
|
||||
let len = test.len();
|
||||
@@ -610,21 +617,60 @@ impl World {
|
||||
);
|
||||
}
|
||||
|
||||
if !level.is_chunk_watched(&chunk_data.position) {
|
||||
log::trace!(
|
||||
"Received chunk {:?}, but it is no longer watched... cleaning",
|
||||
&chunk_data.position
|
||||
);
|
||||
level.clean_chunk(&chunk_data.position).await;
|
||||
continue;
|
||||
}
|
||||
let (world, chunk) = if level.is_chunk_watched(&position) {
|
||||
(player.world().clone(), chunk)
|
||||
} else {
|
||||
send_cancellable! {{
|
||||
ChunkSave {
|
||||
world: player.world().clone(),
|
||||
chunk,
|
||||
cancelled: false,
|
||||
};
|
||||
|
||||
if !player
|
||||
.client
|
||||
.closed
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
player.client.send_packet(&packet).await;
|
||||
'after: {
|
||||
log::trace!(
|
||||
"Received chunk {:?}, but it is no longer watched... cleaning",
|
||||
&position
|
||||
);
|
||||
level.clean_chunk(&position).await;
|
||||
continue 'main;
|
||||
}
|
||||
}};
|
||||
(event.world, event.chunk)
|
||||
};
|
||||
|
||||
let (world, chunk) = if first_load {
|
||||
send_cancellable! {{
|
||||
ChunkLoad {
|
||||
world,
|
||||
chunk,
|
||||
cancelled: false,
|
||||
};
|
||||
|
||||
'cancelled: {
|
||||
continue 'main;
|
||||
}
|
||||
}}
|
||||
(event.world, event.chunk)
|
||||
} else {
|
||||
(world, chunk)
|
||||
};
|
||||
|
||||
if !player.client.closed.load(Ordering::Relaxed) {
|
||||
send_cancellable! {{
|
||||
ChunkSend {
|
||||
world,
|
||||
chunk,
|
||||
cancelled: false,
|
||||
};
|
||||
|
||||
'after: {
|
||||
player
|
||||
.client
|
||||
.send_packet(&CChunkData(&*event.chunk.read().await))
|
||||
.await;
|
||||
}
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,7 +926,7 @@ impl World {
|
||||
// Since we divide by 16 remnant can never exceed u8
|
||||
let relative = ChunkRelativeBlockCoordinates::from(relative_coordinates);
|
||||
|
||||
let chunk = self.receive_chunk(chunk_coordinate).await;
|
||||
let chunk = self.receive_chunk(chunk_coordinate).await.0;
|
||||
let replaced_block_state_id = chunk.read().await.subchunks.get_block(relative).unwrap();
|
||||
chunk
|
||||
.write()
|
||||
@@ -900,7 +946,10 @@ impl World {
|
||||
// Stream the chunks (don't collect them and then do stuff with them)
|
||||
/// Important: must be called from an async function (or changed to accept a tokio runtime
|
||||
/// handle)
|
||||
pub fn receive_chunks(&self, chunks: Vec<Vector2<i32>>) -> Receiver<Arc<RwLock<ChunkData>>> {
|
||||
pub fn receive_chunks(
|
||||
&self,
|
||||
chunks: Vec<Vector2<i32>>,
|
||||
) -> Receiver<(Arc<RwLock<ChunkData>>, bool)> {
|
||||
let (sender, receive) = mpsc::channel(chunks.len());
|
||||
// Put this in another thread so we aren't blocking on it
|
||||
let level = self.level.clone();
|
||||
@@ -911,7 +960,7 @@ impl World {
|
||||
receive
|
||||
}
|
||||
|
||||
pub async fn receive_chunk(&self, chunk_pos: Vector2<i32>) -> Arc<RwLock<ChunkData>> {
|
||||
pub async fn receive_chunk(&self, chunk_pos: Vector2<i32>) -> (Arc<RwLock<ChunkData>>, bool) {
|
||||
let mut receiver = self.receive_chunks(vec![chunk_pos]);
|
||||
let chunk = receiver
|
||||
.recv()
|
||||
@@ -962,7 +1011,7 @@ impl World {
|
||||
pub async fn get_block_state_id(&self, position: &BlockPos) -> Result<u16, GetBlockError> {
|
||||
let (chunk, relative) = position.chunk_and_chunk_relative_position();
|
||||
let relative = ChunkRelativeBlockCoordinates::from(relative);
|
||||
let chunk = self.receive_chunk(chunk).await;
|
||||
let chunk = self.receive_chunk(chunk).await.0;
|
||||
let chunk: tokio::sync::RwLockReadGuard<ChunkData> = chunk.read().await;
|
||||
|
||||
let Some(id) = chunk.subchunks.get_block(relative) else {
|
||||
|
||||
Reference in New Issue
Block a user