Task scheduler, async tasks, repeating tasks in PocketMine-MP
The task scheduler allows you to run code at specific times, with delays, or repeatedly. This is essential for timers, countdowns, auto-save systems, and more.
Tasks extend the Task class and implement onRun():
src/MyPlugin/tasks/MyTask.php
<?phpdeclare(strict_types=1);namespace MyPlugin\tasks;use pocketmine\scheduler\Task;use MyPlugin\Main;class MyTask extends Task { public function __construct( private Main $plugin ){} public function onRun() : void { // This code runs when the task executes $this->plugin->getLogger()->info("Task executed!"); }}
// Run after 20 ticks (1 second)$this->getScheduler()->scheduleDelayedTask(new MyTask($this), 20);// Run after 5 seconds (100 ticks)$this->getScheduler()->scheduleDelayedTask(new MyTask($this), 100);
PocketMine-MP runs at 20 ticks per second (TPS). 1 tick = 0.05 seconds = 50ms.
// Run every 20 ticks (every second)$this->getScheduler()->scheduleRepeatingTask( new MyTask($this), 20 // period in ticks);// Run every 5 seconds$this->getScheduler()->scheduleRepeatingTask( new MyTask($this), 100);
Execute a task repeatedly, but start after a delay:
// Wait 60 ticks (3 seconds), then run every 20 ticks (1 second)$this->getScheduler()->scheduleDelayedRepeatingTask( new MyTask($this), 60, // delay before first run 20 // period between runs);
// Save the handler when scheduling$handler = $this->getScheduler()->scheduleRepeatingTask( new MyTask($this), 20);// Cancel the task later$handler->cancel();
<?phpdeclare(strict_types=1);namespace MyPlugin\tasks;use pocketmine\scheduler\Task;use MyPlugin\Main;class AutoSaveTask extends Task { public function __construct( private Main $plugin ){} public function onRun() : void { // Save all player data foreach($this->plugin->getServer()->getOnlinePlayers() as $player) { $this->plugin->savePlayerData($player); } // Save plugin data $this->plugin->getConfig()->save(); $this->plugin->getLogger()->info("Auto-saved player data"); }}// In Main.php onEnable():protected function onEnable() : void { // Auto-save every 5 minutes (6000 ticks) $this->getScheduler()->scheduleRepeatingTask( new AutoSaveTask($this), 6000 );}
public function teleportWithDelay(Player $player, Position $destination) : void { $player->sendMessage("§eTeleporting in 3 seconds..."); // Save player's position to check if they moved $originalPos = $player->getPosition(); $this->getScheduler()->scheduleDelayedTask( new ClosureTask(function() use ($player, $destination, $originalPos) : void { // Check if player moved if($player->getPosition()->distance($originalPos) > 0.5) { $player->sendMessage("§cTeleport cancelled - you moved!"); return; } $player->teleport($destination); $player->sendMessage("§aTeleported!"); }), 60 // 3 seconds );}
<?phpdeclare(strict_types=1);namespace MyPlugin\tasks;use pocketmine\scheduler\AsyncTask;class MyAsyncTask extends AsyncTask { private string $data; public function __construct(string $data) { $this->data = $data; } // Runs on worker thread (cannot access Server) public function onRun() : void { // Do heavy computation $result = []; for($i = 0; $i < 1000000; $i++) { $result[] = hash('sha256', $this->data . $i); } // Store result (must be serializable) $this->setResult(count($result)); } // Runs on main thread when task completes public function onCompletion() : void { $result = $this->getResult(); // Can access Server here }}
class TimeHelper { public const TICKS_PER_SECOND = 20; public const TICKS_PER_MINUTE = 20 * 60; public const TICKS_PER_HOUR = 20 * 60 * 60; public static function secondsToTicks(float $seconds) : int { return (int) ($seconds * self::TICKS_PER_SECOND); } public static function minutesToTicks(float $minutes) : int { return (int) ($minutes * self::TICKS_PER_MINUTE); }}// Usage:$this->getScheduler()->scheduleDelayedTask( new MyTask($this), TimeHelper::secondsToTicks(5) // 5 seconds);$this->getScheduler()->scheduleRepeatingTask( new MyTask($this), TimeHelper::minutesToTicks(1) // every minute);
$handler = $this->getScheduler()->scheduleRepeatingTask(new MyTask($this), 20);// Cancel the task$handler->cancel();// Check if cancelledif($handler->isCancelled()) { // Task is cancelled}// Check if it's a repeating taskif($handler->isRepeating()) { // This is a repeating task}// Check if it has a delayif($handler->isDelayed()) { // This task has a delay before first run}// Get the task instance$task = $handler->getTask();
$this->getScheduler()->scheduleDelayedTask( new ClosureTask(fn() => $player->sendMessage("Hello!")), 20);
Create Task classes for complex logic:
class ComplexTask extends Task { // Multiple methods, properties, logic}
Always cancel repeating tasks:
private ?TaskHandler $repeatingTask = null;protected function onEnable() : void { $this->repeatingTask = $this->getScheduler()->scheduleRepeatingTask(...);}protected function onDisable() : void { $this->repeatingTask?->cancel();}
Don’t schedule too many tasks:
Each task has overhead. Instead of 1000 individual tasks, use one task that processes multiple items.Use delays wisely:
Don’t schedule a task every tick (20 times per second) unless absolutely necessary. This impacts performance.
class DebugTask extends Task { private int $runCount = 0; public function __construct( private Main $plugin ){} public function onRun() : void { $this->runCount++; $this->plugin->getLogger()->debug( "Task ran {$this->runCount} times" ); // Auto-cancel after 10 runs if($this->runCount >= 10) { $this->plugin->getLogger()->info("Task finished"); $this->getHandler()->cancel(); } }}