mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-15 02:09:42 +00:00
Added ChunkLoader interface
This commit is contained in:
parent
7a34417e67
commit
d6ebff412c
@ -74,6 +74,7 @@ use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\inventory\SimpleTransactionGroup;
|
||||
use pocketmine\inventory\StonecutterShapelessRecipe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\ChunkLoader;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\Level;
|
||||
@ -129,7 +130,7 @@ use pocketmine\utils\Utils;
|
||||
/**
|
||||
* Main class that handles networking, recovery, and packet sending to the server part
|
||||
*/
|
||||
class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
class Player extends Human implements CommandSender, InventoryHolder, ChunkLoader, IPlayer{
|
||||
|
||||
const SURVIVAL = 0;
|
||||
const CREATIVE = 1;
|
||||
@ -199,6 +200,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
protected $sleeping = null;
|
||||
protected $clientID = null;
|
||||
|
||||
private $loaderId = null;
|
||||
|
||||
protected $stepHeight = 0.6;
|
||||
|
||||
public $usedChunks = [];
|
||||
@ -492,6 +495,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->ip = $ip;
|
||||
$this->port = $port;
|
||||
$this->clientID = $clientID;
|
||||
$this->loaderId = Level::generateChunkLoaderId($this);
|
||||
$this->chunksPerTick = (int) $this->server->getProperty("chunk-sending.per-tick", 4);
|
||||
$this->spawnThreshold = (int) $this->server->getProperty("chunk-sending.spawn-threshold", 56);
|
||||
$this->spawnPosition = null;
|
||||
@ -527,11 +531,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isset($this->achievements[$achievementId]) or $this->achievements[$achievementId] == false){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return isset($this->achievements[$achievementId]) and $this->achievements[$achievementId] != false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -596,10 +596,21 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
return $this->sleeping !== null;
|
||||
}
|
||||
|
||||
public function unloadChunk($x, $z){
|
||||
protected function switchLevel(Level $targetLevel){
|
||||
$oldLevel = $this->level;
|
||||
if(parent::switchLevel($targetLevel)){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
$this->unloadChunk($X, $Z, $oldLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function unloadChunk($x, $z, Level $level = null){
|
||||
$level = $level === null ? $this->level : $level;
|
||||
$index = Level::chunkHash($x, $z);
|
||||
if(isset($this->usedChunks[$index])){
|
||||
foreach($this->level->getChunkEntities($x, $z) as $entity){
|
||||
foreach($level->getChunkEntities($x, $z) as $entity){
|
||||
if($entity !== $this){
|
||||
$entity->despawnFrom($this);
|
||||
}
|
||||
@ -607,7 +618,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
unset($this->usedChunks[$index]);
|
||||
}
|
||||
$this->level->freeChunk($x, $z, $this);
|
||||
$level->unregisterChunkLoader($this, $x, $z);
|
||||
unset($this->loadQueue[$index]);
|
||||
}
|
||||
|
||||
@ -672,11 +683,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unset($this->loadQueue[$index]);
|
||||
$this->usedChunks[$index] = false;
|
||||
|
||||
$this->level->useChunk($X, $Z, $this);
|
||||
$this->level->registerChunkLoader($this, $X, $Z);
|
||||
$this->level->requestChunk($X, $Z, $this, LevelProvider::ORDER_ZXY);
|
||||
}
|
||||
|
||||
@ -2757,7 +2767,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
$this->level->freeChunk($chunkX, $chunkZ, $this);
|
||||
$this->level->unregisterChunkLoader($this, $chunkX, $chunkZ);
|
||||
unset($this->usedChunks[$index]);
|
||||
}
|
||||
|
||||
@ -3174,4 +3184,32 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
}
|
||||
|
||||
|
||||
public function onChunkChanged(FullChunk $chunk){
|
||||
$this->loadQueue[Level::chunkHash($chunk->getX(), $chunk->getZ())] = abs(($this->x >> 4) - $chunk->getX()) + abs(($this->z >> 4) - $chunk->getZ());
|
||||
}
|
||||
|
||||
public function onChunkLoaded(FullChunk $chunk){
|
||||
|
||||
}
|
||||
|
||||
public function onChunkPopulated(FullChunk $chunk){
|
||||
|
||||
}
|
||||
|
||||
public function onChunkUnloaded(FullChunk $chunk){
|
||||
|
||||
}
|
||||
|
||||
public function onBlockChanged(Vector3 $block){
|
||||
|
||||
}
|
||||
|
||||
public function getLoaderId(){
|
||||
return $this->loaderId;
|
||||
}
|
||||
|
||||
public function isLoaderActive(){
|
||||
return $this->isConnected();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\command\defaults\DeopCommand;
|
||||
use pocketmine\command\defaults\DifficultyCommand;
|
||||
use pocketmine\command\defaults\EffectCommand;
|
||||
use pocketmine\command\defaults\GamemodeCommand;
|
||||
use pocketmine\command\defaults\GarbageCollectorCommand;
|
||||
use pocketmine\command\defaults\GiveCommand;
|
||||
use pocketmine\command\defaults\HelpCommand;
|
||||
use pocketmine\command\defaults\KickCommand;
|
||||
@ -115,6 +116,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
if($this->server->getProperty("debug.commands", false) === true){
|
||||
$this->register("pocketmine", new StatusCommand("status"));
|
||||
$this->register("pocketmine", new GarbageCollectorCommand("gc"));
|
||||
}
|
||||
}
|
||||
|
||||
|
67
src/pocketmine/command/defaults/GarbageCollectorCommand.php
Normal file
67
src/pocketmine/command/defaults/GarbageCollectorCommand.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\command\defaults;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class GarbageCollectorCommand extends VanillaCommand{
|
||||
|
||||
public function __construct($name){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"%pocketmine.command.gc.description",
|
||||
"%pocketmine.command.gc.usage"
|
||||
);
|
||||
$this->setPermission("pocketmine.command.gc");
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, $currentAlias, array $args){
|
||||
if(!$this->testPermission($sender)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$chunksCollected = 0;
|
||||
$entitiesCollected = 0;
|
||||
$tilesCollected = 0;
|
||||
|
||||
foreach($sender->getServer()->getLevels() as $level){
|
||||
$diff = [count($level->getChunks()), count($level->getEntities()), count($level->getTiles())];
|
||||
$level->doChunkGarbageCollection();
|
||||
$level->unloadChunks();
|
||||
$chunksCollected += $diff[0] - count($level->getChunks());
|
||||
$entitiesCollected += $diff[1] - count($level->getEntities());
|
||||
$tilesCollected += $diff[2] - count($level->getTiles());
|
||||
}
|
||||
|
||||
$cyclesCollected = $sender->getServer()->getMemoryManager()->triggerGarbageCollector();
|
||||
|
||||
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Garbage collection result" . TextFormat::GREEN . " ----");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Chunks: " . TextFormat::RED . number_format($chunksCollected));
|
||||
$sender->sendMessage(TextFormat::GOLD . "Entities: " . TextFormat::RED . number_format($entitiesCollected));
|
||||
$sender->sendMessage(TextFormat::GOLD . "Tiles: " . TextFormat::RED . number_format($tilesCollected));
|
||||
|
||||
$sender->sendMessage(TextFormat::GOLD . "Cycles: " . TextFormat::RED . number_format($cyclesCollected));
|
||||
return true;
|
||||
}
|
||||
}
|
@ -978,13 +978,8 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$this->chunk->removeEntity($this);
|
||||
}
|
||||
$this->despawnFromAll();
|
||||
if($this instanceof Player){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
$this->unloadChunk($X, $Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->setLevel($targetLevel);
|
||||
$this->level->addEntity($this);
|
||||
if($this instanceof Player){
|
||||
|
112
src/pocketmine/level/ChunkLoader.php
Normal file
112
src/pocketmine/level/ChunkLoader.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
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.
|
||||
*
|
||||
* Register Level->registerChunkLoader($this, $chunkX, $chunkZ)
|
||||
* Unregister Level->unregisterChunkLoader($this, $chunkX, $chunkZ)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
interface ChunkLoader{
|
||||
|
||||
/**
|
||||
* Returns the ChunkLoader id.
|
||||
* Call Level::generateChunkLoaderId($this) to generate and save it
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLoaderId();
|
||||
|
||||
/**
|
||||
* Returns if the chunk loader is currently active
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoaderActive();
|
||||
|
||||
/**
|
||||
* @return Position
|
||||
*/
|
||||
public function getPosition();
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getX();
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getZ();
|
||||
|
||||
/**
|
||||
* @return Level
|
||||
*/
|
||||
public function getLevel();
|
||||
|
||||
/**
|
||||
* This method will be called when a Chunk is replaced by a new one
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function onChunkChanged(FullChunk $chunk);
|
||||
|
||||
/**
|
||||
* This method will be called when a registered chunk is loaded
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function onChunkLoaded(FullChunk $chunk);
|
||||
|
||||
|
||||
/**
|
||||
* This method will be called when a registered chunk is unloaded
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function onChunkUnloaded(FullChunk $chunk);
|
||||
|
||||
/**
|
||||
* This method will be called when a registered chunk is populated
|
||||
* Usually it'll be sent with another call to onChunkChanged()
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function onChunkPopulated(FullChunk $chunk);
|
||||
|
||||
/**
|
||||
* This method will be called when a block changes in a registered chunk
|
||||
*
|
||||
* @param Block|Vector3 $block
|
||||
*/
|
||||
public function onBlockChanged(Vector3 $block);
|
||||
|
||||
}
|
@ -109,6 +109,7 @@ use pocketmine\level\particle\DestroyBlockParticle;
|
||||
class Level implements ChunkManager, Metadatable{
|
||||
|
||||
private static $levelIdCounter = 1;
|
||||
private static $chunkLoaderCounter = 1;
|
||||
public static $COMPRESSION_LEVEL = 8;
|
||||
|
||||
|
||||
@ -156,8 +157,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var LevelProvider */
|
||||
private $provider;
|
||||
|
||||
/** @var ChunkLoader[] */
|
||||
private $loaders = [];
|
||||
/** @var ChunkLoader[][] */
|
||||
private $chunkLoaders = [];
|
||||
/** @var Player[][] */
|
||||
private $usedChunks = [];
|
||||
private $playerLoaders = [];
|
||||
|
||||
/** @var FullChunk[]|Chunk[] */
|
||||
private $unloadQueue;
|
||||
@ -285,6 +290,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
public static function generateChunkLoaderId(ChunkLoader $loader){
|
||||
if($loader->getLoaderId() === 0 or $loader->getLoaderId() === null or $loader->getLoaderId() === null){
|
||||
return self::$chunkLoaderCounter++;
|
||||
}else{
|
||||
throw new \InvalidStateException("ChunkLoader has a loader id already assigned: " . $loader->getLoaderId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the default level data
|
||||
*
|
||||
@ -504,43 +517,76 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chunks being used by players
|
||||
* @deprecated Use Level->getChunkPlayers($chunkX, $chunkZ)
|
||||
*/
|
||||
public function getUsingChunk($chunkX, $chunkZ){
|
||||
return $this->getChunkPlayers($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the players being used in a specific chunk
|
||||
*
|
||||
* @param int $X
|
||||
* @param int $Z
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return Player[]
|
||||
*/
|
||||
public function getUsingChunk($X, $Z){
|
||||
return isset($this->usedChunks[$index = Level::chunkHash($X, $Z)]) ? $this->usedChunks[$index] : [];
|
||||
public function getChunkPlayers($chunkX, $chunkZ){
|
||||
return isset($this->playerLoaders[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->playerLoaders[$index] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: Do not use this, it's only for internal use.
|
||||
* Changes to this function won't be recorded on the version.
|
||||
* Gets the chunk loaders being used in a specific chunk
|
||||
*
|
||||
* @param int $X
|
||||
* @param int $Z
|
||||
* @param Player $player
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return ChunkLoader[]
|
||||
*/
|
||||
public function useChunk($X, $Z, Player $player){
|
||||
$index = Level::chunkHash($X, $Z);
|
||||
$this->loadChunk($X, $Z);
|
||||
$this->usedChunks[$index][$player->getId()] = $player;
|
||||
public function getChunkLoaders($chunkX, $chunkZ){
|
||||
return isset($this->chunkLoaders[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->chunkLoaders[$index] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: Do not use this, it's only for internal use.
|
||||
* Changes to this function won't be recorded on the version.
|
||||
*
|
||||
* @param int $X
|
||||
* @param int $Z
|
||||
* @param Player $player
|
||||
*/
|
||||
public function freeChunk($X, $Z, Player $player){
|
||||
unset($this->usedChunks[$index = Level::chunkHash($X, $Z)][$player->getId()]);
|
||||
public function registerChunkLoader(ChunkLoader $loader, $chunkX, $chunkZ, $autoLoad = true){
|
||||
$hash = spl_object_hash($loader);
|
||||
|
||||
$this->unloadChunkRequest($X, $Z, true);
|
||||
if(!isset($this->chunkLoaders[$index = Level::chunkHash($chunkX, $chunkZ)])){
|
||||
$this->chunkLoaders[$index] = [];
|
||||
$this->playerLoaders[$index] = [];
|
||||
}elseif(isset($this->chunkLoaders[$index][$hash])){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->chunkLoaders[$index][$hash] = $loader;
|
||||
if($loader instanceof Player){
|
||||
$this->playerLoaders[$index][$hash] = $loader;
|
||||
}
|
||||
|
||||
if(!isset($this->loaders[$hash])){
|
||||
$this->loaders[$hash] = 1;
|
||||
}else{
|
||||
++$this->loaders[$hash];
|
||||
}
|
||||
|
||||
if($autoLoad){
|
||||
$this->loadChunk($chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
public function unregisterChunkLoader(ChunkLoader $loader, $chunkX, $chunkZ){
|
||||
if(isset($this->chunkLoaders[$index = Level::chunkHash($chunkX, $chunkZ)][$hash = spl_object_hash($loader)])){
|
||||
unset($this->chunkLoaders[$index][$hash = spl_object_hash($loader)]);
|
||||
unset($this->playerLoaders[$index][$hash]);
|
||||
if(count($this->chunkLoaders[$index]) === 0){
|
||||
unset($this->chunkLoaders[$index]);
|
||||
unset($this->playerLoaders[$index]);
|
||||
$this->unloadChunkRequest($chunkX, $chunkZ, true);
|
||||
}
|
||||
|
||||
if(--$this->loaders[$hash] === 0){
|
||||
unset($this->loaders[$hash]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -632,13 +678,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(count($this->players) > 0){
|
||||
foreach($this->changedBlocks as $index => $blocks){
|
||||
unset($this->chunkCache[$index]);
|
||||
Level::getXZ($index, $X, $Z);
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
if(count($blocks) > 512){
|
||||
foreach($this->getUsingChunk($X, $Z) as $p){
|
||||
$p->unloadChunk($X, $Z);
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
foreach($this->getChunkPlayers($chunkX, $chunkZ) as $p){
|
||||
$p->onChunkChanged($chunk);
|
||||
}
|
||||
}else{
|
||||
$this->sendBlocks($this->getUsingChunk($X, $Z), $blocks, UpdateBlockPacket::FLAG_ALL);
|
||||
$this->sendBlocks($this->getChunkPlayers($chunkX, $chunkZ), $blocks, UpdateBlockPacket::FLAG_ALL);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@ -688,20 +735,46 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param Player[] $target
|
||||
* @param Block[] $blocks
|
||||
* @param int $flags
|
||||
* @param bool $optimizeRebuilds
|
||||
*/
|
||||
public function sendBlocks(array $target, array $blocks, $flags = UpdateBlockPacket::FLAG_NONE){
|
||||
public function sendBlocks(array $target, array $blocks, $flags = UpdateBlockPacket::FLAG_NONE, $optimizeRebuilds = false){
|
||||
$pk = new UpdateBlockPacket();
|
||||
foreach($blocks as $b){
|
||||
if($b === null){
|
||||
continue;
|
||||
|
||||
if($optimizeRebuilds){
|
||||
$chunks = [];
|
||||
foreach($blocks as $b){
|
||||
if($b === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$first = false;
|
||||
if(!isset($chunks[$index = Level::chunkHash($b->x >> 4, $b->z >> 4)])){
|
||||
$chunks[$index] = true;
|
||||
$first = true;
|
||||
}
|
||||
|
||||
if($b instanceof Block){
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $b->getId(), $b->getDamage(), $first ? $flags : UpdateBlockPacket::FLAG_NONE];
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $fullBlock >> 4, $fullBlock & 0xf, $first ? $flags : UpdateBlockPacket::FLAG_NONE];
|
||||
}
|
||||
}
|
||||
}else{
|
||||
foreach($blocks as $b){
|
||||
if($b === null){
|
||||
continue;
|
||||
}
|
||||
if($b instanceof Block){
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $b->getId(), $b->getDamage(), $flags];
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $fullBlock >> 4, $fullBlock & 0xf, $flags];
|
||||
}
|
||||
}
|
||||
if($b instanceof Block){
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $b->getId(), $b->getDamage(), $flags];
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $fullBlock >> 4, $fullBlock & 0xf, $flags];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Server::broadcastPacket($target, $pk->setChannel(Network::CHANNEL_BLOCKS));
|
||||
}
|
||||
|
||||
@ -719,18 +792,18 @@ class Level implements ChunkManager, Metadatable{
|
||||
return;
|
||||
}
|
||||
|
||||
$chunksPerPlayer = min(200, max(1, (int) ((($this->chunksPerTick - count($this->players)) / count($this->players)) + 0.5)));
|
||||
$randRange = 3 + $chunksPerPlayer / 30;
|
||||
$chunksPerLoader = min(200, max(1, (int) ((($this->chunksPerTick - count($this->loaders)) / count($this->loaders)) + 0.5)));
|
||||
$randRange = 3 + $chunksPerLoader / 30;
|
||||
$randRange = $randRange > $this->chunkTickRadius ? $this->chunkTickRadius : $randRange;
|
||||
|
||||
foreach($this->players as $player){
|
||||
$x = $player->x >> 4;
|
||||
$z = $player->z >> 4;
|
||||
foreach($this->loaders as $loader){
|
||||
$chunkX = $loader->getX() >> 4;
|
||||
$chunkZ = $loader->getZ() >> 4;
|
||||
|
||||
$index = Level::chunkHash($x, $z);
|
||||
$existingPlayers = max(0, isset($this->chunkTickList[$index]) ? $this->chunkTickList[$index] : 0);
|
||||
$this->chunkTickList[$index] = $existingPlayers + 1;
|
||||
for($chunk = 0; $chunk < $chunksPerPlayer; ++$chunk){
|
||||
$existingLoaders = max(0, isset($this->chunkTickList[$index]) ? $this->chunkTickList[$index] : 0);
|
||||
$this->chunkTickList[$index] = $existingLoaders + 1;
|
||||
for($chunk = 0; $chunk < $chunksPerLoader; ++$chunk){
|
||||
$dx = mt_rand(-$randRange, $randRange);
|
||||
$dz = mt_rand(-$randRange, $randRange);
|
||||
$hash = Level::chunkHash($dx + $x, $dz + $z);
|
||||
@ -742,7 +815,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$blockTest = 0;
|
||||
|
||||
foreach($this->chunkTickList as $index => $players){
|
||||
foreach($this->chunkTickList as $index => $loaders){
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
|
||||
|
||||
@ -750,7 +823,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(!isset($this->chunks[$index]) or ($chunk = $this->getChunk($chunkX, $chunkZ, false)) === null){
|
||||
unset($this->chunkTickList[$index]);
|
||||
continue;
|
||||
}elseif($players <= 0){
|
||||
}elseif($loaders <= 0){
|
||||
unset($this->chunkTickList[$index]);
|
||||
}
|
||||
|
||||
@ -905,12 +978,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return Block[]
|
||||
*/
|
||||
public function getCollisionBlocks(AxisAlignedBB $bb){
|
||||
$minX = Math::floorFloat($bb->minX);
|
||||
$minY = Math::floorFloat($bb->minY);
|
||||
$minZ = Math::floorFloat($bb->minZ);
|
||||
$maxX = Math::ceilFloat($bb->maxX);
|
||||
$maxY = Math::ceilFloat($bb->maxY);
|
||||
$maxZ = Math::ceilFloat($bb->maxZ);
|
||||
$minX = (int) $bb->minX;
|
||||
$minY = (int) $bb->minY;
|
||||
$minZ = (int) $bb->minZ;
|
||||
$maxX = (int) ($bb->maxX + 1);
|
||||
$maxY = (int) ($bb->maxY + 1);
|
||||
$maxZ = (int) ($bb->maxZ + 1);
|
||||
|
||||
$collides = [];
|
||||
|
||||
@ -1238,7 +1311,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$index = Level::chunkHash($pos->x >> 4, $pos->z >> 4);
|
||||
|
||||
if($direct === true){
|
||||
$this->sendBlocks($this->getUsingChunk($pos->x >> 4, $pos->z >> 4), [$block], UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
$this->sendBlocks($this->getChunkPlayers($pos->x >> 4, $pos->z >> 4), [$block], UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
unset($this->chunkCache[$index]);
|
||||
}else{
|
||||
if(!isset($this->changedBlocks[$index])){
|
||||
@ -1248,6 +1321,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->changedBlocks[$index][Level::blockHash($block->x, $block->y, $block->z)] = clone $block;
|
||||
}
|
||||
|
||||
foreach($this->getChunkLoaders($pos->x >> 4, $pos->z >> 4) as $loader){
|
||||
$loader->onBlockChanged($block);
|
||||
}
|
||||
|
||||
if($update === true){
|
||||
$this->updateAllLight($block);
|
||||
|
||||
@ -1604,10 +1681,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
public function getNearbyEntities(AxisAlignedBB $bb, Entity $entity = null){
|
||||
$nearby = [];
|
||||
|
||||
$minX = Math::floorFloat(($bb->minX - 2) / 16);
|
||||
$maxX = Math::ceilFloat(($bb->maxX + 2) / 16);
|
||||
$minZ = Math::floorFloat(($bb->minZ - 2) / 16);
|
||||
$maxZ = Math::ceilFloat(($bb->maxZ + 2) / 16);
|
||||
$minX = (int) (($bb->minX - 2) / 16);
|
||||
$maxX = (int) (($bb->maxX + 2) / 16 + 1);
|
||||
$minZ = (int) (($bb->minZ - 2) / 16);
|
||||
$maxZ = (int) (($bb->maxZ + 2) / 16 + 1);
|
||||
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
@ -1649,6 +1726,13 @@ class Level implements ChunkManager, Metadatable{
|
||||
return $this->players;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ChunkLoader[]
|
||||
*/
|
||||
public function getLoaders(){
|
||||
return $this->loaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Tile in a position, or null if not found
|
||||
*
|
||||
@ -1718,7 +1802,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){
|
||||
$this->changedBlocks[$index] = [];
|
||||
}
|
||||
$this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = new Vector3($x, $y, $z);
|
||||
$this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = $v = new Vector3($x, $y, $z);
|
||||
foreach($this->getChunkLoaders($x >> 4, $z >> 4) as $loader){
|
||||
$loader->onBlockChanged($v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1749,7 +1836,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){
|
||||
$this->changedBlocks[$index] = [];
|
||||
}
|
||||
$this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = new Vector3($x, $y, $z);
|
||||
$this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = $v = new Vector3($x, $y, $z);
|
||||
foreach($this->getChunkLoaders($x >> 4, $z >> 4) as $loader){
|
||||
$loader->onBlockChanged($v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1915,6 +2005,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if($chunk !== null and ($oldChunk === null or $oldChunk->isPopulated() === false) and $chunk->isPopulated() and $chunk->getProvider() !== null){
|
||||
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
|
||||
|
||||
foreach($this->getChunkLoaders($x, $z) as $loader){
|
||||
$loader->onChunkPopulated($chunk);
|
||||
}
|
||||
}
|
||||
}elseif(isset($this->chunkGenerationQueue[$index]) or isset($this->chunkPopulationLock[$index])){
|
||||
unset($this->chunkGenerationQueue[$index]);
|
||||
@ -1925,24 +2019,37 @@ class Level implements ChunkManager, Metadatable{
|
||||
Timings::$generationCallbackTimer->stopTiming();
|
||||
}
|
||||
|
||||
public function setChunk($x, $z, FullChunk $chunk = null, $unload = true){
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
* @param bool $unload
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk = null, $unload = true){
|
||||
if($chunk === null){
|
||||
return;
|
||||
}
|
||||
$index = Level::chunkHash($x, $z);
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if($unload){
|
||||
foreach($this->getUsingChunk($x, $z) as $player){
|
||||
$player->unloadChunk($x, $z);
|
||||
if($this->isChunkLoaded($chunkX, $chunkZ) and ($oldChunk = $this->getChunk($chunkX, $chunkZ, false)) !== false){
|
||||
foreach($this->getChunkLoaders($chunkX, $chunkZ) as $loader){
|
||||
$loader->onChunkUnloaded($oldChunk);
|
||||
}
|
||||
}
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
|
||||
$this->provider->setChunk($chunkX, $chunkZ, $chunk);
|
||||
$this->chunks[$index] = $chunk;
|
||||
}else{
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->provider->setChunk($chunkX, $chunkZ, $chunk);
|
||||
$this->chunks[$index] = $chunk;
|
||||
}
|
||||
|
||||
unset($this->chunkCache[$index]);
|
||||
$chunk->setChanged();
|
||||
|
||||
foreach($this->getChunkLoaders($chunkX, $chunkZ) as $loader){
|
||||
$loader->onChunkChanged($chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2139,7 +2246,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return bool
|
||||
*/
|
||||
public function isChunkInUse($x, $z){
|
||||
return isset($this->usedChunks[$index = Level::chunkHash($x, $z)]) and count($this->usedChunks[$index]) > 0;
|
||||
return isset($this->chunkLoaders[$index = Level::chunkHash($x, $z)]) and count($this->chunkLoaders[$index]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2179,6 +2286,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$chunk->setChanged(false);
|
||||
|
||||
foreach($this->getChunkLoaders($x, $z) as $loader){
|
||||
$loader->onChunkLoaded($chunk);
|
||||
}
|
||||
|
||||
$this->timings->syncChunkLoadTimer->stopTiming();
|
||||
|
||||
return true;
|
||||
@ -2227,18 +2338,24 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
try{
|
||||
if($chunk !== null and $this->getAutoSave()){
|
||||
$entities = 0;
|
||||
foreach($chunk->getEntities() as $e){
|
||||
if($e instanceof Player){
|
||||
continue;
|
||||
if($chunk !== null){
|
||||
if($this->getAutoSave()){
|
||||
$entities = 0;
|
||||
foreach($chunk->getEntities() as $e){
|
||||
if($e instanceof Player){
|
||||
continue;
|
||||
}
|
||||
++$entities;
|
||||
}
|
||||
|
||||
if($chunk->hasChanged() or count($chunk->getTiles()) > 0 or $entities > 0){
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->provider->saveChunk($x, $z);
|
||||
}
|
||||
++$entities;
|
||||
}
|
||||
|
||||
if($chunk->hasChanged() or count($chunk->getTiles()) > 0 or $entities > 0){
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->provider->saveChunk($x, $z);
|
||||
foreach($this->getChunkLoaders($x, $z) as $loader){
|
||||
$loader->onChunkUnloaded($chunk);
|
||||
}
|
||||
}
|
||||
$this->provider->unloadChunk($x, $z, $safe);
|
||||
@ -2495,7 +2612,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->timings->doChunkGC->stopTiming();
|
||||
}
|
||||
|
||||
private function unloadChunks(){
|
||||
public function unloadChunks(){
|
||||
if(count($this->unloadQueue) > 0){
|
||||
$X = null;
|
||||
$Z = null;
|
||||
|
@ -296,29 +296,30 @@ class Chunk extends BaseFullChunk{
|
||||
foreach($this->getEntities() as $entity){
|
||||
if(!($entity instanceof Player) and !$entity->closed){
|
||||
$entity->saveNBT();
|
||||
$nbt->setData($entity->namedtag);
|
||||
$entities[] = $nbt->write();
|
||||
$entities[] = $entity->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($entities) > 0){
|
||||
$provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_ENTITIES, implode($entities));
|
||||
$nbt->setData($entities);
|
||||
$provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_ENTITIES, $nbt->write());
|
||||
}else{
|
||||
$provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_ENTITIES);
|
||||
}
|
||||
|
||||
|
||||
$tiles = [];
|
||||
|
||||
foreach($this->getTiles() as $tile){
|
||||
if(!$tile->closed){
|
||||
$tile->saveNBT();
|
||||
$nbt->setData($tile->namedtag);
|
||||
$tiles[] = $nbt->write();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($tiles) > 0){
|
||||
$provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_TILES, implode($tiles));
|
||||
$nbt->setData($tiles);
|
||||
$provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_TILES, $nbt->write());
|
||||
}else{
|
||||
$provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_TILES);
|
||||
}
|
||||
|
@ -314,11 +314,17 @@ class NBT{
|
||||
$this->fromArray($this->data, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Compound|array
|
||||
*/
|
||||
public function getData(){
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData(Compound $data){
|
||||
/**
|
||||
* @param Compound|array $data
|
||||
*/
|
||||
public function setData($data){
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
|
@ -116,6 +116,7 @@ abstract class DefaultPermissions{
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.defaultgamemode", "Allows the user to change the default gamemode", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.seed", "Allows the user to view the seed of the world", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.status", "Allows the user to view the server performance", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.gc", "Allows the user to fire garbage collection tasks", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.timings", "Allows the user to records timings for all plugin events", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.spawnpoint", "Allows the user to change player's spawnpoint", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn", Permission::DEFAULT_OP), $commands);
|
||||
|
@ -82,7 +82,7 @@ network:
|
||||
debug:
|
||||
#If > 1, it will show debug messages in the console
|
||||
level: 1
|
||||
#Enables /status
|
||||
#Enables /status, /gc
|
||||
commands: false
|
||||
|
||||
level-settings:
|
||||
|
Loading…
x
Reference in New Issue
Block a user