This is the most critical issue. Accessing server, plugin, world, or player objects from onRun() will cause crashes.
NEVER access these in onRun():
getServer()
getPlugin()
getOwningPlugin()
getPlayer()
getWorld()
getLevel()
Dangerous code that will crash:
class BadTask extends AsyncTask { private Server $server; public function onRun(): void { // CRASH! Cannot access server from another thread $this->server->broadcastMessage("Hello"); // CRASH! Cannot access plugin $this->getPlugin()->getLogger()->info("Working"); // CRASH! Cannot get players $player = $this->getPlayer(); }}
Safe approach:
class SafeTask extends AsyncTask { private string $playerName; public function __construct(string $playerName) { // Store data needed for processing $this->playerName = $playerName; } public function onRun(): void { // Safe: Only using primitive data $result = $this->performHeavyCalculation($this->playerName); $this->storeLocal("result", $result); } public function onCompletion(): void { // Now it's safe to access server objects $result = $this->fetchLocal("result"); Server::getInstance()->broadcastMessage("Task complete: " . $result); } private function performHeavyCalculation(string $name): string { // Heavy processing here return strtoupper($name); }}
If you use storeLocal() but never call fetchLocal(), the data is wasted.Incorrect:
class WastedDataTask extends AsyncTask { public function onRun(): void { $data = ["result" => 123]; $this->storeLocal("data", $data); } public function onCompletion(): void { // Never using the stored data! }}
Correct:
class ProperDataTask extends AsyncTask { public function onRun(): void { $data = ["result" => 123]; $this->storeLocal("data", $data); } public function onCompletion(): void { $data = $this->fetchLocal("data"); // Use the data Server::getInstance()->getLogger()->info("Result: " . $data["result"]); }}
The correct pattern for passing data between threads:
use pocketmine\scheduler\AsyncTask;use pocketmine\Server;class DatabaseQueryTask extends AsyncTask { private string $query; private array $params; public function __construct(string $query, array $params) { // 1. Pass data through constructor $this->query = $query; $this->params = $params; } public function onRun(): void { // 2. Use the data in background thread $db = new \SQLite3("path/to/database.db"); $stmt = $db->prepare($this->query); foreach ($this->params as $i => $param) { $stmt->bindValue($i + 1, $param); } $result = $stmt->execute(); $rows = []; while ($row = $result->fetchArray(SQLITE3_ASSOC)) { $rows[] = $row; } // 3. Store results $this->storeLocal("rows", $rows); } public function onCompletion(): void { // 4. Fetch and use results on main thread $rows = $this->fetchLocal("rows"); // Now safe to interact with server Server::getInstance()->getLogger()->info( "Query returned " . count($rows) . " rows" ); // Safe to access players, worlds, etc. foreach ($rows as $row) { // Process results } }}
class DatabaseTask extends AsyncTask { private string $dbPath; private string $query; public function onRun(): void { $db = new \SQLite3($this->dbPath); $result = $db->query($this->query); $this->storeLocal("result", $result->fetchArray()); } public function onCompletion(): void { $data = $this->fetchLocal("result"); // Use data safely }}
File operations
class FileProcessTask extends AsyncTask { private string $filePath; public function onRun(): void { $content = file_get_contents($this->filePath); $processed = $this->processLargeFile($content); $this->storeLocal("processed", $processed); } public function onCompletion(): void { $result = $this->fetchLocal("processed"); // Update game state with result }}
Web API requests
class ApiRequestTask extends AsyncTask { private string $url; public function onRun(): void { $response = file_get_contents($this->url); $data = json_decode($response, true); $this->storeLocal("data", $data); } public function onCompletion(): void { $data = $this->fetchLocal("data"); // Update players with API data }}
class Main extends PluginBase { public function performDatabaseQuery(string $query): void { $task = new DatabaseQueryTask($query); // Submit to async pool Server::getInstance()->getAsyncPool()->submitTask($task); }}
AsyncTasks run on separate threads. Understanding thread safety is crucial for stable plugins.