Overview
Worlds in PocketMine-MP represent dimensions where players interact with blocks and entities. TheWorldManager handles loading, unloading, and ticking of all worlds.
World Class
TheWorld class manages a single dimension:
World.php:151
class World implements ChunkManager {
public const Y_MAX = 320;
public const Y_MIN = -64;
public const TIME_DAY = 1000;
public const TIME_NOON = 6000;
public const TIME_SUNSET = 12000;
public const TIME_NIGHT = 13000;
public const TIME_MIDNIGHT = 18000;
public const TIME_SUNRISE = 23000;
public const TIME_FULL = 24000;
public const DIFFICULTY_PEACEFUL = 0;
public const DIFFICULTY_EASY = 1;
public const DIFFICULTY_NORMAL = 2;
public const DIFFICULTY_HARD = 3;
private string $folderName;
private string $displayName;
private array $chunks = [];
private array $players = [];
private array $entities = [];
}
WorldManager
Accessing Worlds
use pocketmine\Server;
$server = Server::getInstance();
$worldManager = $server->getWorldManager();
// Get default world
$defaultWorld = $worldManager->getDefaultWorld();
// Get world by name
$world = $worldManager->getWorldByName("world");
if ($world === null) {
$server->getLogger()->error("World not found!");
}
// Get all loaded worlds
$worlds = $worldManager->getWorlds();
foreach ($worlds as $world) {
$server->getLogger()->info("Loaded: " . $world->getDisplayName());
}
Loading Worlds
$worldManager = $server->getWorldManager();
// Load existing world
if ($worldManager->loadWorld("my_world")) {
$world = $worldManager->getWorldByName("my_world");
$server->getLogger()->info("World loaded: " . $world->getFolderName());
} else {
$server->getLogger()->error("Failed to load world!");
}
// Check if world is loaded
if ($worldManager->isWorldLoaded("my_world")) {
// World is available
}
Creating Worlds
use pocketmine\world\WorldCreationOptions;
use pocketmine\world\generator\GeneratorManager;
$options = WorldCreationOptions::create()
->setSeed(12345)
->setGeneratorClass(GeneratorManager::getInstance()->getGenerator("normal"))
->setDifficulty(World::DIFFICULTY_NORMAL);
if ($worldManager->generateWorld("new_world", $options)) {
$worldManager->loadWorld("new_world");
$world = $worldManager->getWorldByName("new_world");
$server->getLogger()->info("Created world: " . $world->getDisplayName());
}
Unloading Worlds
$world = $worldManager->getWorldByName("my_world");
if ($world !== null) {
// Kick all players to default world
foreach ($world->getPlayers() as $player) {
$player->teleport($worldManager->getDefaultWorld()->getSafeSpawn());
}
// Unload the world
if ($worldManager->unloadWorld($world)) {
$server->getLogger()->info("World unloaded");
}
}
You cannot unload the default world. Players must be moved before unloading.
Chunk Management
What is a Chunk?
Chunks are 16x16 vertical sections of the world, from Y_MIN (-64) to Y_MAX (320).┌─────────┐
│ 16 x 16 │
│ │ Y: -64 to 320
│ Chunk │
└─────────┘
Loading Chunks
$world = $server->getWorldManager()->getDefaultWorld();
$chunkX = 0;
$chunkZ = 0;
// Check if chunk is loaded
if ($world->isChunkLoaded($chunkX, $chunkZ)) {
$chunk = $world->getChunk($chunkX, $chunkZ);
// Use chunk
}
// Request chunk load (async)
$world->requestChunkPopulation($chunkX, $chunkZ)->onCompletion(
function() use ($world, $chunkX, $chunkZ): void {
$chunk = $world->getChunk($chunkX, $chunkZ);
// Chunk is now loaded and populated
},
function(): void {
// Failed to load
}
);
Chunk Loaders
Chunks stay loaded while they have “loaders” (usually players):use pocketmine\world\ChunkLoader;
use pocketmine\world\ChunkTicker;
class MyChunkLoader implements ChunkLoader {
public function getChunkLoaderX(): int {
return $this->x >> 4; // Block X to chunk X
}
public function getChunkLoaderZ(): int {
return $this->z >> 4;
}
}
// Register chunk loader
$world->registerChunkLoader($loader, $chunkX, $chunkZ);
// Unregister
$world->unregisterChunkLoader($loader, $chunkX, $chunkZ);
Chunks with no loaders are automatically unloaded after a delay.
World Generators
Built-in Generators
use pocketmine\world\generator\GeneratorManager;
$generatorManager = GeneratorManager::getInstance();
// Available generators:
// - "normal" (overworld)
// - "flat" (superflat)
// - "nether"
// - "end"
$generator = $generatorManager->getGenerator("normal");
Generator Base Class
Generator.php:34
abstract class Generator {
protected Random $random;
public function __construct(
protected int $seed,
protected string $preset
) {
$this->random = new Random($seed);
}
abstract public function generateChunk(ChunkManager $world, int $chunkX, int $chunkZ): void;
abstract public function populateChunk(ChunkManager $world, int $chunkX, int $chunkZ): void;
}
Registering Custom Generators
use pocketmine\world\generator\Generator;
use pocketmine\world\generator\GeneratorManager;
use pocketmine\world\ChunkManager;
class MyGenerator extends Generator {
public function generateChunk(ChunkManager $world, int $chunkX, int $chunkZ): void {
// Generate terrain
$chunk = $world->getChunk($chunkX, $chunkZ);
for ($x = 0; $x < 16; $x++) {
for ($z = 0; $z < 16; $z++) {
$chunk->setBlockStateId($x, 64, $z, BlockStateIds::GRASS);
}
}
}
public function populateChunk(ChunkManager $world, int $chunkX, int $chunkZ): void {
// Add trees, ores, etc.
}
}
// Register in onLoad()
protected function onLoad(): void {
GeneratorManager::getInstance()->addGenerator(
MyGenerator::class,
"mygen",
fn() => null
);
}
Block Operations
Getting Blocks
use pocketmine\math\Vector3;
$world = $player->getWorld();
$position = new Vector3(100, 64, 200);
// Get block at position
$block = $world->getBlock($position);
$blockName = $block->getName();
$blockId = $block->getTypeId();
// Batch get blocks
for ($x = 0; $x < 10; $x++) {
for ($y = 60; $y < 70; $y++) {
for ($z = 0; $z < 10; $z++) {
$block = $world->getBlockAt($x, $y, $z);
}
}
}
Setting Blocks
use pocketmine\block\VanillaBlocks;
$world = $player->getWorld();
// Set single block
$world->setBlock($position, VanillaBlocks::STONE());
// Set with update
$world->setBlock($position, VanillaBlocks::WATER(), true); // true = update neighbors
// Set multiple blocks efficiently
$blocks = [
new Vector3(0, 64, 0) => VanillaBlocks::GRASS(),
new Vector3(1, 64, 0) => VanillaBlocks::DIRT(),
new Vector3(2, 64, 0) => VanillaBlocks::STONE(),
];
foreach ($blocks as $pos => $block) {
$world->setBlock($pos, $block, false); // false = don't update yet
}
// Update all at once if needed
Set
$update to false when placing many blocks to improve performance, then manually trigger updates.Entity Management
Spawning Entities
use pocketmine\entity\EntityFactory;
use pocketmine\entity\Location;
use pocketmine\nbt\tag\CompoundTag;
$world = $player->getWorld();
$location = new Location(100, 65, 200, $world, 0, 0);
// Create entity
$entity = EntityFactory::getInstance()->create(
"minecraft:cow",
$location
);
if ($entity !== null) {
$entity->spawnToAll();
}
Getting Entities
$world = $player->getWorld();
// Get all entities
$entities = $world->getEntities();
foreach ($entities as $entity) {
$server->getLogger()->info("Entity: " . $entity->getName());
}
// Get nearby entities
$nearbyEntities = $world->getNearbyEntities(
new AxisAlignedBB(95, 60, 195, 105, 70, 205)
);
// Get specific entity type
use pocketmine\entity\Living;
$livingEntities = array_filter(
$world->getEntities(),
fn($e) => $e instanceof Living
);
Time and Weather
World Time
$world = $player->getWorld();
// Get time
$time = $world->getTime();
// Set time
$world->setTime(World::TIME_DAY); // Sunrise
$world->setTime(World::TIME_NOON); // Noon
$world->setTime(World::TIME_SUNSET); // Sunset
$world->setTime(World::TIME_MIDNIGHT); // Midnight
// Stop time
$world->stopTime();
// Start time
$world->startTime();
// Check if time is stopped
if ($world->stopTime) {
$player->sendMessage("Time is frozen!");
}
Difficulty
// Get difficulty
$difficulty = $world->getDifficulty();
// Set difficulty
$world->setDifficulty(World::DIFFICULTY_HARD);
switch ($world->getDifficulty()) {
case World::DIFFICULTY_PEACEFUL:
$player->sendMessage("Peaceful mode");
break;
case World::DIFFICULTY_EASY:
$player->sendMessage("Easy mode");
break;
case World::DIFFICULTY_NORMAL:
$player->sendMessage("Normal mode");
break;
case World::DIFFICULTY_HARD:
$player->sendMessage("Hard mode");
break;
}
Spawn Management
$world = $player->getWorld();
// Get spawn position
$spawn = $world->getSpawnLocation();
// Set spawn position
$world->setSpawnLocation(new Vector3(0, 65, 0));
// Get safe spawn (finds safe Y level)
$safeSpawn = $world->getSafeSpawn();
$player->teleport($safeSpawn);
Particles and Sounds
Adding Particles
use pocketmine\world\particle\FlameParticle;
use pocketmine\world\particle\HugeExplodeParticle;
$world = $player->getWorld();
$position = $player->getPosition();
// Add particle
$world->addParticle($position, new FlameParticle());
// Add to specific players
$world->addParticle($position, new HugeExplodeParticle(), [$player]);
Playing Sounds
use pocketmine\world\sound\ClickSound;
use pocketmine\world\sound\EndermanTeleportSound;
$world = $player->getWorld();
// Play sound to all nearby
$world->addSound($position, new ClickSound());
// Play to specific players
$world->addSound($position, new EndermanTeleportSound(), [$player]);
World Events
use pocketmine\event\Listener;
use pocketmine\event\world\ChunkLoadEvent;
use pocketmine\event\world\ChunkUnloadEvent;
use pocketmine\event\world\WorldLoadEvent;
use pocketmine\event\world\WorldSaveEvent;
class WorldListener implements Listener {
public function onChunkLoad(ChunkLoadEvent $event): void {
$world = $event->getWorld();
$chunkX = $event->getChunkX();
$chunkZ = $event->getChunkZ();
// Chunk was loaded
}
public function onWorldLoad(WorldLoadEvent $event): void {
$world = $event->getWorld();
// World finished loading
}
public function onWorldSave(WorldSaveEvent $event): void {
$world = $event->getWorld();
// World is being saved
}
}
Best Practices
Chunk Loading
Chunk Loading
Always check if chunks are loaded before accessing:
$chunkX = $position->getFloorX() >> 4;
$chunkZ = $position->getFloorZ() >> 4;
if ($world->isChunkLoaded($chunkX, $chunkZ)) {
$block = $world->getBlock($position);
}
Batch Operations
Batch Operations
Disable updates for bulk block changes:
for ($i = 0; $i < 1000; $i++) {
$world->setBlock($positions[$i], $blocks[$i], false);
}
Async Chunk Operations
Async Chunk Operations
Use promises for chunk loading:
$world->requestChunkPopulation($chunkX, $chunkZ)->onCompletion(
function() { /* success */ },
function() { /* failure */ }
);
World Unloading
World Unloading
Always move players before unloading:
foreach ($world->getPlayers() as $player) {
$player->teleport($defaultWorld->getSafeSpawn());
}
$worldManager->unloadWorld($world);