Split up ChunkLoader and ChunkListener

This commit is contained in:
Dylan K. Taylor 2019-02-02 19:51:41 +00:00
parent 2c0f91ce50
commit 923b1ad9a6
4 changed files with 149 additions and 58 deletions

View File

@ -84,6 +84,7 @@ use pocketmine\item\WritableBook;
use pocketmine\item\WrittenBook;
use pocketmine\lang\TextContainer;
use pocketmine\lang\TranslationContainer;
use pocketmine\level\ChunkListener;
use pocketmine\level\ChunkLoader;
use pocketmine\level\format\Chunk;
use pocketmine\level\Level;
@ -174,7 +175,7 @@ use const PHP_INT_MAX;
/**
* Main class that handles networking, recovery, and packet sending to the server part
*/
class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, IPlayer{
/**
* Checks a supplied username and checks it is valid.
@ -920,6 +921,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
unset($this->usedChunks[$index]);
}
$level->unregisterChunkLoader($this, $x, $z);
$level->unregisterChunkListener($this, $x, $z);
unset($this->loadQueue[$index]);
}
@ -977,6 +979,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->usedChunks[$index] = false;
$this->level->registerChunkLoader($this, $X, $Z, true);
$this->level->registerChunkListener($this, $X, $Z);
if(!$this->level->populateChunk($X, $Z)){
continue;
@ -1840,6 +1843,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//load the spawn chunk so we can see the terrain
$level->registerChunkLoader($this, $spawn->getFloorX() >> 4, $spawn->getFloorZ() >> 4, true);
$level->registerChunkListener($this, $spawn->getFloorX() >> 4, $spawn->getFloorZ() >> 4);
if($spawnReset){
$spawn = $level->getSafeSpawn($spawn);
}
@ -2880,6 +2884,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
foreach($this->usedChunks as $index => $d){
Level::getXZ($index, $chunkX, $chunkZ);
$this->level->unregisterChunkLoader($this, $chunkX, $chunkZ);
$this->level->unregisterChunkListener($this, $chunkX, $chunkZ);
foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){
$entity->despawnFrom($this);
}

View File

@ -0,0 +1,80 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\level;
use pocketmine\block\Block;
use pocketmine\level\format\Chunk;
use pocketmine\math\Vector3;
/**
* This interface allows you to listen for events occurring on or in specific chunks. This will receive events for any
* chunks which it is registered to listen to.
*
* @see Level::registerChunkListener()
* @see Level::unregisterChunkListener()
*
* WARNING: When you're done with the listener, make sure you unregister it from all chunks it's listening to, otherwise
* the object will not be destroyed.
* The listener WILL NOT be unregistered when chunks are unloaded. You need to do this yourself when you're done with
* a chunk.
*/
interface ChunkListener{
/**
* This method will be called when a Chunk is replaced by a new one
*
* @param Chunk $chunk
*/
public function onChunkChanged(Chunk $chunk);
/**
* This method will be called when a registered chunk is loaded
*
* @param Chunk $chunk
*/
public function onChunkLoaded(Chunk $chunk);
/**
* This method will be called when a registered chunk is unloaded
*
* @param Chunk $chunk
*/
public function onChunkUnloaded(Chunk $chunk);
/**
* This method will be called when a registered chunk is populated
* Usually it'll be sent with another call to onChunkChanged()
*
* @param Chunk $chunk
*/
public function onChunkPopulated(Chunk $chunk);
/**
* This method will be called when a block changes in a registered chunk
*
* @param Block|Vector3 $block
*/
public function onBlockChanged(Vector3 $block);
}

View File

@ -23,19 +23,14 @@ declare(strict_types=1);
namespace pocketmine\level;
use pocketmine\block\Block;
use pocketmine\level\format\Chunk;
use pocketmine\math\Vector3;
/**
* If you want to keep chunks loaded and receive notifications on a specific area,
* extend this class and register it into Level. This will also tick chunks.
* If you want to keep chunks loaded, implement this interface and register it into Level. This will also tick chunks.
*
* Register Level->registerChunkLoader($this, $chunkX, $chunkZ)
* Unregister Level->unregisterChunkLoader($this, $chunkX, $chunkZ)
* @see Level::registerChunkLoader()
* @see Level::unregisterChunkLoader()
*
* WARNING: When moving this object around in the world or destroying it,
* be sure to free the existing references from Level, otherwise you'll leak memory.
* be sure to unregister the loader from chunks you're not using, otherwise you'll leak memory.
*/
interface ChunkLoader{
@ -48,42 +43,4 @@ interface ChunkLoader{
* @return float
*/
public function getZ();
/**
* This method will be called when a Chunk is replaced by a new one
*
* @param Chunk $chunk
*/
public function onChunkChanged(Chunk $chunk);
/**
* This method will be called when a registered chunk is loaded
*
* @param Chunk $chunk
*/
public function onChunkLoaded(Chunk $chunk);
/**
* This method will be called when a registered chunk is unloaded
*
* @param Chunk $chunk
*/
public function onChunkUnloaded(Chunk $chunk);
/**
* This method will be called when a registered chunk is populated
* Usually it'll be sent with another call to onChunkChanged()
*
* @param Chunk $chunk
*/
public function onChunkPopulated(Chunk $chunk);
/**
* This method will be called when a block changes in a registered chunk
*
* @param Block|Vector3 $block
*/
public function onBlockChanged(Vector3 $block);
}

View File

@ -179,6 +179,9 @@ class Level implements ChunkManager, Metadatable{
/** @var Player[][] */
private $playerLoaders = [];
/** @var ChunkListener[][] */
private $chunkListeners = [];
/** @var ClientboundPacket[][] */
private $chunkPackets = [];
/** @var ClientboundPacket[] */
@ -680,6 +683,52 @@ class Level implements ChunkManager, Metadatable{
}
}
/**
* Registers a listener to receive events on a chunk.
*
* @param ChunkListener $listener
* @param int $chunkX
* @param int $chunkZ
*/
public function registerChunkListener(ChunkListener $listener, int $chunkX, int $chunkZ) : void{
$hash = Level::chunkHash($chunkX, $chunkZ);
if(isset($this->chunkListeners[$hash])){
$this->chunkListeners[$hash][spl_object_id($listener)] = $listener;
}else{
$this->chunkListeners[$hash] = [spl_object_id($listener) => $listener];
}
}
/**
* Unregisters a chunk listener previously registered.
* @see Level::registerChunkListener()
*
* @param ChunkListener $listener
* @param int $chunkX
* @param int $chunkZ
*/
public function unregisterChunkListener(ChunkListener $listener, int $chunkX, int $chunkZ) : void{
$hash = Level::chunkHash($chunkX, $chunkZ);
if(isset($this->chunkListeners[$hash])){
unset($this->chunkListeners[$hash][spl_object_id($listener)]);
if(empty($this->chunkListeners[$hash])){
unset($this->chunkListeners[$hash]);
}
}
}
/**
* Returns all the listeners attached to this chunk.
*
* @param int $chunkX
* @param int $chunkZ
*
* @return ChunkListener[]
*/
public function getChunkListeners(int $chunkX, int $chunkZ) : array{
return $this->chunkListeners[Level::chunkHash($chunkX, $chunkZ)] ?? [];
}
/**
* @internal
*
@ -1539,8 +1588,8 @@ class Level implements ChunkManager, Metadatable{
}
$this->changedBlocks[$chunkHash][$relativeBlockHash] = $block;
foreach($this->getChunkLoaders($x >> 4, $z >> 4) as $loader){
$loader->onBlockChanged($block);
foreach($this->getChunkListeners($x >> 4, $z >> 4) as $listener){
$listener->onBlockChanged($block);
}
if($update){
@ -2229,8 +2278,8 @@ class Level implements ChunkManager, Metadatable{
if(($oldChunk === null or !$oldChunk->isPopulated()) and $chunk->isPopulated()){
(new ChunkPopulateEvent($this, $chunk))->call();
foreach($this->getChunkLoaders($x, $z) as $loader){
$loader->onChunkPopulated($chunk);
foreach($this->getChunkListeners($x, $z) as $listener){
$listener->onChunkPopulated($chunk);
}
}
}
@ -2301,8 +2350,8 @@ class Level implements ChunkManager, Metadatable{
if(!$this->isChunkInUse($chunkX, $chunkZ)){
$this->unloadChunkRequest($chunkX, $chunkZ);
}else{
foreach($this->getChunkLoaders($chunkX, $chunkZ) as $loader){
$loader->onChunkChanged($chunk);
foreach($this->getChunkListeners($chunkX, $chunkZ) as $listener){
$listener->onChunkChanged($chunk);
}
}
}
@ -2617,8 +2666,8 @@ class Level implements ChunkManager, Metadatable{
}
if($this->isChunkInUse($x, $z)){
foreach($this->getChunkLoaders($x, $z) as $loader){
$loader->onChunkLoaded($chunk);
foreach($this->getChunkListeners($x, $z) as $listener){
$listener->onChunkLoaded($chunk);
}
}else{
$this->server->getLogger()->debug("Newly loaded chunk $x $z has no loaders registered, will be unloaded at next available opportunity");
@ -2678,8 +2727,8 @@ class Level implements ChunkManager, Metadatable{
}
}
foreach($this->getChunkLoaders($x, $z) as $loader){
$loader->onChunkUnloaded($chunk);
foreach($this->getChunkListeners($x, $z) as $listener){
$listener->onChunkUnloaded($chunk);
}
$chunk->onUnload();