mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Merge pull request #1801 from PocketMine/McRegion
Added multiple level formats support, implemented McRegion
This commit is contained in:
commit
58bded4988
@ -57,6 +57,7 @@ use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\inventory\SimpleTransactionGroup;
|
||||
use pocketmine\inventory\StonecutterShapelessRecipe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -519,7 +520,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
return $this->spawnPosition;
|
||||
}else{
|
||||
$level = $this->server->getDefaultLevel();
|
||||
return $level->getSpawn();
|
||||
return $level->getSafeSpawn();
|
||||
}
|
||||
}
|
||||
|
||||
@ -601,7 +602,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->usedChunks[$index] = [false, 0];
|
||||
|
||||
$this->getLevel()->useChunk($X, $Z, $this);
|
||||
$this->getLevel()->requestChunk($X, $Z, $this);
|
||||
$this->getLevel()->requestChunk($X, $Z, $this, LevelProvider::ORDER_ZXY);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1740,7 +1741,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
$this->craftingType = 0;
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->spawnPosition));
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn()));
|
||||
|
||||
$this->teleport($ev->getRespawnPosition());
|
||||
//$this->entity->fire = 0;
|
||||
|
@ -74,7 +74,7 @@ namespace pocketmine {
|
||||
use raklib\RakLib;
|
||||
|
||||
const VERSION = "Alpha_1.4dev";
|
||||
const API_VERSION = "1.1.0";
|
||||
const API_VERSION = "1.2.0";
|
||||
const CODENAME = "絶好(Zekkou)ケーキ(Cake)";
|
||||
const MINECRAFT_VERSION = "v0.9.5 alpha";
|
||||
const PHP_VERSION = "5.5";
|
||||
@ -141,7 +141,7 @@ namespace pocketmine {
|
||||
}
|
||||
}
|
||||
|
||||
gc_disable();
|
||||
gc_enable();
|
||||
error_reporting(-1);
|
||||
ini_set("allow_url_fopen", 1);
|
||||
ini_set("display_errors", 1);
|
||||
|
@ -1052,7 +1052,9 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
$provider = "pocketmine\\level\\format\\anvil\\Anvil";
|
||||
if(($provider = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "mcregion"))) === null){
|
||||
$provider = "pocketmine\\level\\format\\mcregion\\McRegion";
|
||||
}
|
||||
|
||||
$path = $this->getDataPath() . "worlds/" . $name . "/";
|
||||
/** @var \pocketmine\level\format\LevelProvider $provider */
|
||||
@ -1500,6 +1502,7 @@ class Server{
|
||||
$this->generationManager = new GenerationRequestManager($this);
|
||||
|
||||
LevelProviderManager::addProvider($this, "pocketmine\\level\\format\\anvil\\Anvil");
|
||||
LevelProviderManager::addProvider($this, "pocketmine\\level\\format\\mcregion\\McRegion");
|
||||
|
||||
|
||||
Generator::addGenerator("pocketmine\\level\\generator\\Flat", "flat");
|
||||
@ -1830,7 +1833,6 @@ class Server{
|
||||
$lastLoop = 0;
|
||||
$connectionTimer = Timings::$connectionTimer;
|
||||
while($this->isRunning){
|
||||
|
||||
$connectionTimer->startTiming();
|
||||
foreach($this->interfaces as $interface){
|
||||
if($interface->process()){
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\event\entity\EntitySpawnEvent;
|
||||
use pocketmine\event\entity\EntityTeleportEvent;
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -146,7 +147,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
public $closed = false;
|
||||
|
||||
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
if($chunk->getLevel() === null){
|
||||
throw new \Exception("Invalid garbage Chunk given to Entity");
|
||||
}
|
||||
@ -932,7 +933,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
|
||||
|
||||
if($this->chunk === null or ($this->chunk->getX() !== ($this->x >> 4) and $this->chunk->getZ() !== ($this->z >> 4))){
|
||||
if($this->chunk instanceof Chunk){
|
||||
if($this->chunk instanceof FullChunk){
|
||||
$this->chunk->removeEntity($this);
|
||||
}
|
||||
$this->getLevel()->loadChunk($this->x >> 4, $this->z >> 4);
|
||||
@ -1040,7 +1041,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
if($this->closed === false){
|
||||
$this->closed = true;
|
||||
unset(Entity::$needUpdate[$this->id]);
|
||||
if($this->chunk instanceof Chunk){
|
||||
if($this->chunk instanceof FullChunk){
|
||||
$this->chunk->removeEntity($this);
|
||||
}
|
||||
$this->getLevel()->removeEntity($this);
|
||||
|
@ -37,9 +37,9 @@ use pocketmine\event\LevelTimings;
|
||||
use pocketmine\event\player\PlayerInteractEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\generic\EmptyChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector2;
|
||||
@ -136,6 +136,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var BlockMetadataStore */
|
||||
private $blockMetadata;
|
||||
|
||||
private $useSections;
|
||||
private $blockOrder;
|
||||
|
||||
protected $chunkTickRadius;
|
||||
protected $chunkTickList = [];
|
||||
protected $chunksPerTick;
|
||||
@ -199,6 +202,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->levelId = static::$levelIdCounter++;
|
||||
$this->blockMetadata = new BlockMetadataStore($this);
|
||||
$this->server = $server;
|
||||
|
||||
/** @var LevelProvider $provider */
|
||||
|
||||
if(is_subclass_of($provider, "pocketmine\\level\\format\\LevelProvider", true)){
|
||||
$this->provider = new $provider($this, $path);
|
||||
}else{
|
||||
@ -208,6 +214,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
$generator = Generator::getGenerator($this->provider->getGenerator());
|
||||
$this->server->getGenerationManager()->openLevel($this, $generator, $this->provider->getGeneratorOptions());
|
||||
|
||||
$this->blockOrder = $provider::getProviderOrder();
|
||||
$this->useSections = $provider::usesChunkSection();
|
||||
|
||||
$this->folderName = $name;
|
||||
$this->updateQueue = new ReversePriorityQueue();
|
||||
$this->updateQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
|
||||
@ -220,6 +229,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->chunkTickList = [];
|
||||
$this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", false);
|
||||
$this->timings = new LevelTimings($this);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -509,9 +519,27 @@ class Level implements ChunkManager, Metadatable{
|
||||
$chunk = $this->getChunkAt($chunkX, $chunkZ, true);
|
||||
|
||||
|
||||
foreach($chunk->getSections() as $section){
|
||||
if(!($section instanceof EmptyChunkSection)){
|
||||
$Y = $section->getY();
|
||||
if($this->useSections){
|
||||
foreach($chunk->getSections() as $section){
|
||||
if(!($section instanceof EmptyChunkSection)){
|
||||
$Y = $section->getY();
|
||||
$k = mt_rand(0, PHP_INT_MAX);
|
||||
for($i = 0; $i < 3; ++$i){
|
||||
$j = $k >> 2;
|
||||
$x = $j & 0x0f;
|
||||
$y = ($j >> 8) & 0x0f;
|
||||
$z = ($j >> 16) & 0x0f;
|
||||
$k %= 1073741827;
|
||||
$blockId = $section->getBlockId($x, $y, $z);
|
||||
if(isset($this->randomTickBlocks[$blockId])){
|
||||
$block = Block::get($blockId, $section->getBlockData($x, $y, $z), new Position($chunkX * 16 + $x, ($Y << 4) + $y, $chunkZ * 16 + $z, $this));
|
||||
$block->onUpdate(self::BLOCK_UPDATE_RANDOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for($Y = 0; $Y < 8; ++$Y){
|
||||
$k = mt_rand(0, PHP_INT_MAX);
|
||||
for($i = 0; $i < 3; ++$i){
|
||||
$j = $k >> 2;
|
||||
@ -519,9 +547,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
$y = ($j >> 8) & 0x0f;
|
||||
$z = ($j >> 16) & 0x0f;
|
||||
$k %= 1073741827;
|
||||
$blockId = $section->getBlockId($x, $y, $z);
|
||||
$blockId = $chunk->getBlockId($x, $y + ($Y << 4), $z);
|
||||
if(isset($this->randomTickBlocks[$blockId])){
|
||||
$block = Block::get($blockId, $section->getBlockData($x, $y, $z), new Position($chunkX * 16 + $x, $Y * 16 + $y, $chunkZ * 16 + $z, $this));
|
||||
$block = Block::get($blockId, $chunk->getBlockData($x, $y + ($Y << 4), $z), new Position($chunkX * 16 + $x, ($Y << 4) + $y, $chunkZ * 16 + $z, $this));
|
||||
$block->onUpdate(self::BLOCK_UPDATE_RANDOM);
|
||||
}
|
||||
}
|
||||
@ -1263,45 +1291,17 @@ class Level implements ChunkManager, Metadatable{
|
||||
return isset($this->chunks[$index = "$x:$z"]) ? $this->chunks[$index] : $this->chunks[$index] = $this->provider->getChunk($x, $z, $create);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param bool $create
|
||||
*
|
||||
* @return SimpleChunk
|
||||
*/
|
||||
public function getChunk($x, $z, $create = false){
|
||||
$chunk = $this->getChunkAt($x, $z, $create);
|
||||
if($chunk === null){
|
||||
return new SimpleChunk($x, $z, 0);
|
||||
}else{
|
||||
$flags = SimpleChunk::FLAG_GENERATED;
|
||||
if($this->isChunkPopulated($x, $z)){
|
||||
$flags |= SimpleChunk::FLAG_POPULATED;
|
||||
}
|
||||
$blockIds = [];
|
||||
$data = [];
|
||||
for($Y = 0; $Y < 8; ++$Y){
|
||||
$section = $chunk->getSection($Y);
|
||||
$blockIds[$Y] = $section->getIdArray();
|
||||
$data[$Y] = $section->getDataArray();
|
||||
}
|
||||
|
||||
return new SimpleChunk($x, $z, $flags, $blockIds, $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateChunkCallback($x, $z, SimpleChunk $chunk){
|
||||
public function generateChunkCallback($x, $z, FullChunk $chunk){
|
||||
unset($this->chunkGenerationQueue["$x:$z"]);
|
||||
$this->setChunk($x, $z, $chunk);
|
||||
}
|
||||
|
||||
public function setChunk($x, $z, SimpleChunk $chunk){
|
||||
public function setChunk($x, $z, FullChunk $chunk){
|
||||
$index = Level::chunkHash($x, $z);
|
||||
foreach($this->getUsingChunk($x, $z) as $player){
|
||||
$player->unloadChunk($x, $z);
|
||||
}
|
||||
unset($this->chunks[Level::chunkHash($x, $z)]);
|
||||
unset($this->chunks[$index]);
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->loadChunk($x, $z);
|
||||
}
|
||||
@ -1372,7 +1372,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->server->getPluginManager()->callEvent(new SpawnChangeEvent($this, $previousSpawn));
|
||||
}
|
||||
|
||||
public function requestChunk($x, $z, Player $player){
|
||||
public function requestChunk($x, $z, Player $player, $order = LevelProvider::ORDER_ZXY){
|
||||
$index = Level::chunkHash($x, $z);
|
||||
if(!isset($this->chunkSendQueue[$index])){
|
||||
$this->chunkSendQueue[$index] = [];
|
||||
@ -1399,7 +1399,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
unset($this->chunkSendQueue[$index]);
|
||||
}else{
|
||||
$task = new ChunkRequestTask($this, $x, $z);
|
||||
$task = $this->provider->requestChunkTask($x, $z);
|
||||
$this->server->getScheduler()->scheduleAsyncTask($task);
|
||||
$this->chunkSendTasks[$index] = true;
|
||||
}
|
||||
@ -1512,13 +1512,13 @@ class Level implements ChunkManager, Metadatable{
|
||||
*/
|
||||
public function loadChunk($x, $z, $generate = true){
|
||||
if($generate === true){
|
||||
return $this->getChunkAt($x, $z, true) instanceof Chunk;
|
||||
return $this->getChunkAt($x, $z, true) instanceof FullChunk;
|
||||
}
|
||||
|
||||
$this->cancelUnloadChunkRequest($x, $z);
|
||||
|
||||
$chunk = $this->provider->getChunk($x, $z, false);
|
||||
if($chunk instanceof Chunk){
|
||||
if($chunk instanceof FullChunk){
|
||||
$this->chunks[Level::chunkHash($x, $z)] = $chunk;
|
||||
return true;
|
||||
}else{
|
||||
@ -1526,7 +1526,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->provider->loadChunk($x, $z);
|
||||
$this->timings->syncChunkLoadTimer->stopTiming();
|
||||
|
||||
if(($chunk = $this->provider->getChunk($x, $z)) instanceof Chunk){
|
||||
if(($chunk = $this->provider->getChunk($x, $z)) instanceof FullChunk){
|
||||
$this->chunks[Level::chunkHash($x, $z)] = $chunk;
|
||||
return true;
|
||||
}
|
||||
@ -1635,9 +1635,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
* Sets the spawnpoint
|
||||
*
|
||||
* @param Vector3 $pos
|
||||
* @deprecated
|
||||
*/
|
||||
public function setSpawn(Vector3 $pos){
|
||||
$this->provider->setSpawn($pos);
|
||||
$this->setSpawnLocation($pos);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,217 +24,9 @@ namespace pocketmine\level\format;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
interface Chunk{
|
||||
interface Chunk extends FullChunk{
|
||||
const SECTION_COUNT = 8;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getX();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getZ();
|
||||
|
||||
/**
|
||||
* @return \pocketmine\level\format\LevelProvider
|
||||
*/
|
||||
public function getLevel();
|
||||
|
||||
|
||||
/**
|
||||
* Modifies $blockId and $meta
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int &$blockId
|
||||
* @param int &$meta
|
||||
*/
|
||||
public function getBlock($x, $y, $z, &$blockId, &$meta = null);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $blockId , if null, do not change
|
||||
* @param int $meta 0-15, if null, do not change
|
||||
*
|
||||
*/
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockId($x, $y, $z, $id);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockData($x, $y, $z, $data);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockSkyLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockSkyLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-127
|
||||
*/
|
||||
public function getHighestBlockAt($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBiomeId($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $biomeId 0-255
|
||||
*/
|
||||
public function setBiomeId($x, $z, $biomeId);
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @return int[] RGB bytes
|
||||
*/
|
||||
public function getBiomeColor($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $R 0-255
|
||||
* @param int $G 0-255
|
||||
* @param int $B 0-255
|
||||
*/
|
||||
public function setBiomeColor($x, $z, $R, $G, $B);
|
||||
|
||||
/**
|
||||
* Thread-safe read-only chunk
|
||||
*
|
||||
* @param bool $includeMaxBlockY
|
||||
* @param bool $includeBiome
|
||||
* @param bool $includeBiomeTemp
|
||||
*
|
||||
* @return ChunkSnapshot
|
||||
*/
|
||||
public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function addEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function removeEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function addTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function removeTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @return \pocketmine\entity\Entity[]
|
||||
*/
|
||||
public function getEntities();
|
||||
|
||||
/**
|
||||
* @return \pocketmine\tile\Tile[]
|
||||
*/
|
||||
public function getTiles();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoaded();
|
||||
|
||||
/**
|
||||
* Loads the chunk
|
||||
*
|
||||
* @param bool $generate If the chunk does not exist, generate it
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function load($generate = true);
|
||||
|
||||
/**
|
||||
* @param bool $save
|
||||
* @param bool $safe If false, unload the chunk even if players are nearby
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unload($save = true, $safe = true);
|
||||
|
||||
/**
|
||||
* Tests whether a section (mini-chunk) is empty
|
||||
*
|
||||
@ -264,14 +56,4 @@ interface Chunk{
|
||||
*/
|
||||
public function getSections();
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getBiomeIdArray();
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getBiomeColorArray();
|
||||
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
<?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\format;
|
||||
|
||||
interface ChunkSnapshot{
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getX();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getZ();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLevelName();
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockSkyLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-127
|
||||
*/
|
||||
public function getHighestBlockAt($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getBiome($x, $z);
|
||||
|
||||
}
|
274
src/pocketmine/level/format/FullChunk.php
Normal file
274
src/pocketmine/level/format/FullChunk.php
Normal file
@ -0,0 +1,274 @@
|
||||
<?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\format;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
interface FullChunk{
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getX();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getZ();
|
||||
|
||||
public function setX($x);
|
||||
|
||||
public function setZ($z);
|
||||
|
||||
/**
|
||||
* @return \pocketmine\level\format\LevelProvider
|
||||
*/
|
||||
public function getLevel();
|
||||
|
||||
|
||||
/**
|
||||
* Modifies $blockId and $meta
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int &$blockId
|
||||
* @param int &$meta
|
||||
*/
|
||||
public function getBlock($x, $y, $z, &$blockId, &$meta = null);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $blockId , if null, do not change
|
||||
* @param int $meta 0-15, if null, do not change
|
||||
*
|
||||
*/
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockId($x, $y, $z, $id);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockData($x, $y, $z, $data);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockSkyLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockSkyLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-127
|
||||
*/
|
||||
public function getHighestBlockAt($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBiomeId($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $biomeId 0-255
|
||||
*/
|
||||
public function setBiomeId($x, $z, $biomeId);
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @return int[] RGB bytes
|
||||
*/
|
||||
public function getBiomeColor($x, $z);
|
||||
|
||||
public function getBlockIdColumn($x, $z);
|
||||
|
||||
public function getBlockDataColumn($x, $z);
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z);
|
||||
|
||||
public function getBlockLightColumn($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $R 0-255
|
||||
* @param int $G 0-255
|
||||
* @param int $B 0-255
|
||||
*/
|
||||
public function setBiomeColor($x, $z, $R, $G, $B);
|
||||
|
||||
public function isPopulated();
|
||||
|
||||
public function setPopulated($value = 1);
|
||||
|
||||
public function isGenerated();
|
||||
|
||||
public function setGenerated($value = 1);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function addEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function removeEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function addTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function removeTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @return \pocketmine\entity\Entity[]
|
||||
*/
|
||||
public function getEntities();
|
||||
|
||||
/**
|
||||
* @return \pocketmine\tile\Tile[]
|
||||
*/
|
||||
public function getTiles();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoaded();
|
||||
|
||||
/**
|
||||
* Loads the chunk
|
||||
*
|
||||
* @param bool $generate If the chunk does not exist, generate it
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function load($generate = true);
|
||||
|
||||
/**
|
||||
* @param bool $save
|
||||
* @param bool $safe If false, unload the chunk even if players are nearby
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unload($save = true, $safe = true);
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getBiomeIdArray();
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getBiomeColorArray();
|
||||
|
||||
public function getBlockIdArray();
|
||||
|
||||
public function getBlockDataArray();
|
||||
|
||||
public function getBlockSkyLightArray();
|
||||
|
||||
public function getBlockLightArray();
|
||||
|
||||
public function toBinary();
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public static function fromBinary($data, LevelProvider $provider = null);
|
||||
|
||||
}
|
@ -26,12 +26,42 @@ use pocketmine\math\Vector3;
|
||||
|
||||
interface LevelProvider{
|
||||
|
||||
const ORDER_YZX = 0;
|
||||
const ORDER_ZXY = 1;
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct(Level $level, $path);
|
||||
|
||||
/**
|
||||
* Returns the full provider name, like "anvil" or "mcregion", will be used to find the correct format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getProviderName();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public static function getProviderOrder();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function usesChunkSection();
|
||||
|
||||
/**
|
||||
* Requests a MC: PE network chunk to be sent
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @return \pocketmine\scheduler\AsyncTask
|
||||
*/
|
||||
public function requestChunkTask($x, $z);
|
||||
|
||||
/** @return string */
|
||||
public function getPath();
|
||||
|
||||
@ -85,7 +115,7 @@ interface LevelProvider{
|
||||
*
|
||||
* @return ChunkSection
|
||||
*/
|
||||
public function createChunkSection($Y);
|
||||
public static function createChunkSection($Y);
|
||||
|
||||
public function saveChunks();
|
||||
|
||||
@ -140,13 +170,13 @@ interface LevelProvider{
|
||||
public function isChunkLoaded($X, $Z);
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param SimpleChunk $chunk
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk);
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
|
@ -36,7 +36,7 @@ abstract class LevelProviderManager{
|
||||
if(!is_subclass_of($class, "pocketmine\\level\\format\\LevelProvider")){
|
||||
throw new \Exception("Class is not a subclass of LevelProvider");
|
||||
}
|
||||
self::$providers[strtolower($class)] = $class;
|
||||
self::$providers[strtolower($class::getProviderName())] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,4 +56,9 @@ abstract class LevelProviderManager{
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getProviderByName($name){
|
||||
$name = trim(strtolower($name));
|
||||
return isset(self::$providers[$name]) ? self::$providers[$name] : null;
|
||||
}
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
<?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\format;
|
||||
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class SimpleChunk{
|
||||
|
||||
const FLAG_GENERATED = 1;
|
||||
const FLAG_POPULATED = 2;
|
||||
|
||||
protected static $HEIGHT = 8;
|
||||
|
||||
/** @var string[] */
|
||||
protected $ids;
|
||||
/** @var string[] */
|
||||
protected $meta;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
|
||||
protected $flags = 0;
|
||||
|
||||
protected $changed = false;
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param int $flags
|
||||
* @param string[] $ids
|
||||
* @param string[] $meta
|
||||
*/
|
||||
public function __construct($chunkX, $chunkZ, $flags = 0, array $ids = [], array $meta = []){
|
||||
$this->x = $chunkX;
|
||||
$this->z = $chunkZ;
|
||||
$this->flags = $flags;
|
||||
for($y = 0; $y < self::$HEIGHT; ++$y){
|
||||
$this->ids[$y] = (isset($ids[$y]) and strlen($ids[$y]) === 4096) ? $ids[$y] : str_repeat("\x00", 4096);
|
||||
$this->meta[$y] = (isset($meta[$y]) and strlen($meta[$y]) === 2048) ? $meta[$y] : str_repeat("\x00", 2048);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasChanged($set = true){
|
||||
if($this->changed === true and $set === true){
|
||||
$this->changed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
*/
|
||||
public function setX($x){
|
||||
$this->x = $x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $z
|
||||
*/
|
||||
public function setZ($z){
|
||||
$this->z = $z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGenerated(){
|
||||
return ($this->flags & self::FLAG_GENERATED) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPopulated(){
|
||||
return ($this->flags & self::FLAG_POPULATED) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setGenerated($value = true){
|
||||
$this->changed = true;
|
||||
$this->flags = ($this->flags & ~self::FLAG_GENERATED) | ($value === true ? self::FLAG_GENERATED : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setPopulated($value = true){
|
||||
$this->changed = true;
|
||||
$this->flags = ($this->flags & ~self::FLAG_POPULATED) | ($value === true ? self::FLAG_POPULATED : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord(@$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $blockId 0-255
|
||||
*/
|
||||
public function setBlockId($x, $y, $z, $blockId){
|
||||
$this->changed = true;
|
||||
@$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x} = chr($blockId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData($x, $y, $z){
|
||||
$m = ord($this->meta[$y >> 4]{(($y & 0x0f) << 7) + ($z << 3) + ($x >> 1)});
|
||||
if(($x & 1) === 0){
|
||||
return $m & 0x0F;
|
||||
}else{
|
||||
return $m >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
$this->changed = true;
|
||||
$i = (($y & 0x0f) << 7) + ($z << 3) + ($x >> 1);
|
||||
$old_m = ord($this->meta[$y >> 4]{$i});
|
||||
if(($x & 1) === 0){
|
||||
$this->meta[$y >> 4]{$i} = chr(($old_m & 0xf0) | ($data & 0x0f));
|
||||
}else{
|
||||
$this->meta[$y >> 4]{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $y 0-7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSectionIds($y){
|
||||
return $this->ids[$y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $y 0-7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSectionData($y){
|
||||
return $this->meta[$y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $y 0-7
|
||||
* @param string $ids
|
||||
* @param string $meta
|
||||
*/
|
||||
public function setSection($y, $ids = null, $meta = null){
|
||||
$this->changed = true;
|
||||
if($ids !== null){
|
||||
$this->ids[$y] = $ids;
|
||||
}
|
||||
|
||||
if($meta !== null){
|
||||
$this->meta[$y] = $meta;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toBinary(){
|
||||
$binary = Binary::writeInt($this->x) . Binary::writeInt($this->z) . chr($this->flags);
|
||||
if($this->isGenerated()){
|
||||
for($y = 0; $y < self::$HEIGHT; ++$y){
|
||||
$binary .= $this->getSectionIds($y);
|
||||
$binary .= $this->getSectionData($y);
|
||||
}
|
||||
}
|
||||
|
||||
return $binary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $binary
|
||||
*
|
||||
* @return SimpleChunk
|
||||
*/
|
||||
public static function fromBinary($binary){
|
||||
$offset = 0;
|
||||
$chunkX = Binary::readInt(substr($binary, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkZ = Binary::readInt(substr($binary, $offset, 4));
|
||||
$offset += 4;
|
||||
$flags = ord($binary{$offset++});
|
||||
$ids = [];
|
||||
$meta = [];
|
||||
if(($flags & self::FLAG_GENERATED) > 0){
|
||||
for($y = 0; $y < self::$HEIGHT; ++$y){
|
||||
$ids[$y] = substr($binary, $offset, 4096);
|
||||
$offset += 4096;
|
||||
$meta[$y] = substr($binary, $offset, 2048);
|
||||
$offset += 2048;
|
||||
}
|
||||
}
|
||||
|
||||
return new SimpleChunk($chunkX, $chunkZ, $flags, $ids, $meta);
|
||||
}
|
||||
|
||||
}
|
@ -21,20 +21,14 @@
|
||||
|
||||
namespace pocketmine\level\format\anvil;
|
||||
|
||||
use pocketmine\level\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\mcregion\McRegion;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\ByteArray;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Anvil extends BaseLevelProvider{
|
||||
class Anvil extends McRegion{
|
||||
|
||||
/** @var RegionLoader[] */
|
||||
protected $regions = [];
|
||||
@ -42,6 +36,17 @@ class Anvil extends BaseLevelProvider{
|
||||
/** @var Chunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
public static function getProviderName(){
|
||||
return "anvil";
|
||||
}
|
||||
|
||||
public static function getProviderOrder(){
|
||||
return self::ORDER_YZX;
|
||||
}
|
||||
|
||||
public static function usesChunkSection(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function isValid($path){
|
||||
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
|
||||
@ -58,122 +63,8 @@ class Anvil extends BaseLevelProvider{
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public static function generate($path, $name, $seed, $generator, array $options = []){
|
||||
@mkdir($path, 0777, true);
|
||||
@mkdir($path . "/region", 0777);
|
||||
//TODO, add extra details
|
||||
$levelData = new Compound("Data", [
|
||||
"hardcore" => new Byte("hardcore", 0),
|
||||
"initialized" => new Byte("initialized", 1),
|
||||
"GameType" => new Int("GameType", 0),
|
||||
"generatorVersion" => new Int("generatorVersion", 1), //2 in MCPE
|
||||
"SpawnX" => new Int("SpawnX", 128),
|
||||
"SpawnY" => new Int("SpawnY", 70),
|
||||
"SpawnZ" => new Int("SpawnZ", 128),
|
||||
"version" => new Int("version", 19133),
|
||||
"DayTime" => new Int("DayTime", 0),
|
||||
"LastPlayed" => new Long("LastPlayed", microtime(true) * 1000),
|
||||
"RandomSeed" => new Long("RandomSeed", $seed),
|
||||
"SizeOnDisk" => new Long("SizeOnDisk", 0),
|
||||
"Time" => new Long("Time", 0),
|
||||
"generatorName" => new String("generatorName", Generator::getGeneratorName($generator)),
|
||||
"generatorOptions" => new String("generatorOptions", isset($options["preset"]) ? $options["preset"] : ""),
|
||||
"LevelName" => new String("LevelName", $name),
|
||||
"GameRules" => new Compound("GameRules", [])
|
||||
]);
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setData(new Compound(null, [
|
||||
"Data" => $levelData
|
||||
]));
|
||||
$buffer = $nbt->writeCompressed();
|
||||
@file_put_contents($path . "level.dat", $buffer);
|
||||
}
|
||||
|
||||
public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){
|
||||
$x = $chunkX >> 5;
|
||||
$z = $chunkZ >> 5;
|
||||
}
|
||||
|
||||
public function unloadChunks(){
|
||||
$this->chunks = [];
|
||||
}
|
||||
|
||||
public function getGenerator(){
|
||||
return $this->levelData["generatorName"];
|
||||
}
|
||||
|
||||
public function getGeneratorOptions(){
|
||||
return ["preset" => $this->levelData["generatorOptions"]];
|
||||
}
|
||||
|
||||
public function getLoadedChunks(){
|
||||
return $this->chunks;
|
||||
}
|
||||
|
||||
public function isChunkLoaded($x, $z){
|
||||
return isset($this->chunks[Level::chunkHash($x, $z)]);
|
||||
}
|
||||
|
||||
public function saveChunks(){
|
||||
foreach($this->chunks as $chunk){
|
||||
$this->saveChunk($chunk->getX(), $chunk->getZ());
|
||||
}
|
||||
}
|
||||
|
||||
public function loadChunk($chunkX, $chunkZ, $create = false){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunks[$index])){
|
||||
return true;
|
||||
}
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
|
||||
$this->level->timings->syncChunkLoadDataTimer->startTiming();
|
||||
$chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded
|
||||
$this->level->timings->syncChunkLoadDataTimer->stopTiming();
|
||||
|
||||
if($chunk instanceof Chunk){
|
||||
$this->chunks[$index] = $chunk;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function unloadChunk($x, $z, $safe = true){
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if($chunk instanceof Chunk){
|
||||
if($safe === true and $this->isChunkLoaded($x, $z)){
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
$entity->close();
|
||||
}
|
||||
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
|
||||
$this->chunks[$index = Level::chunkHash($x, $z)] = null;
|
||||
|
||||
unset($this->chunks[$index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function saveChunk($x, $z){
|
||||
if($this->isChunkLoaded($x, $z)){
|
||||
$this->getRegion($x >> 5, $z >> 5)->writeChunk($this->getChunk($x, $z));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
public function requestChunkTask($x, $z){
|
||||
return new ChunkRequestTask($this, $this->getLevel()->getID(), $x, $z);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,17 +87,15 @@ class Anvil extends BaseLevelProvider{
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ, $create = false){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunks[$index])){
|
||||
return $this->chunks[$index];
|
||||
}else{
|
||||
$this->loadChunk($chunkX, $chunkZ, $create);
|
||||
return isset($this->chunks[$index]) ? $this->chunks[$index] : null;
|
||||
}
|
||||
return parent::getChunk($chunkX, $chunkZ, $create);
|
||||
}
|
||||
|
||||
public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){
|
||||
if($chunk->isGenerated() === false){
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new \Exception("Invalid Chunk class");
|
||||
}
|
||||
|
||||
if($chunk->isPopulated() === false){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false);
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
@ -215,52 +104,31 @@ class Anvil extends BaseLevelProvider{
|
||||
$region->removeChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
|
||||
$this->loadChunk($chunkX, $chunkZ);
|
||||
}else{
|
||||
$newChunk = $this->getChunk($chunkX, $chunkZ, true);
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
$section = new ChunkSection(new Compound(null, [
|
||||
"Y" => new Byte("Y", $y),
|
||||
"Blocks" => new ByteArray("Blocks", $chunk->getSectionIds($y)),
|
||||
"Data" => new ByteArray("Data", $chunk->getSectionData($y)),
|
||||
"SkyLight" => new ByteArray("SkyLight", str_repeat("\xff", 2048)), //TODO
|
||||
"BlockLight" => new ByteArray("BlockLight", str_repeat("\x00", 2048)) //TODO
|
||||
]));
|
||||
$newChunk->setSection($y, $section);
|
||||
}
|
||||
if($chunk->isPopulated()){
|
||||
$newChunk->setPopulated(1);
|
||||
}
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $newChunk;
|
||||
$this->saveChunk($chunkX, $chunkZ);
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
//$this->saveChunk($chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
public function createChunkSection($Y){
|
||||
public static function createChunkSection($Y){
|
||||
return new ChunkSection(new Compound(null, [
|
||||
"Y" => new Byte("Y", $Y),
|
||||
"Blocks" => new ByteArray("Blocks", str_repeat("\xff", 4096)),
|
||||
"Data" => new ByteArray("Data", str_repeat("\xff", 2048)),
|
||||
"SkyLight" => new ByteArray("SkyLight", str_repeat("\xff", 2048)), //TODO
|
||||
"BlockLight" => new ByteArray("BlockLight", str_repeat("\x00", 2048)) //TODO
|
||||
"Data" => new ByteArray("Data", $half = str_repeat("\xff", 2048)),
|
||||
"SkyLight" => new ByteArray("SkyLight", $half),
|
||||
"BlockLight" => new ByteArray("BlockLight", $half)
|
||||
]));
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) instanceof RegionLoader){
|
||||
return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
|
||||
return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isChunkPopulated($chunkX, $chunkZ){
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
if($chunk instanceof Chunk){
|
||||
return $chunk->isPopulated();
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadRegion($x, $z){
|
||||
$index = $x . ":" . $z;
|
||||
if(isset($this->regions[$index])){
|
||||
@ -271,12 +139,4 @@ class Anvil extends BaseLevelProvider{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->unloadChunks();
|
||||
foreach($this->regions as $index => $region){
|
||||
$region->close();
|
||||
unset($this->regions[$index]);
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,9 @@ use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\ByteArray;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class Chunk extends BaseChunk{
|
||||
@ -37,7 +39,7 @@ class Chunk extends BaseChunk{
|
||||
/** @var Compound */
|
||||
protected $nbt;
|
||||
|
||||
public function __construct(LevelProvider $level, Compound $nbt){
|
||||
public function __construct($level, Compound $nbt){
|
||||
$this->nbt = $nbt;
|
||||
|
||||
if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof Enum){
|
||||
@ -73,7 +75,7 @@ class Chunk extends BaseChunk{
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
}
|
||||
|
||||
$sections = [];
|
||||
@ -92,6 +94,8 @@ class Chunk extends BaseChunk{
|
||||
}
|
||||
|
||||
parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $sections, $this->nbt->Biomes->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue());
|
||||
|
||||
unset($this->nbt->Sections);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,35 +112,18 @@ class Chunk extends BaseChunk{
|
||||
$this->nbt->TerrainPopulated = new Byte("TerrainPopulated", $value);
|
||||
}
|
||||
|
||||
public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false){
|
||||
$blockId = "";
|
||||
$blockData = "";
|
||||
$blockSkyLight = "";
|
||||
$blockLight = "";
|
||||
$emptySections = [false, false, false, false, false, false, false, false];
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGenerated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0 or (isset($this->nbt->TerrainGenerated) and $this->nbt["TerrainGenerated"] > 0);
|
||||
}
|
||||
|
||||
$emptyBlocks = str_repeat("\x00", 4096);
|
||||
$emptyHalf = str_repeat("\x00", 2048);
|
||||
|
||||
foreach($this->sections as $i => $section){
|
||||
if($section instanceof EmptyChunkSection){
|
||||
$blockId .= $emptyBlocks;
|
||||
$blockData .= $emptyHalf;
|
||||
$blockSkyLight .= $emptyHalf;
|
||||
$blockLight .= $emptyHalf;
|
||||
$emptySections[$i] = true;
|
||||
}else{
|
||||
$blockId .= $section->getIdArray();
|
||||
$blockData .= $section->getDataArray();
|
||||
$blockSkyLight .= $section->getSkyLightArray();
|
||||
$blockLight .= $section->getLightArray();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: maxBlockY, biomeMap, biomeTemp
|
||||
|
||||
//TODO: time
|
||||
return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), 0 /*$this->getLevel()->getTime()*/, $blockId, $blockData, $blockSkyLight, $blockLight, $emptySections, null, null, null, null);
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setGenerated($value = 1){
|
||||
$this->nbt->TerrainGenerated = new Byte("TerrainGenerated", $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,4 +132,72 @@ class Chunk extends BaseChunk{
|
||||
public function getNBT(){
|
||||
return $this->nbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function fromBinary($data, LevelProvider $provider = null){
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE);
|
||||
$chunk = $nbt->getData();
|
||||
|
||||
if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Chunk($provider instanceof LevelProvider ? $provider : "pocketmine\\level\\format\\anvil\\Anvil", $chunk->Level);
|
||||
}
|
||||
|
||||
public function toBinary(){
|
||||
$nbt = $this->getNBT();
|
||||
|
||||
$nbt->xPos = new Int("xPos", $this->x);
|
||||
$nbt->zPos = new Int("zPos", $this->z);
|
||||
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
foreach($this->getSections() as $section){
|
||||
$nbt->Sections[$section->getY()] = new Compound(null, [
|
||||
"Y" => new Byte("Y", $section->getY()),
|
||||
"Blocks" => new ByteArray("Blocks", $section->getIdArray()),
|
||||
"Data" => new ByteArray("Data", $section->getDataArray()),
|
||||
"BlockLight" => new ByteArray("BlockLight", $section->getLightArray()),
|
||||
"SkyLight" => new ByteArray("SkyLight", $section->getSkyLightArray())
|
||||
]);
|
||||
}
|
||||
|
||||
$nbt->Biomes = new ByteArray("Biomes", $this->getBiomeIdArray());
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", $this->getBiomeColorArray());
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($this->getEntities() as $entity){
|
||||
if(!($entity instanceof Player) and $entity->closed !== true){
|
||||
$entity->saveNBT();
|
||||
$entities[] = $entity->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("Entities", $entities);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
|
||||
$tiles = [];
|
||||
foreach($this->getTiles() as $tile){
|
||||
if($tile->closed !== true){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("TileEntities", $tiles);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new Compound("", array("Level" => $nbt)));
|
||||
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
}
|
||||
}
|
@ -19,8 +19,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level;
|
||||
namespace pocketmine\level\format\anvil;
|
||||
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\Server;
|
||||
@ -43,11 +44,14 @@ class ChunkRequestTask extends AsyncTask{
|
||||
|
||||
protected $tiles;
|
||||
|
||||
public function __construct(Level $level, $chunkX, $chunkZ){
|
||||
$this->levelId = $level->getID();
|
||||
public function __construct(Anvil $level, $levelId, $chunkX, $chunkZ){
|
||||
$this->levelId = $levelId;
|
||||
$this->chunkX = $chunkX;
|
||||
$this->chunkZ = $chunkZ;
|
||||
$chunk = $level->getChunkAt($chunkX, $chunkZ, true);
|
||||
$chunk = $level->getChunk($chunkX, $chunkZ, false);
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new \Exception("Invalid Chunk sent");
|
||||
}
|
||||
$this->biomeIds = $chunk->getBiomeIdArray();
|
||||
$this->biomeColors = $chunk->getBiomeColorArray();
|
||||
|
@ -1,78 +0,0 @@
|
||||
<?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\format\anvil;
|
||||
|
||||
use pocketmine\level\format\generic\BaseChunkSnapshot;
|
||||
|
||||
class ChunkSnapshot extends BaseChunkSnapshot{
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blockId{
|
||||
(($y >> 4) << 12) //get section index
|
||||
+ ($y << 8) + ($z << 4) + $x //get block index in section
|
||||
});
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
$data = ord($this->blockData{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $data & 0x0F;
|
||||
}else{
|
||||
return $data >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
$level = ord($this->skyLight{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $level & 0x0F;
|
||||
}else{
|
||||
return $level >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
$level = ord($this->light{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $level & 0x0F;
|
||||
}else{
|
||||
return $level >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBiome(){
|
||||
return 0; //TODO
|
||||
}
|
||||
|
||||
public function getHighestBlockAt($x, $z){
|
||||
return 127; //TODO
|
||||
}
|
||||
}
|
@ -30,24 +30,14 @@ use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class RegionLoader{
|
||||
class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{
|
||||
const VERSION = 1;
|
||||
const COMPRESSION_GZIP = 1;
|
||||
const COMPRESSION_ZLIB = 2;
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
protected $filePath;
|
||||
protected $filePointer;
|
||||
protected $lastSector;
|
||||
/** @var LevelProvider */
|
||||
protected $levelProvider;
|
||||
protected $locationTable = [];
|
||||
|
||||
public function __construct(LevelProvider $level, $regionX, $regionZ){
|
||||
$this->x = $regionX;
|
||||
$this->z = $regionZ;
|
||||
@ -65,19 +55,6 @@ class RegionLoader{
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct(){
|
||||
if(is_resource($this->filePointer)){
|
||||
$this->cleanGarbage();
|
||||
$this->writeLocationTable();
|
||||
flock($this->filePointer, LOCK_UN);
|
||||
fclose($this->filePointer);
|
||||
}
|
||||
}
|
||||
|
||||
protected function isChunkGenerated($index){
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
}
|
||||
|
||||
public function readChunk($x, $z, $generate = true){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($index < 0 or $index >= 4096){
|
||||
@ -119,20 +96,9 @@ class RegionLoader{
|
||||
return false;
|
||||
}
|
||||
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->readCompressed(fread($this->filePointer, $length - 1), $compression);
|
||||
$chunk = $nbt->getData();
|
||||
$chunk = Chunk::fromBinary(fread($this->filePointer, $length - 1), $this->levelProvider);
|
||||
|
||||
|
||||
if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Chunk($this->levelProvider, $chunk->Level);
|
||||
}
|
||||
|
||||
public function chunkExists($x, $z){
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
return $chunk instanceof Chunk ? $chunk : false;
|
||||
}
|
||||
|
||||
public function generateChunk($x, $z){
|
||||
@ -145,9 +111,8 @@ class RegionLoader{
|
||||
$nbt->V = new Byte("V", self::VERSION);
|
||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
//TODO: check type and name
|
||||
//$nbt->GrassMap = new IntArray("GrassMap", array_fill(0, 256, 127));
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
$nbt->Entities = new Enum("Entities", []);
|
||||
@ -156,216 +121,11 @@ class RegionLoader{
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
$this->saveChunk($x, $z, $nbt);
|
||||
}
|
||||
|
||||
protected function saveChunk($x, $z, Compound $nbt){
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new Compound("", array("Level" => $nbt)));
|
||||
$chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, self::$COMPRESSION_LEVEL);
|
||||
$length = strlen($chunkData) + 1;
|
||||
$sectors = (int) ceil(($length + 4) / 4096);
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($this->locationTable[$index][1] < $sectors){
|
||||
$this->locationTable[$index][0] = $this->lastSector += $sectors; //The GC will clean this shift later
|
||||
}
|
||||
$this->locationTable[$index][1] = $sectors;
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
public function removeChunk($x, $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 0;
|
||||
}
|
||||
|
||||
public function writeChunk(Chunk $chunk){
|
||||
$nbt = $chunk->getNBT();
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
foreach($chunk->getSections() as $section){
|
||||
$nbt->Sections[$section->getY()] = new Compound(null, [
|
||||
"Y" => new Byte("Y", $section->getY()),
|
||||
"Blocks" => new ByteArray("Blocks", $section->getIdArray()),
|
||||
"Data" => new ByteArray("Data", $section->getDataArray()),
|
||||
"BlockLight" => new ByteArray("BlockLight", $section->getLightArray()),
|
||||
"SkyLight" => new ByteArray("SkyLight", $section->getSkyLightArray())
|
||||
]);
|
||||
}
|
||||
|
||||
$nbt->Biomes = new ByteArray("Biomes", $chunk->getBiomeIdArray());
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", $chunk->getBiomeColorArray());
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if(!($entity instanceof Player) and $entity->closed !== true){
|
||||
$entity->saveNBT();
|
||||
$entities[] = $entity->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("Entities", $entities);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
|
||||
$tiles = [];
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile->closed !== true){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("TileEntities", $tiles);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $nbt);
|
||||
}
|
||||
|
||||
protected static function getChunkOffset($x, $z){
|
||||
return $x + ($z << 5);
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->writeLocationTable();
|
||||
flock($this->filePointer, LOCK_UN);
|
||||
fclose($this->filePointer);
|
||||
}
|
||||
|
||||
public function doSlowCleanUp(){
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
if($this->locationTable[$i][0] === 0 or $this->locationTable[$i][1] === 0){
|
||||
continue;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
$chunk = fread($this->filePointer, $this->locationTable[$i][1] << 12);
|
||||
$length = Binary::readInt(substr($chunk, 0, 4));
|
||||
if($length <= 1){
|
||||
$this->locationTable[$i] = array(0, 0, 0); //Non-generated chunk, remove it from index
|
||||
}
|
||||
$chunk = zlib_decode(substr($chunk, 5));
|
||||
if(strlen($chunk) <= 1){
|
||||
$this->locationTable[$i] = array(0, 0, 0); //Corrupted chunk, remove it
|
||||
continue;
|
||||
}
|
||||
$chunk = chr(self::COMPRESSION_ZLIB) . zlib_encode($chunk, 15, 9);
|
||||
$chunk = Binary::writeInt(strlen($chunk)) . $chunk;
|
||||
$sectors = (int) ceil(strlen($chunk) / 4096);
|
||||
if($sectors > $this->locationTable[$i][1]){
|
||||
$this->locationTable[$i][0] = $this->lastSector += $sectors;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
fwrite($this->filePointer, str_pad($chunk, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
}
|
||||
$this->writeLocationTable();
|
||||
$n = $this->cleanGarbage();
|
||||
$this->writeLocationTable();
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
private function cleanGarbage(){
|
||||
$sectors = [];
|
||||
foreach($this->locationTable as $index => $data){ //Calculate file usage
|
||||
if($data[0] === 0 or $data[1] === 0){
|
||||
$this->locationTable[$index] = array(0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
for($i = 0; $i < $data[1]; ++$i){
|
||||
$sectors[$data[0]] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($sectors) === ($this->lastSector - 2)){ //No collection needed
|
||||
return 0;
|
||||
}
|
||||
|
||||
ksort($sectors);
|
||||
$shift = 0;
|
||||
$lastSector = 1; //First chunk - 1
|
||||
|
||||
fseek($this->filePointer, 8192);
|
||||
$sector = 2;
|
||||
foreach($sectors as $sector => $index){
|
||||
if(($sector - $lastSector) > 1){
|
||||
$shift += $sector - $lastSector - 1;
|
||||
}
|
||||
if($shift > 0){
|
||||
fseek($this->filePointer, $sector << 12);
|
||||
$old = fread($this->filePointer, 4096);
|
||||
fseek($this->filePointer, ($sector - $shift) << 12);
|
||||
fwrite($this->filePointer, $old, 4096);
|
||||
}
|
||||
$this->locationTable[$index][0] -= $shift;
|
||||
$lastSector = $sector;
|
||||
}
|
||||
ftruncate($this->filePointer, ($sector + 1) << 12); //Truncate to the end of file written
|
||||
return $shift;
|
||||
}
|
||||
|
||||
private function loadLocationTable(){
|
||||
fseek($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
$table = fread($this->filePointer, 4 * 1024 * 2);
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$index = Binary::readInt(substr($table, $i << 2, 4));
|
||||
$this->locationTable[$i] = array(($index & ~0xff) >> 8, $index & 0xff, Binary::readInt(substr($table, 4096 + ($i << 2), 4)));
|
||||
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
||||
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function writeLocationTable(){
|
||||
$table = "";
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$table .= Binary::writeInt(($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]);
|
||||
}
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$table .= Binary::writeInt($this->locationTable[$i][2]);
|
||||
}
|
||||
fseek($this->filePointer, 0);
|
||||
fwrite($this->filePointer, $table, 4096 * 2);
|
||||
}
|
||||
|
||||
private function writeLocationIndex($index){
|
||||
fseek($this->filePointer, $index << 2);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
|
||||
fseek($this->filePointer, 4096 + ($index << 2));
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
|
||||
}
|
||||
|
||||
private function createBlank(){
|
||||
fseek($this->filePointer, 0);
|
||||
ftruncate($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
$table = "";
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i] = array(0, 0);
|
||||
$table .= Binary::writeInt(0);
|
||||
}
|
||||
|
||||
$time = time();
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i][2] = $time;
|
||||
$table .= Binary::writeInt($time);
|
||||
}
|
||||
|
||||
fwrite($this->filePointer, $table, 4096 * 2);
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
$chunkData= $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
$this->saveChunk($x, $z, $chunkData);
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ namespace pocketmine\level\format\generic;
|
||||
use pocketmine\entity\DroppedItem;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\ChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
@ -34,7 +35,7 @@ use pocketmine\tile\Sign;
|
||||
use pocketmine\tile\Tile;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
abstract class BaseChunk implements Chunk{
|
||||
abstract class BaseChunk extends BaseFullChunk implements Chunk{
|
||||
|
||||
/** @var ChunkSection[] */
|
||||
protected $sections = [];
|
||||
@ -69,8 +70,8 @@ abstract class BaseChunk implements Chunk{
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function __construct(LevelProvider $level, $x, $z, array $sections, $biomeIds = null, array $biomeColors = [], array $entities = [], array $tiles = []){
|
||||
$this->level = new \WeakRef($level);
|
||||
protected function __construct($level, $x, $z, array $sections, $biomeIds = null, array $biomeColors = [], array $entities = [], array $tiles = []){
|
||||
$this->level = $level instanceof LevelProvider ? new \WeakRef($level) : $level;
|
||||
$this->x = (int) $x;
|
||||
$this->z = (int) $z;
|
||||
foreach($sections as $Y => $section){
|
||||
@ -98,61 +99,48 @@ abstract class BaseChunk implements Chunk{
|
||||
$this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a"));
|
||||
}
|
||||
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($entities as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
if($this->getLevel() instanceof LevelProvider){
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($entities as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($nbt->id instanceof String){ //New format
|
||||
if($nbt->id instanceof String){ //New format
|
||||
switch($nbt["id"]){
|
||||
case "Item":
|
||||
(new DroppedItem($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
}
|
||||
}else{ //Old format
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($tiles as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
switch($nbt["id"]){
|
||||
case "Item":
|
||||
(new DroppedItem($this, $nbt))->spawnToAll();
|
||||
case Tile::CHEST:
|
||||
new Chest($this, $nbt);
|
||||
break;
|
||||
case Tile::FURNACE:
|
||||
new Furnace($this, $nbt);
|
||||
break;
|
||||
case Tile::SIGN:
|
||||
new Sign($this, $nbt);
|
||||
break;
|
||||
}
|
||||
}else{ //Old format
|
||||
|
||||
}
|
||||
}
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
}
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($tiles as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
switch($nbt["id"]){
|
||||
case Tile::CHEST:
|
||||
new Chest($this, $nbt);
|
||||
break;
|
||||
case Tile::FURNACE:
|
||||
new Furnace($this, $nbt);
|
||||
break;
|
||||
case Tile::SIGN:
|
||||
new Sign($this, $nbt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LevelProvider
|
||||
*/
|
||||
public function getLevel(){
|
||||
return $this->level->valid() ? $this->level->get() : null;
|
||||
}
|
||||
|
||||
public function getBlock($x, $y, $z, &$blockId, &$meta = null){
|
||||
@ -163,7 +151,8 @@ abstract class BaseChunk implements Chunk{
|
||||
try{
|
||||
return $this->sections[$y >> 4]->setBlock($x, $y & 0x0f, $z, $blockId & 0xff, $meta & 0x0f);
|
||||
}catch(\Exception $e){
|
||||
$this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y));
|
||||
$level = $this->getLevel();
|
||||
$this->setSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
return $this->setBlock($x, $y, $z, $blockId, $meta);
|
||||
}
|
||||
}
|
||||
@ -176,7 +165,8 @@ abstract class BaseChunk implements Chunk{
|
||||
try{
|
||||
$this->sections[$y >> 4]->setBlockId($x, $y & 0x0f, $z, $id);
|
||||
}catch(\Exception $e){
|
||||
$this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y));
|
||||
$level = $this->getLevel();
|
||||
$this->setSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockId($x, $y, $z, $id);
|
||||
}
|
||||
}
|
||||
@ -189,7 +179,8 @@ abstract class BaseChunk implements Chunk{
|
||||
try{
|
||||
$this->sections[$y >> 4]->setBlockData($x, $y & 0x0f, $z, $data);
|
||||
}catch(\Exception $e){
|
||||
$this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y));
|
||||
$level = $this->getLevel();
|
||||
$this->setSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockData($x, $y, $z, $data);
|
||||
}
|
||||
}
|
||||
@ -202,7 +193,8 @@ abstract class BaseChunk implements Chunk{
|
||||
try{
|
||||
$this->sections[$y >> 4]->getBlockSkyLight($x, $y & 0x0f, $z, $data);
|
||||
}catch(\Exception $e){
|
||||
$this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y));
|
||||
$level = $this->getLevel();
|
||||
$this->setSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockSkyLight($x, $y, $z, $data);
|
||||
}
|
||||
}
|
||||
@ -215,41 +207,42 @@ abstract class BaseChunk implements Chunk{
|
||||
try{
|
||||
$this->sections[$y >> 4]->getBlockSkyLight($x, $y & 0x0f, $z, $data);
|
||||
}catch(\Exception $e){
|
||||
$this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y));
|
||||
$level = $this->getLevel();
|
||||
$this->setSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockLight($x, $y, $z, $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBiomeId($x, $z){
|
||||
return ord($this->biomeIds{($z << 4) + $x});
|
||||
}
|
||||
|
||||
public function setBiomeId($x, $z, $biomeId){
|
||||
$this->biomeIds{($z << 4) + $x} = chr($biomeId);
|
||||
}
|
||||
|
||||
public function getBiomeColor($x, $z){
|
||||
$color = $this->biomeColors[($z << 4) + $x] & 0xFFFFFF;
|
||||
return [$color >> 16, ($color >> 8) & 0xFF, $color & 0xFF];
|
||||
}
|
||||
|
||||
public function setBiomeColor($x, $z, $R, $G, $B){
|
||||
$this->biomeColors[($z << 4) + $x] = 0 | (($R & 0xFF) << 16) | (($G & 0xFF) << 8) | ($B & 0xFF);
|
||||
}
|
||||
|
||||
public function getHighestBlockAt($x, $z){
|
||||
for($Y = self::SECTION_COUNT - 1; $Y >= 0; --$Y){
|
||||
if(!$this->isSectionEmpty($Y)){
|
||||
$column = $this->sections[$Y]->getBlockIdColumn($x, $z);
|
||||
for($y = 15; $y >= 0; --$y){
|
||||
if($column{$y} !== "\x00"){
|
||||
return $y + ($Y << 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function getBlockIdColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockIdColumn($x, $z);
|
||||
}
|
||||
return $column;
|
||||
}
|
||||
|
||||
return 0;
|
||||
public function getBlockDataColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockDataColumn($x, $z);
|
||||
}
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockSkyLightColumn($x, $z);
|
||||
}
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockLightColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockLightColumn($x, $z);
|
||||
}
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function isSectionEmpty($fY){
|
||||
@ -268,54 +261,40 @@ abstract class BaseChunk implements Chunk{
|
||||
}
|
||||
}
|
||||
|
||||
public function addEntity(Entity $entity){
|
||||
$this->entities[$entity->getID()] = $entity;
|
||||
}
|
||||
|
||||
public function removeEntity(Entity $entity){
|
||||
unset($this->entities[$entity->getID()]);
|
||||
}
|
||||
|
||||
public function addTile(Tile $tile){
|
||||
$this->tiles[$tile->getID()] = $tile;
|
||||
}
|
||||
|
||||
public function removeTile(Tile $tile){
|
||||
unset($this->tiles[$tile->getID()]);
|
||||
}
|
||||
|
||||
public function getEntities(){
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
public function getTiles(){
|
||||
return $this->tiles;
|
||||
}
|
||||
|
||||
public function isLoaded(){
|
||||
return $this->getLevel() === null ? false : $this->getLevel()->isChunkLoaded($this->getX(), $this->getZ());
|
||||
}
|
||||
|
||||
public function load($generate = true){
|
||||
return $this->getLevel() === null ? false : $this->getLevel()->getChunk($this->getX(), $this->getZ(), true) instanceof Chunk;
|
||||
}
|
||||
|
||||
public function unload($save = true, $safe = true){
|
||||
$level = $this->getLevel();
|
||||
if($level === null){
|
||||
return true;
|
||||
public function getBlockIdArray(){
|
||||
$blocks = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$blocks .= $this->sections[$y]->getIdArray();
|
||||
}
|
||||
if($save === true){
|
||||
$level->saveChunk($this->getX(), $this->getZ());
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
public function getBlockDataArray(){
|
||||
$data = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$data .= $this->sections[$y]->getDataArray();
|
||||
}
|
||||
if($this->getLevel()->unloadChunk($this->getX(), $this->getZ(), $safe)){
|
||||
foreach($this->getEntities() as $entity){
|
||||
$entity->close();
|
||||
}
|
||||
foreach($this->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightArray(){
|
||||
$skyLight = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$skyLight .= $this->sections[$y]->getSkyLightArray();
|
||||
}
|
||||
return $skyLight;
|
||||
}
|
||||
|
||||
public function getBlockLightArray(){
|
||||
$blockLight = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$blockLight .= $this->sections[$y]->getLightArray();
|
||||
}
|
||||
return $blockLight;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -325,12 +304,4 @@ abstract class BaseChunk implements Chunk{
|
||||
return $this->sections;
|
||||
}
|
||||
|
||||
public function getBiomeIdArray(){
|
||||
return $this->biomeIds;
|
||||
}
|
||||
|
||||
public function getBiomeColorArray(){
|
||||
return $this->biomeColors;
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
<?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\format\generic;
|
||||
|
||||
use pocketmine\level\format\ChunkSnapshot;
|
||||
|
||||
abstract class BaseChunkSnapshot implements ChunkSnapshot{
|
||||
|
||||
protected $blockId;
|
||||
protected $blockData;
|
||||
protected $skyLight;
|
||||
protected $light;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
protected $levelName;
|
||||
protected $levelTime;
|
||||
|
||||
public function __construct($x, $z, $levelName, $levelTime, $blockId, $blockData, $skyLight, $light, $heightMap, $biome, $biomeTemp, $biomeRain){
|
||||
$this->x = $x;
|
||||
$this->z = $z;
|
||||
$this->levelName = $levelName;
|
||||
$this->levelTime = $levelTime;
|
||||
$this->blockId = $blockId;
|
||||
$this->blockData = $blockData;
|
||||
$this->skyLight = $skyLight;
|
||||
$this->light = $light;
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
public function getLevelName(){
|
||||
return $this->levelName;
|
||||
}
|
||||
|
||||
public function getLevelTime(){
|
||||
return $this->levelTime;
|
||||
}
|
||||
}
|
270
src/pocketmine/level/format/generic/BaseFullChunk.php
Normal file
270
src/pocketmine/level/format/generic/BaseFullChunk.php
Normal file
@ -0,0 +1,270 @@
|
||||
<?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\format\generic;
|
||||
|
||||
use pocketmine\entity\DroppedItem;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\tile\Chest;
|
||||
use pocketmine\tile\Furnace;
|
||||
use pocketmine\tile\Sign;
|
||||
use pocketmine\tile\Tile;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
abstract class BaseFullChunk implements FullChunk{
|
||||
|
||||
/** @var Entity[] */
|
||||
protected $entities = [];
|
||||
|
||||
/** @var Tile[] */
|
||||
protected $tiles = [];
|
||||
|
||||
/** @var string */
|
||||
protected $biomeIds;
|
||||
|
||||
/** @var int[256] */
|
||||
protected $biomeColors;
|
||||
|
||||
protected $blocks;
|
||||
|
||||
protected $data;
|
||||
|
||||
protected $skyLight;
|
||||
|
||||
protected $blockLight;
|
||||
|
||||
/** @var \WeakRef<LevelProvider> */
|
||||
protected $level;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
|
||||
/**
|
||||
* @param LevelProvider $level
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param string $blocks
|
||||
* @param string $data
|
||||
* @param string $skyLight
|
||||
* @param string $blockLight
|
||||
* @param string $biomeIds
|
||||
* @param int[] $biomeColors
|
||||
* @param Compound[] $entities
|
||||
* @param Compound[] $tiles
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function __construct($level, $x, $z, $blocks, $data, $skyLight, $blockLight, $biomeIds = null, array $biomeColors = [], array $entities = [], array $tiles = []){
|
||||
$this->level = $level instanceof LevelProvider ? new \WeakRef($level) : $level;
|
||||
$this->x = (int) $x;
|
||||
$this->z = (int) $z;
|
||||
|
||||
$this->blocks = $blocks;
|
||||
$this->data = $data;
|
||||
$this->skyLight = $skyLight;
|
||||
$this->blockLight = $blockLight;
|
||||
|
||||
if(strlen($biomeIds) === 256){
|
||||
$this->biomeIds = $biomeIds;
|
||||
}else{
|
||||
$this->biomeIds = str_repeat("\x01", 256);
|
||||
}
|
||||
|
||||
if(count($biomeColors) === 256){
|
||||
$this->biomeColors = $biomeColors;
|
||||
}else{
|
||||
$this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a"));
|
||||
}
|
||||
|
||||
if($this->getLevel() instanceof LevelProvider){
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($entities as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($nbt->id instanceof String){ //New format
|
||||
switch($nbt["id"]){
|
||||
case "Item":
|
||||
(new DroppedItem($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
}
|
||||
}else{ //Old format
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($tiles as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
switch($nbt["id"]){
|
||||
case Tile::CHEST:
|
||||
new Chest($this, $nbt);
|
||||
break;
|
||||
case Tile::FURNACE:
|
||||
new Furnace($this, $nbt);
|
||||
break;
|
||||
case Tile::SIGN:
|
||||
new Sign($this, $nbt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
public function setX($x){
|
||||
$this->x = $x;
|
||||
}
|
||||
|
||||
public function setZ($z){
|
||||
$this->z = $z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LevelProvider
|
||||
*/
|
||||
public function getLevel(){
|
||||
return $this->level instanceof \WeakRef ? ($this->level->valid() ? $this->level->get() : null) : $this->level;
|
||||
}
|
||||
|
||||
public function getBiomeId($x, $z){
|
||||
return ord($this->biomeIds{($z << 4) + $x});
|
||||
}
|
||||
|
||||
public function setBiomeId($x, $z, $biomeId){
|
||||
$this->biomeIds{($z << 4) + $x} = chr($biomeId);
|
||||
}
|
||||
|
||||
public function getBiomeColor($x, $z){
|
||||
$color = $this->biomeColors[($z << 4) + $x] & 0xFFFFFF;
|
||||
return [$color >> 16, ($color >> 8) & 0xFF, $color & 0xFF];
|
||||
}
|
||||
|
||||
public function setBiomeColor($x, $z, $R, $G, $B){
|
||||
$this->biomeColors[($z << 4) + $x] = 0 | (($R & 0xFF) << 16) | (($G & 0xFF) << 8) | ($B & 0xFF);
|
||||
}
|
||||
|
||||
public function getHighestBlockAt($x, $z){
|
||||
$column = $this->getBlockIdColumn($x, $z);
|
||||
for($y = 127; $y >= 0; --$y){
|
||||
if($column{$y} !== "\x00"){
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function addEntity(Entity $entity){
|
||||
$this->entities[$entity->getID()] = $entity;
|
||||
}
|
||||
|
||||
public function removeEntity(Entity $entity){
|
||||
unset($this->entities[$entity->getID()]);
|
||||
}
|
||||
|
||||
public function addTile(Tile $tile){
|
||||
$this->tiles[$tile->getID()] = $tile;
|
||||
}
|
||||
|
||||
public function removeTile(Tile $tile){
|
||||
unset($this->tiles[$tile->getID()]);
|
||||
}
|
||||
|
||||
public function getEntities(){
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
public function getTiles(){
|
||||
return $this->tiles;
|
||||
}
|
||||
|
||||
public function isLoaded(){
|
||||
return $this->getLevel() === null ? false : $this->getLevel()->isChunkLoaded($this->getX(), $this->getZ());
|
||||
}
|
||||
|
||||
public function load($generate = true){
|
||||
return $this->getLevel() === null ? false : $this->getLevel()->getChunk($this->getX(), $this->getZ(), true) instanceof FullChunk;
|
||||
}
|
||||
|
||||
public function unload($save = true, $safe = true){
|
||||
$level = $this->getLevel();
|
||||
if($level === null){
|
||||
return true;
|
||||
}
|
||||
if($save === true){
|
||||
$level->saveChunk($this->getX(), $this->getZ());
|
||||
}
|
||||
if($this->getLevel()->unloadChunk($this->getX(), $this->getZ(), $safe)){
|
||||
foreach($this->getEntities() as $entity){
|
||||
$entity->close();
|
||||
}
|
||||
foreach($this->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockIdArray(){
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
public function getBlockDataArray(){
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightArray(){
|
||||
return $this->skyLight;
|
||||
}
|
||||
|
||||
public function getBlockLightArray(){
|
||||
return $this->blockLight;
|
||||
}
|
||||
|
||||
public function getBiomeIdArray(){
|
||||
return $this->biomeIds;
|
||||
}
|
||||
|
||||
public function getBiomeColorArray(){
|
||||
return $this->biomeColors;
|
||||
}
|
||||
|
||||
}
|
299
src/pocketmine/level/format/mcregion/Chunk.php
Normal file
299
src/pocketmine/level/format/mcregion/Chunk.php
Normal file
@ -0,0 +1,299 @@
|
||||
<?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\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\generic\BaseFullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\ByteArray;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class Chunk extends BaseFullChunk{
|
||||
|
||||
/** @var Compound */
|
||||
protected $nbt;
|
||||
|
||||
public function __construct($level, Compound $nbt){
|
||||
$this->nbt = $nbt;
|
||||
|
||||
if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof Enum){
|
||||
$this->nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->Entities = new Enum("Entities", []);
|
||||
$this->nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(isset($this->nbt->TileEntities) and $this->nbt->TileEntities instanceof Enum){
|
||||
$this->nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->TileEntities = new Enum("TileEntities", []);
|
||||
$this->nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(isset($this->nbt->TileTicks) and $this->nbt->TileTicks instanceof Enum){
|
||||
$this->nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$this->nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->Biomes) or !($this->nbt->Biomes instanceof ByteArray)){
|
||||
$this->nbt->Biomes = new ByteArray("Biomes", str_repeat("\x01", 256));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
}
|
||||
|
||||
parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $this->nbt["Blocks"], $this->nbt["Data"], $this->nbt["SkyLight"], $this->nbt["BlockLight"], $this->nbt->Biomes->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue());
|
||||
unset($this->nbt->Blocks);
|
||||
unset($this->nbt->Data);
|
||||
unset($this->nbt->SkyLight);
|
||||
unset($this->nbt->BlockLight);
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blocks{($z << 11) + ($x << 7) + $y});
|
||||
}
|
||||
|
||||
public function setBlockId($x, $y, $z, $id){
|
||||
$this->blocks{($z << 11) + ($x << 7) + $y} = chr($id);
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
$m = ord($this->data{($z << 10) + ($x << 6) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $m >> 4;
|
||||
}else{
|
||||
return $m & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
$i = ($z << 10) + ($x << 6) + ($y >> 1);
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
}else{
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($data & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlock($x, $y, $z, &$blockId, &$meta = null){
|
||||
$i = ($z << 11) + ($x << 7) + $y;
|
||||
$blockId = ord($this->blocks{$i});
|
||||
$m = ord($this->data{$i >> 1});
|
||||
if(($y & 1) === 0){
|
||||
$meta = $m >> 4;
|
||||
}else{
|
||||
$meta = $m & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null){
|
||||
$i = ($z << 11) + ($x << 7) + $y;
|
||||
|
||||
$changed = false;
|
||||
|
||||
if($blockId !== null){
|
||||
$blockId = chr($blockId);
|
||||
if($this->blocks{$i} !== $blockId){
|
||||
$this->blocks{$i} = $blockId;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($meta !== null){
|
||||
$i >>= 1;
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr((($meta & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
if((($old_m & 0xf0) >> 4) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}else{
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($meta & 0x0f));
|
||||
if(($old_m & 0x0f) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
$sl = ord($this->skyLight{($z << 10) + ($x << 6) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $sl >> 4;
|
||||
}else{
|
||||
return $sl & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockSkyLight($x, $y, $z, $level){
|
||||
$i = ($z << 10) + ($x << 6) + ($y >> 1);
|
||||
$old_sl = ord($this->skyLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($old_sl & 0x0f));
|
||||
}else{
|
||||
$this->skyLight{$i} = chr(($old_sl & 0xf0) | ($level & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
$l = ord($this->blockLight{($z << 10) + ($x << 6) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $l >> 4;
|
||||
}else{
|
||||
return $l & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockLight($x, $y, $z, $level){
|
||||
$i = ($z << 10) + ($x << 6) + ($y >> 1);
|
||||
$old_l = ord($this->blockLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($old_l & 0x0f));
|
||||
}else{
|
||||
$this->blockLight{$i} = chr(($old_l & 0xf0) | ($level & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockIdColumn($x, $z){
|
||||
return substr($this->blocks, ($z << 11) + ($x << 7), 128);
|
||||
}
|
||||
|
||||
public function getBlockDataColumn($x, $z){
|
||||
return substr($this->data, ($z << 10) + ($x << 6), 64);
|
||||
}
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z){
|
||||
return substr($this->skyLight, ($z << 10) + ($x << 6), 64);
|
||||
}
|
||||
|
||||
public function getBlockLightColumn($x, $z){
|
||||
return substr($this->blockLight, ($z << 10) + ($x << 6), 64);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPopulated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setPopulated($value = 1){
|
||||
$this->nbt->TerrainPopulated = new Byte("TerrainPopulated", $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGenerated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0 or (isset($this->nbt->TerrainGenerated) and $this->nbt["TerrainGenerated"] > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setGenerated($value = 1){
|
||||
$this->nbt->TerrainGenerated = new Byte("TerrainGenerated", $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function fromBinary($data, LevelProvider $provider = null){
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE);
|
||||
$chunk = $nbt->getData();
|
||||
|
||||
if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Chunk($provider instanceof LevelProvider ? $provider : "pocketmine\\level\\format\\mcregion\\McRegion", $chunk->Level);
|
||||
}
|
||||
|
||||
public function toBinary(){
|
||||
$nbt = $this->getNBT();
|
||||
|
||||
$nbt->xPos = new Int("xPos", $this->x);
|
||||
$nbt->zPos = new Int("zPos", $this->z);
|
||||
|
||||
$nbt->Blocks = new ByteArray("Blocks", $this->getBlockIdArray());
|
||||
$nbt->Data = new ByteArray("Data", $this->getBlockDataArray());
|
||||
$nbt->SkyLight = new ByteArray("SkyLight", $this->getBlockSkyLightArray());
|
||||
$nbt->BlockLight = new ByteArray("BlockLight", $this->getBlockLightArray());
|
||||
|
||||
$nbt->Biomes = new ByteArray("Biomes", $this->getBiomeIdArray());
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", $this->getBiomeColorArray());
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($this->getEntities() as $entity){
|
||||
if(!($entity instanceof Player) and $entity->closed !== true){
|
||||
$entity->saveNBT();
|
||||
$entities[] = $entity->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("Entities", $entities);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
|
||||
$tiles = [];
|
||||
foreach($this->getTiles() as $tile){
|
||||
if($tile->closed !== true){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("TileEntities", $tiles);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new Compound("", array("Level" => $nbt)));
|
||||
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Compound
|
||||
*/
|
||||
public function getNBT(){
|
||||
return $this->nbt;
|
||||
}
|
||||
}
|
123
src/pocketmine/level/format/mcregion/ChunkRequestTask.php
Normal file
123
src/pocketmine/level/format/mcregion/ChunkRequestTask.php
Normal file
@ -0,0 +1,123 @@
|
||||
<?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\format\mcregion;
|
||||
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\tile\Spawnable;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class ChunkRequestTask extends AsyncTask{
|
||||
|
||||
protected $levelId;
|
||||
protected $chunkX;
|
||||
protected $chunkZ;
|
||||
protected $compressionLevel;
|
||||
|
||||
protected $blocks;
|
||||
protected $data;
|
||||
protected $skyLight;
|
||||
protected $blockLight;
|
||||
|
||||
/** @var string[256] */
|
||||
protected $biomeIds;
|
||||
/** @var int[] */
|
||||
protected $biomeColors;
|
||||
|
||||
protected $tiles;
|
||||
|
||||
public function __construct(McRegion $level, $levelId, $chunkX, $chunkZ){
|
||||
$this->levelId = $levelId;
|
||||
$this->chunkX = $chunkX;
|
||||
$this->chunkZ = $chunkZ;
|
||||
$chunk = $level->getChunk($chunkX, $chunkZ, false);
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new \Exception("Invalid Chunk sent");
|
||||
}
|
||||
$this->blocks = $chunk->getBlockIdArray();
|
||||
$this->data = $chunk->getBlockDataArray();
|
||||
$this->skyLight = $chunk->getBlockSkyLightArray();
|
||||
$this->blockLight = $chunk->getBlockLightArray();
|
||||
$this->biomeIds = $chunk->getBiomeIdArray();
|
||||
$this->biomeColors = $chunk->getBiomeColorArray();
|
||||
|
||||
$tiles = "";
|
||||
$nbt = new NBT(NBT::LITTLE_ENDIAN);
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile instanceof Spawnable){
|
||||
$nbt->setData($tile->getSpawnCompound());
|
||||
$tiles .= $nbt->write();
|
||||
}
|
||||
}
|
||||
|
||||
$this->tiles = $tiles;
|
||||
|
||||
$this->compressionLevel = Level::$COMPRESSION_LEVEL;
|
||||
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
$biomeColors = "";
|
||||
|
||||
foreach($this->biomeColors as $color){
|
||||
$biomeColors .= Binary::writeInt($color);
|
||||
}
|
||||
|
||||
$ordered = zlib_encode(Binary::writeLInt($this->chunkX) . Binary::writeLInt($this->chunkZ) . $this->blocks . $this->data . $this->skyLight . $this->blockLight . $this->biomeIds . $biomeColors . $this->tiles, ZLIB_ENCODING_DEFLATE, $this->compressionLevel);
|
||||
|
||||
$this->setResult($ordered);
|
||||
}
|
||||
|
||||
public function getColumn(&$data, $x, $z){
|
||||
$i = ($z << 4) + $x;
|
||||
$column = "";
|
||||
for($y = 0; $y < 128; ++$y){
|
||||
$column .= $data{($y << 8) + $i};
|
||||
}
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getHalfColumn(&$data, $x, $z){
|
||||
$i = ($z << 3) + ($x >> 1);
|
||||
$column = "";
|
||||
if(($x & 1) === 0){
|
||||
for($y = 0; $y < 128; $y += 2){
|
||||
$column .= ($data{($y << 7) + $i} & "\x0f") | chr((ord($data{(($y + 1) << 7) + $i}) & 0x0f) << 4);
|
||||
}
|
||||
}else{
|
||||
for($y = 0; $y < 128; $y += 2){
|
||||
$column .= chr((ord($data{($y << 7) + $i}) & 0xf0) >> 4) | ($data{(($y + 1) << 7) + $i} & "\xf0");
|
||||
}
|
||||
}
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
$level = $server->getLevel($this->levelId);
|
||||
if($level instanceof Level and $this->hasResult()){
|
||||
$level->chunkRequestCallback($this->chunkX, $this->chunkZ, $this->getResult());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
281
src/pocketmine/level/format/mcregion/McRegion.php
Normal file
281
src/pocketmine/level/format/mcregion/McRegion.php
Normal file
@ -0,0 +1,281 @@
|
||||
<?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\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\Player;
|
||||
|
||||
class McRegion extends BaseLevelProvider{
|
||||
|
||||
/** @var RegionLoader[] */
|
||||
protected $regions = [];
|
||||
|
||||
/** @var Chunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
public static function getProviderName(){
|
||||
return "mcregion";
|
||||
}
|
||||
|
||||
public static function getProviderOrder(){
|
||||
return self::ORDER_ZXY;
|
||||
}
|
||||
|
||||
public static function usesChunkSection(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function isValid($path){
|
||||
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
|
||||
|
||||
if($isValid){
|
||||
$files = glob($path . "/region/*.mc*");
|
||||
foreach($files as $f){
|
||||
if(strpos($f, ".mca") !== false){ //Anvil
|
||||
$isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public static function generate($path, $name, $seed, $generator, array $options = []){
|
||||
@mkdir($path, 0777, true);
|
||||
@mkdir($path . "/region", 0777);
|
||||
//TODO, add extra details
|
||||
$levelData = new Compound("Data", [
|
||||
"hardcore" => new Byte("hardcore", 0),
|
||||
"initialized" => new Byte("initialized", 1),
|
||||
"GameType" => new Int("GameType", 0),
|
||||
"generatorVersion" => new Int("generatorVersion", 1), //2 in MCPE
|
||||
"SpawnX" => new Int("SpawnX", 128),
|
||||
"SpawnY" => new Int("SpawnY", 70),
|
||||
"SpawnZ" => new Int("SpawnZ", 128),
|
||||
"version" => new Int("version", 19133),
|
||||
"DayTime" => new Int("DayTime", 0),
|
||||
"LastPlayed" => new Long("LastPlayed", microtime(true) * 1000),
|
||||
"RandomSeed" => new Long("RandomSeed", $seed),
|
||||
"SizeOnDisk" => new Long("SizeOnDisk", 0),
|
||||
"Time" => new Long("Time", 0),
|
||||
"generatorName" => new String("generatorName", Generator::getGeneratorName($generator)),
|
||||
"generatorOptions" => new String("generatorOptions", isset($options["preset"]) ? $options["preset"] : ""),
|
||||
"LevelName" => new String("LevelName", $name),
|
||||
"GameRules" => new Compound("GameRules", [])
|
||||
]);
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setData(new Compound(null, [
|
||||
"Data" => $levelData
|
||||
]));
|
||||
$buffer = $nbt->writeCompressed();
|
||||
@file_put_contents($path . "level.dat", $buffer);
|
||||
}
|
||||
|
||||
public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){
|
||||
$x = $chunkX >> 5;
|
||||
$z = $chunkZ >> 5;
|
||||
}
|
||||
|
||||
public function requestChunkTask($x, $z){
|
||||
return new ChunkRequestTask($this, $this->getLevel()->getID(), $x, $z);
|
||||
}
|
||||
|
||||
public function unloadChunks(){
|
||||
$this->chunks = [];
|
||||
}
|
||||
|
||||
public function getGenerator(){
|
||||
return $this->levelData["generatorName"];
|
||||
}
|
||||
|
||||
public function getGeneratorOptions(){
|
||||
return ["preset" => $this->levelData["generatorOptions"]];
|
||||
}
|
||||
|
||||
public function getLoadedChunks(){
|
||||
return $this->chunks;
|
||||
}
|
||||
|
||||
public function isChunkLoaded($x, $z){
|
||||
return isset($this->chunks[Level::chunkHash($x, $z)]);
|
||||
}
|
||||
|
||||
public function saveChunks(){
|
||||
foreach($this->chunks as $chunk){
|
||||
$this->saveChunk($chunk->getX(), $chunk->getZ());
|
||||
}
|
||||
}
|
||||
|
||||
public function loadChunk($chunkX, $chunkZ, $create = false){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunks[$index])){
|
||||
return true;
|
||||
}
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
$this->level->timings->syncChunkLoadDataTimer->startTiming();
|
||||
$chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded
|
||||
$this->level->timings->syncChunkLoadDataTimer->stopTiming();
|
||||
|
||||
if($chunk instanceof FullChunk){
|
||||
$this->chunks[$index] = $chunk;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function unloadChunk($x, $z, $safe = true){
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if($chunk instanceof FullChunk){
|
||||
if($safe === true and $this->isChunkLoaded($x, $z)){
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
$entity->close();
|
||||
}
|
||||
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
|
||||
$this->chunks[$index = Level::chunkHash($x, $z)] = null;
|
||||
|
||||
unset($this->chunks[$index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function saveChunk($x, $z){
|
||||
if($this->isChunkLoaded($x, $z)){
|
||||
$this->getRegion($x >> 5, $z >> 5)->writeChunk($this->getChunk($x, $z));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $x
|
||||
* @param $z
|
||||
*
|
||||
* @return RegionLoader
|
||||
*/
|
||||
protected function getRegion($x, $z){
|
||||
$index = $x . ":" . $z;
|
||||
|
||||
return isset($this->regions[$index]) ? $this->regions[$index] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param bool $create
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ, $create = false){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunks[$index])){
|
||||
return $this->chunks[$index];
|
||||
}else{
|
||||
$this->loadChunk($chunkX, $chunkZ, $create);
|
||||
return isset($this->chunks[$index]) ? $this->chunks[$index] : null;
|
||||
}
|
||||
}
|
||||
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new \Exception("Invalid Chunk class");
|
||||
}
|
||||
|
||||
if($chunk->isPopulated() === false){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false);
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
$region = $this->getRegion($regionX, $regionZ);
|
||||
$region->removeChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
|
||||
$this->loadChunk($chunkX, $chunkZ);
|
||||
}else{
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
//$this->saveChunk($chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
public static function createChunkSection($Y){
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) instanceof RegionLoader){
|
||||
return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isChunkPopulated($chunkX, $chunkZ){
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
if($chunk instanceof FullChunk){
|
||||
return $chunk->isPopulated();
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadRegion($x, $z){
|
||||
$index = $x . ":" . $z;
|
||||
if(isset($this->regions[$index])){
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->regions[$index] = new RegionLoader($this, $x, $z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->unloadChunks();
|
||||
foreach($this->regions as $index => $region){
|
||||
$region->close();
|
||||
unset($this->regions[$index]);
|
||||
}
|
||||
}
|
||||
}
|
329
src/pocketmine/level/format/mcregion/RegionLoader.php
Normal file
329
src/pocketmine/level/format/mcregion/RegionLoader.php
Normal file
@ -0,0 +1,329 @@
|
||||
<?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\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\ByteArray;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class RegionLoader{
|
||||
const VERSION = 1;
|
||||
const COMPRESSION_GZIP = 1;
|
||||
const COMPRESSION_ZLIB = 2;
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
protected $filePath;
|
||||
protected $filePointer;
|
||||
protected $lastSector;
|
||||
/** @var LevelProvider */
|
||||
protected $levelProvider;
|
||||
protected $locationTable = [];
|
||||
|
||||
public function __construct(LevelProvider $level, $regionX, $regionZ){
|
||||
$this->x = $regionX;
|
||||
$this->z = $regionZ;
|
||||
$this->levelProvider = $level;
|
||||
$this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.mcr";
|
||||
touch($this->filePath);
|
||||
$this->filePointer = fopen($this->filePath, "r+b");
|
||||
flock($this->filePointer, LOCK_EX);
|
||||
stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB
|
||||
stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB
|
||||
if(!file_exists($this->filePath)){
|
||||
$this->createBlank();
|
||||
}else{
|
||||
$this->loadLocationTable();
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct(){
|
||||
if(is_resource($this->filePointer)){
|
||||
$this->cleanGarbage();
|
||||
$this->writeLocationTable();
|
||||
flock($this->filePointer, LOCK_UN);
|
||||
fclose($this->filePointer);
|
||||
}
|
||||
}
|
||||
|
||||
protected function isChunkGenerated($index){
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
}
|
||||
|
||||
public function readChunk($x, $z, $generate = true){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($index < 0 or $index >= 4096){
|
||||
//Regenerate chunk due to corruption
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 1;
|
||||
}
|
||||
|
||||
if(!$this->isChunkGenerated($index)){
|
||||
if($generate === true){
|
||||
//Allocate space
|
||||
$this->locationTable[$index][0] = ++$this->lastSector;
|
||||
$this->locationTable[$index][1] = 1;
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt(-1) . chr(self::COMPRESSION_ZLIB), 4096, "\x00", STR_PAD_RIGHT));
|
||||
$this->writeLocationIndex($index);
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
$length = Binary::readInt(fread($this->filePointer, 4));
|
||||
$compression = ord(fgetc($this->filePointer));
|
||||
|
||||
if($length <= 0){ //Not yet generated
|
||||
$this->generateChunk($x, $z);
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
$length = Binary::readInt(fread($this->filePointer, 4));
|
||||
$compression = ord(fgetc($this->filePointer));
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
trigger_error("Corrupted bigger chunk detected", E_USER_WARNING);
|
||||
$this->locationTable[$index][1] = $length >> 12;
|
||||
$this->writeLocationIndex($index);
|
||||
}elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
|
||||
trigger_error("Invalid compression type", E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
$chunk = Chunk::fromBinary(fread($this->filePointer, $length - 1), $this->levelProvider);
|
||||
|
||||
return $chunk instanceof Chunk ? $chunk : false;
|
||||
}
|
||||
|
||||
public function chunkExists($x, $z){
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
}
|
||||
|
||||
public function generateChunk($x, $z){
|
||||
$nbt = new Compound("Level", []);
|
||||
$nbt->xPos = new Int("xPos", ($this->getX() * 32) + $x);
|
||||
$nbt->zPos = new Int("zPos", ($this->getZ() * 32) + $z);
|
||||
$nbt->LastUpdate = new Long("LastUpdate", 0);
|
||||
$nbt->LightPopulated = new Byte("LightPopulated", 0);
|
||||
$nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
||||
$nbt->V = new Byte("V", self::VERSION);
|
||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
|
||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
|
||||
$nbt->Blocks = new ByteArray("Blocks", str_repeat("\x00", 32768));
|
||||
$nbt->Data = new ByteArray("Data", $half = str_repeat("\x00", 16384));
|
||||
$nbt->SkyLight = new ByteArray("SkyLight", $half);
|
||||
$nbt->BlockLight = new ByteArray("BlockLight", $half);
|
||||
|
||||
$nbt->Entities = new Enum("Entities", []);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileEntities = new Enum("TileEntities", []);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new Compound("", array("Level" => $nbt)));
|
||||
$chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, self::$COMPRESSION_LEVEL);
|
||||
|
||||
$this->saveChunk($x, $z, $chunkData);
|
||||
}
|
||||
|
||||
protected function saveChunk($x, $z, $chunkData){
|
||||
$length = strlen($chunkData) + 1;
|
||||
$sectors = (int) ceil(($length + 4) / 4096);
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($this->locationTable[$index][1] < $sectors){
|
||||
$this->locationTable[$index][0] = $this->lastSector += $sectors; //The GC will clean this shift later
|
||||
}
|
||||
$this->locationTable[$index][1] = $sectors;
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
public function removeChunk($x, $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 0;
|
||||
}
|
||||
|
||||
public function writeChunk(FullChunk $chunk){
|
||||
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunk->toBinary());
|
||||
}
|
||||
|
||||
protected static function getChunkOffset($x, $z){
|
||||
return $x + ($z << 5);
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->writeLocationTable();
|
||||
flock($this->filePointer, LOCK_UN);
|
||||
fclose($this->filePointer);
|
||||
}
|
||||
|
||||
public function doSlowCleanUp(){
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
if($this->locationTable[$i][0] === 0 or $this->locationTable[$i][1] === 0){
|
||||
continue;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
$chunk = fread($this->filePointer, $this->locationTable[$i][1] << 12);
|
||||
$length = Binary::readInt(substr($chunk, 0, 4));
|
||||
if($length <= 1){
|
||||
$this->locationTable[$i] = array(0, 0, 0); //Non-generated chunk, remove it from index
|
||||
}
|
||||
$chunk = zlib_decode(substr($chunk, 5));
|
||||
if(strlen($chunk) <= 1){
|
||||
$this->locationTable[$i] = array(0, 0, 0); //Corrupted chunk, remove it
|
||||
continue;
|
||||
}
|
||||
$chunk = chr(self::COMPRESSION_ZLIB) . zlib_encode($chunk, 15, 9);
|
||||
$chunk = Binary::writeInt(strlen($chunk)) . $chunk;
|
||||
$sectors = (int) ceil(strlen($chunk) / 4096);
|
||||
if($sectors > $this->locationTable[$i][1]){
|
||||
$this->locationTable[$i][0] = $this->lastSector += $sectors;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
fwrite($this->filePointer, str_pad($chunk, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
}
|
||||
$this->writeLocationTable();
|
||||
$n = $this->cleanGarbage();
|
||||
$this->writeLocationTable();
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
private function cleanGarbage(){
|
||||
$sectors = [];
|
||||
foreach($this->locationTable as $index => $data){ //Calculate file usage
|
||||
if($data[0] === 0 or $data[1] === 0){
|
||||
$this->locationTable[$index] = array(0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
for($i = 0; $i < $data[1]; ++$i){
|
||||
$sectors[$data[0]] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($sectors) === ($this->lastSector - 2)){ //No collection needed
|
||||
return 0;
|
||||
}
|
||||
|
||||
ksort($sectors);
|
||||
$shift = 0;
|
||||
$lastSector = 1; //First chunk - 1
|
||||
|
||||
fseek($this->filePointer, 8192);
|
||||
$sector = 2;
|
||||
foreach($sectors as $sector => $index){
|
||||
if(($sector - $lastSector) > 1){
|
||||
$shift += $sector - $lastSector - 1;
|
||||
}
|
||||
if($shift > 0){
|
||||
fseek($this->filePointer, $sector << 12);
|
||||
$old = fread($this->filePointer, 4096);
|
||||
fseek($this->filePointer, ($sector - $shift) << 12);
|
||||
fwrite($this->filePointer, $old, 4096);
|
||||
}
|
||||
$this->locationTable[$index][0] -= $shift;
|
||||
$lastSector = $sector;
|
||||
}
|
||||
ftruncate($this->filePointer, ($sector + 1) << 12); //Truncate to the end of file written
|
||||
return $shift;
|
||||
}
|
||||
|
||||
protected function loadLocationTable(){
|
||||
fseek($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
$table = fread($this->filePointer, 4 * 1024 * 2);
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$index = Binary::readInt(substr($table, $i << 2, 4));
|
||||
$this->locationTable[$i] = array(($index & ~0xff) >> 8, $index & 0xff, Binary::readInt(substr($table, 4096 + ($i << 2), 4)));
|
||||
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
||||
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function writeLocationTable(){
|
||||
$table = "";
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$table .= Binary::writeInt(($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]);
|
||||
}
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$table .= Binary::writeInt($this->locationTable[$i][2]);
|
||||
}
|
||||
fseek($this->filePointer, 0);
|
||||
fwrite($this->filePointer, $table, 4096 * 2);
|
||||
}
|
||||
|
||||
protected function writeLocationIndex($index){
|
||||
fseek($this->filePointer, $index << 2);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
|
||||
fseek($this->filePointer, 4096 + ($index << 2));
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
|
||||
}
|
||||
|
||||
protected function createBlank(){
|
||||
fseek($this->filePointer, 0);
|
||||
ftruncate($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
$table = "";
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i] = array(0, 0);
|
||||
$table .= Binary::writeInt(0);
|
||||
}
|
||||
|
||||
$time = time();
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i][2] = $time;
|
||||
$table .= Binary::writeInt($time);
|
||||
}
|
||||
|
||||
fwrite($this->filePointer, $table, 4096 * 2);
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\block\CoalOre;
|
||||
use pocketmine\block\DiamondOre;
|
||||
use pocketmine\block\Dirt;
|
||||
@ -30,7 +31,6 @@ use pocketmine\block\IronOre;
|
||||
use pocketmine\block\LapisOre;
|
||||
use pocketmine\block\RedstoneOre;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\generator\populator\Ore;
|
||||
use pocketmine\level\generator\populator\Populator;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
@ -39,7 +39,7 @@ use pocketmine\utils\Random;
|
||||
class Flat extends Generator{
|
||||
/** @var GenerationChunkManager */
|
||||
private $level;
|
||||
/** @var SimpleChunk */
|
||||
/** @var FullChunk */
|
||||
private $chunk;
|
||||
/** @var Random */
|
||||
private $random;
|
||||
@ -59,11 +59,7 @@ class Flat extends Generator{
|
||||
$this->preset = "2;7,2x3,2;1;";
|
||||
//$this->preset = "2;7,59x1,3x3,2;1;spawn(radius=10 block=89),decoration(treecount=80 grasscount=45)";
|
||||
$this->options = $options;
|
||||
if(isset($options["preset"]) and $options["preset"] != ""){
|
||||
$this->parsePreset($options["preset"]);
|
||||
}else{
|
||||
$this->parsePreset($this->preset);
|
||||
}
|
||||
|
||||
if(isset($this->options["decoration"])){
|
||||
$ores = new Ore();
|
||||
$ores->setOreTypes(array(
|
||||
@ -110,7 +106,8 @@ class Flat extends Generator{
|
||||
}
|
||||
|
||||
|
||||
$this->chunk = new SimpleChunk(null, null, SimpleChunk::FLAG_GENERATED);
|
||||
$this->chunk = $this->level->getChunk(0, 0);
|
||||
$this->chunk->setGenerated();
|
||||
|
||||
for($Z = 0; $Z < 16; ++$Z){
|
||||
for($X = 0; $X < 16; ++$X){
|
||||
@ -125,6 +122,7 @@ class Flat extends Generator{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
preg_match_all('#(([0-9a-z_]{1,})\(?([0-9a-z_ =:]{0,})\)?),?#', $options, $matches);
|
||||
foreach($matches[2] as $i => $option){
|
||||
$params = true;
|
||||
@ -145,6 +143,13 @@ class Flat extends Generator{
|
||||
public function init(GenerationChunkManager $level, Random $random){
|
||||
$this->level = $level;
|
||||
$this->random = $random;
|
||||
|
||||
if(isset($this->options["preset"]) and $this->options["preset"] != ""){
|
||||
$this->parsePreset($this->options["preset"]);
|
||||
}else{
|
||||
$this->parsePreset($this->preset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
@ -30,10 +30,10 @@ class GenerationChunkManager implements ChunkManager{
|
||||
|
||||
protected $levelID;
|
||||
|
||||
/** @var SimpleChunk[] */
|
||||
/** @var FullChunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
/** @var \SplObjectStorage<SimpleChunk> */
|
||||
/** @var \SplObjectStorage<FullChunk> */
|
||||
protected $unloadQueue;
|
||||
|
||||
/** @var Generator */
|
||||
@ -44,6 +44,8 @@ class GenerationChunkManager implements ChunkManager{
|
||||
|
||||
protected $seed;
|
||||
|
||||
protected $changes = [];
|
||||
|
||||
public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){
|
||||
if(!is_subclass_of($class, "pocketmine\\level\\generator\\Generator")){
|
||||
throw new \Exception("Class is not a subclass of Generator");
|
||||
@ -77,49 +79,49 @@ class GenerationChunkManager implements ChunkManager{
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return SimpleChunk
|
||||
* @return FullChunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
$chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index];
|
||||
$this->unloadQueue->detach($chunk);
|
||||
$this->changes[$index] = $chunk;
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $set
|
||||
*
|
||||
* @return SimpleChunk[]
|
||||
* @return FullChunk[]
|
||||
*/
|
||||
public function getChangedChunks($set = true){
|
||||
$changed = [];
|
||||
foreach($this->chunks as $chunk){
|
||||
if($chunk->hasChanged($set)){
|
||||
$changed[] = $chunk;
|
||||
}
|
||||
}
|
||||
public function getChangedChunks(){
|
||||
return $this->changes;
|
||||
}
|
||||
|
||||
return $changed;
|
||||
public function cleanChangedChunks(){
|
||||
$this->changes = [];
|
||||
}
|
||||
|
||||
public function doGarbageCollection(){
|
||||
if($this->unloadQueue->count() > 0){
|
||||
/** @var SimpleChunk $chunk */
|
||||
/** @var FullChunk $chunk */
|
||||
foreach($this->unloadQueue as $chunk){
|
||||
if(!$chunk->hasChanged(false)){
|
||||
unset($this->chunks[Level::chunkHash($chunk->getX(), $chunk->getZ())]);
|
||||
if(isset($this->changes[$index = Level::chunkHash($chunk->getX(), $chunk->getZ())])){
|
||||
continue;
|
||||
}
|
||||
unset($this->chunks[$index]);
|
||||
$this->unloadQueue->detach($chunk);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->chunks as $chunk){
|
||||
if(isset($this->changes[$index = Level::chunkHash($chunk->getX(), $chunk->getZ())])){
|
||||
continue;
|
||||
}
|
||||
$this->unloadQueue->attach($chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = new SimpleChunk($chunkX, $chunkZ, 0);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $this->requestChunk($chunkX, $chunkZ);
|
||||
$this->generator->generateChunk($chunkX, $chunkZ);
|
||||
$this->setChunkGenerated($chunkX, $chunkZ);
|
||||
}
|
||||
@ -150,11 +152,13 @@ class GenerationChunkManager implements ChunkManager{
|
||||
}
|
||||
|
||||
public function setChunkGenerated($chunkX, $chunkZ){
|
||||
$this->getChunk($chunkX, $chunkZ)->setGenerated(true);
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setGenerated(true);
|
||||
}
|
||||
|
||||
public function setChunkPopulated($chunkX, $chunkZ){
|
||||
$this->getChunk($chunkX, $chunkZ)->setPopulated(true);
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setPopulated(true);
|
||||
}
|
||||
|
||||
protected function requestChunk($chunkX, $chunkZ){
|
||||
@ -166,11 +170,12 @@ class GenerationChunkManager implements ChunkManager{
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param SimpleChunk $chunk
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
if($chunk->isGenerated() and $chunk->isPopulated()){
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
|
||||
$this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
$this->changes[$index] = $chunk;
|
||||
if($chunk->isPopulated()){
|
||||
//TODO: Queue to be sent
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
@ -60,7 +60,8 @@ class GenerationManager{
|
||||
* int32 levelID
|
||||
* int32 chunkX
|
||||
* int32 chunkZ
|
||||
* byte flags (1 generated, 2 populated)
|
||||
* byte className length
|
||||
* byte[] className
|
||||
* byte[] chunk (none if generated flag is not set)
|
||||
*/
|
||||
const PACKET_SEND_CHUNK = 0x02;
|
||||
@ -144,10 +145,11 @@ class GenerationManager{
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->generatedQueue[$levelID][$index] = true;
|
||||
if(count($this->generatedQueue[$levelID]) > 6){
|
||||
$this->levels[$levelID]->doGarbageCollection();
|
||||
foreach($this->levels[$levelID]->getChangedChunks(true) as $chunk){
|
||||
foreach($this->levels[$levelID]->getChangedChunks() as $chunk){
|
||||
$this->sendChunk($levelID, $chunk);
|
||||
}
|
||||
$this->levels[$levelID]->doGarbageCollection();
|
||||
$this->levels[$levelID]->cleanChangedChunks();
|
||||
$this->generatedQueue[$levelID] = [];
|
||||
}
|
||||
}
|
||||
@ -166,7 +168,7 @@ class GenerationManager{
|
||||
$this->requestQueue->enqueue([$levelID, $chunkX, $chunkZ]);
|
||||
}
|
||||
|
||||
protected function receiveChunk($levelID, SimpleChunk $chunk){
|
||||
protected function receiveChunk($levelID, FullChunk $chunk){
|
||||
if($this->needsChunk !== null and $this->needsChunk[0] === $levelID){
|
||||
if($this->needsChunk[1] === $chunk->getX() and $this->needsChunk[2] === $chunk->getZ()){
|
||||
$this->needsChunk = $chunk;
|
||||
@ -180,7 +182,7 @@ class GenerationManager{
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return SimpleChunk
|
||||
* @return FullChunk
|
||||
*/
|
||||
public function requestChunk($levelID, $chunkX, $chunkZ){
|
||||
$this->needsChunk = [$levelID, $chunkX, $chunkZ];
|
||||
@ -188,19 +190,19 @@ class GenerationManager{
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
|
||||
do{
|
||||
$this->readPacket();
|
||||
}while($this->shutdown !== true and !($this->needsChunk instanceof SimpleChunk));
|
||||
}while($this->shutdown !== true and !($this->needsChunk instanceof FullChunk));
|
||||
|
||||
$chunk = $this->needsChunk;
|
||||
$this->needsChunk = null;
|
||||
if($chunk instanceof SimpleChunk){
|
||||
if($chunk instanceof FullChunk){
|
||||
return $chunk;
|
||||
}else{
|
||||
return new SimpleChunk($chunkX, $chunkZ, 0);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function sendChunk($levelID, SimpleChunk $chunk){
|
||||
$binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . $chunk->toBinary();
|
||||
public function sendChunk($levelID, FullChunk $chunk){
|
||||
$binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
|
||||
}
|
||||
|
||||
@ -234,7 +236,11 @@ class GenerationManager{
|
||||
}elseif($pid === self::PACKET_SEND_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunk = SimpleChunk::fromBinary(substr($packet, $offset));
|
||||
$len = ord($packet{$offset++});
|
||||
/** @var FullChunk $class */
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$chunk = $class::fromBinary(substr($packet, $offset));
|
||||
$this->receiveChunk($levelID, $chunk);
|
||||
}elseif($pid === self::PACKET_OPEN_LEVEL){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Binary;
|
||||
@ -77,8 +77,8 @@ class GenerationRequestManager{
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
protected function sendChunk($levelID, SimpleChunk $chunk){
|
||||
$binary = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . $chunk->toBinary();
|
||||
protected function sendChunk($levelID, FullChunk $chunk){
|
||||
$binary = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
|
||||
}
|
||||
|
||||
@ -89,14 +89,14 @@ class GenerationRequestManager{
|
||||
|
||||
protected function handleRequest($levelID, $chunkX, $chunkZ){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$this->sendChunk($levelID, $level->getChunk($chunkX, $chunkZ, false));
|
||||
$this->sendChunk($levelID, $level->getChunkAt($chunkX, $chunkZ, true));
|
||||
}else{
|
||||
$buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID);
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
}
|
||||
}
|
||||
|
||||
protected function receiveChunk($levelID, SimpleChunk $chunk){
|
||||
protected function receiveChunk($levelID, FullChunk $chunk){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
}else{
|
||||
@ -106,10 +106,11 @@ class GenerationRequestManager{
|
||||
}
|
||||
|
||||
public function handlePackets(){
|
||||
if(($len = @socket_read($this->socket, 4)) !== false){
|
||||
if(($len = @socket_read($this->socket, 4)) !== false and $len !== ""){
|
||||
if(strlen($len) < 4){
|
||||
$len .= $this->socketRead(4 - strlen($len));
|
||||
}
|
||||
|
||||
$packet = $this->socketRead(Binary::readInt($len));
|
||||
$pid = ord($packet{0});
|
||||
$offset = 1;
|
||||
@ -124,7 +125,11 @@ class GenerationRequestManager{
|
||||
}elseif($pid === GenerationManager::PACKET_SEND_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunk = SimpleChunk::fromBinary(substr($packet, $offset));
|
||||
$len = ord($packet{$offset++});
|
||||
/** @var FullChunk $class */
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$chunk = $class::fromBinary(substr($packet, $offset));
|
||||
$this->receiveChunk($levelID, $chunk);
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ class GenerationThread extends \Thread{
|
||||
}
|
||||
|
||||
public function run(){
|
||||
error_reporting(-1);
|
||||
//Load removed dependencies, can't use require_once()
|
||||
foreach($this->loadPaths as $name => $path){
|
||||
if(!class_exists($name, false) and !class_exists($name, false)){
|
||||
|
@ -18,6 +18,10 @@ debug:
|
||||
#Enables /status
|
||||
commands: false
|
||||
|
||||
level-settings:
|
||||
default-format: mcregion #The default format that levels will use when created
|
||||
convert-format: false #If true, converts from a format that is not the default to the default format on load
|
||||
|
||||
chunk-sending:
|
||||
per-tick: 4
|
||||
compression-level: 9
|
||||
|
@ -26,6 +26,7 @@ use pocketmine\inventory\DoubleChestInventory;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
@ -42,7 +43,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
/** @var DoubleChestInventory */
|
||||
protected $doubleInventory = null;
|
||||
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::CHEST;
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->inventory = new ChestInventory($this);
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\inventory\FurnaceRecipe;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
@ -37,7 +38,7 @@ class Furnace extends Tile implements InventoryHolder, Container{
|
||||
/** @var FurnaceInventory */
|
||||
protected $inventory;
|
||||
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::FURNACE;
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->inventory = new FurnaceInventory($this);
|
||||
|
@ -22,13 +22,14 @@
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\String;
|
||||
|
||||
class Sign extends Spawnable{
|
||||
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::SIGN;
|
||||
if(!isset($nbt->Text1)){
|
||||
$nbt->Text1 = new String("Text1", "");
|
||||
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\network\protocol\EntityDataPacket;
|
||||
@ -48,7 +49,7 @@ abstract class Spawnable extends Tile{
|
||||
|
||||
public abstract function getSpawnCompound();
|
||||
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->spawnToAll();
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
@ -62,7 +63,7 @@ abstract class Tile extends Position{
|
||||
/** @var \pocketmine\event\TimingsHandler */
|
||||
public $tickTimer;
|
||||
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
if($chunk->getLevel() === null){
|
||||
throw new \Exception("Invalid garbage Chunk given to Tile");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user