//! 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; pub(crate) struct Task { handler: TaskHandler, } pub(crate) static TASK_HANDLERS: Mutex = Mutex::new(LazyTaskHandlers { handlers: BTreeMap::new(), next_id: 0, }); pub(crate) struct LazyTaskHandlers { handlers: BTreeMap, 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(&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(&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(&self, delay_ticks: u64, handler: F) -> u32 where F: FnMut(Server) + Send + 'static, { schedule_delayed_task(delay_ticks, handler) } fn schedule_repeating_task(&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(&self, delay_ticks: u64, handler: F) -> u32 where F: FnMut(Self) + Send + 'static, { schedule_delayed_task(delay_ticks, handler) } fn schedule_repeating_task(&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(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(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); }