diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index acc55c374..3cd22d47b 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -31,6 +31,7 @@ use pocketmine\event\entity\EntityMotionEvent; use pocketmine\event\entity\EntityMoveEvent; use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntityTeleportEvent; +use pocketmine\level\format\Chunk; use pocketmine\level\format\pmf\LevelFormat; use pocketmine\level\Level; use pocketmine\level\Position; @@ -75,11 +76,8 @@ abstract class Entity extends Position implements Metadatable{ /** @var int */ public $chunkZ; - /** - * TODO: REMOVE - * @var int - */ - public $chunkIndex; + /** @var Chunk */ + public $chunk; public $lastX; public $lastY; @@ -141,12 +139,13 @@ abstract class Entity extends Position implements Metadatable{ public $closed = false; - public function __construct(Level $level, Compound $nbt){ + public function __construct(Chunk $chunk, Compound $nbt){ $this->id = Entity::$entityCount++; $this->justCreated = true; $this->namedtag = $nbt; - $this->setLevel($level, true); //Create a hard reference - $this->server = Server::getInstance(); + $this->chunk = $chunk; + $this->setLevel($chunk->getLevel()->getLevel(), true); //Create a hard reference + $this->server = $chunk->getLevel()->getLevel()->getServer(); $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); $this->setPositionAndRotation(new Vector3( @@ -187,11 +186,9 @@ abstract class Entity extends Position implements Metadatable{ } $this->invulnerable = $this->namedtag["Invulnerable"] > 0 ? true : false; - $index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4); - $this->chunkIndex = $index; + $this->chunk->addEntity($this); $this->getLevel()->addEntity($this); $this->initEntity(); - $this->getLevel()->chunkEntities[$this->chunkIndex][$this->id] = $this; $this->lastUpdate = $this->spawnTime = microtime(true); $this->justCreated = false; $this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this)); @@ -228,7 +225,7 @@ abstract class Entity extends Position implements Metadatable{ * @param Player $player */ public function spawnTo(Player $player){ - if(!isset($this->hasSpawned[$player->getID()]) and $player->chunksLoaded[$this->chunkIndex] !== 0xff){ + if(!isset($this->hasSpawned[$player->getID()]) and $player->chunksLoaded[Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())] !== 0xff){ $this->hasSpawned[$player->getID()] = $player; } } @@ -585,7 +582,7 @@ abstract class Entity extends Position implements Metadatable{ } $this->getLevel()->removeEntity($this); - unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]); + $this->chunk->removeEntity($this); $this->despawnFromAll(); if($this instanceof Player){ foreach($this->chunksLoaded as $index => $Yndex){ @@ -611,7 +608,7 @@ abstract class Entity extends Position implements Metadatable{ $this->dataPacket($pk); } $this->spawnToAll(); - $this->chunkIndex = false; + $this->chunk = null; } public function getPosition(){ @@ -856,12 +853,12 @@ abstract class Entity extends Position implements Metadatable{ $this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius); - if(($index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4)) !== $this->chunkIndex){ - if($this->chunkIndex !== false){ - unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]); + if($this->chunk === null or ($this->chunk->getX() !== ($this->x >> 4) and $this->chunk->getZ() !== ($this->z >> 4))){ + if($this->chunk instanceof Chunk){ + $this->chunk->removeEntity($this); } - $this->chunkIndex = $index; $this->getLevel()->loadChunk($this->x >> 4, $this->z >> 4); + $this->chunk = $this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4); if(!$this->justCreated){ $newChunk = $this->getLevel()->getUsingChunk($this->x >> 4, $this->z >> 4); @@ -877,7 +874,7 @@ abstract class Entity extends Position implements Metadatable{ } } - $this->getLevel()->chunkEntities[$this->chunkIndex][$this->id] = $this; + $this->chunk->addEntity($this); } $this->scheduleUpdate(); @@ -970,8 +967,10 @@ abstract class Entity extends Position implements Metadatable{ if($this->closed === false){ $this->closed = true; unset(Entity::$needUpdate[$this->id]); + if($this->chunk instanceof Chunk){ + $this->chunk->removeEntity($this); + } $this->getLevel()->removeEntity($this); - unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]); $this->despawnFromAll(); $this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this)); } diff --git a/src/pocketmine/level/ChunkManager.php b/src/pocketmine/level/ChunkManager.php new file mode 100644 index 000000000..f1ada3547 --- /dev/null +++ b/src/pocketmine/level/ChunkManager.php @@ -0,0 +1,66 @@ +levelId = static::$levelIdCounter++; $this->server = $server; - $this->provider = $provider; + if(is_subclass_of($provider, "pocketmine\\level\\format\\LevelProvider", true)){ + $this->provider = new $provider($this, $path); + }else{ + throw new \Exception("Provider is not a subclass of LevelProvider"); + } $this->players = new \SplObjectStorage(); $this->entities = new \SplObjectStorage(); $this->tiles = new \SplObjectStorage(); } + /** + * @return Server + */ + public function getServer(){ + return $this->server; + } + /** * @return LevelProvider */ @@ -136,35 +146,6 @@ class Level implements Metadatable{ return Block::get($blockId, $meta, Position::fromObject(clone $pos, $this)); } - public function getCollisionBlocks(AxisAlignedBB $bb){ - $minX = floor($bb->minX); - $minY = floor($bb->minY); - $minZ = floor($bb->minZ); - $maxX = floor($bb->maxX + 1); - $maxY = floor($bb->maxY + 1); - $maxZ = floor($bb->maxZ + 1); - - $collides = []; - - for($z = $minZ; $z < $maxZ; ++$z){ - for($x = $minX; $x < $maxX; ++$x){ - if($this->isChunkLoaded($x >> 4, $z >> 4)){ - for($y = $minY - 1; $y < $maxY; ++$y){ - $this->getBlock(new Vector3($x, $y, $z))->collidesWithBB($bb, $collides); - } - } - } - } - - return $collides; - } - - public function isFullBlock(Vector3 $pos){ - $bb = $this->getBlock($pos)->getBoundingBox(); - - return $bb instanceof AxisAlignedBB and $bb->getAverageEdgeLength() >= 1; - } - /** * Sets on Vector3 the data from a Block object, * does block updates and puts the changes to the send queue. @@ -173,7 +154,8 @@ class Level implements Metadatable{ * @param Block $block */ public function setBlock(Vector3 $pos, Block $block){ - //TODO: handle block setting + $this->getChunkAt($pos->x >> 4, $pos->z >> 4)->setBlock($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f, $block->getID(), $block->getDamage()); + //TODO: //block updates //block change send queue //etc. @@ -288,7 +270,7 @@ class Level implements Metadatable{ * * @return Chunk */ - protected function getChunkAt($x, $z, $create = false){ + public function getChunkAt($x, $z, $create = false){ $this->provider->getChunk($x, $z, $create); } @@ -314,8 +296,7 @@ class Level implements Metadatable{ * @return bool */ public function isChunkLoaded($x, $z){ - //TODO - return false; + return $this->provider->isChunkLoaded($x, $z); } /** diff --git a/src/pocketmine/level/Level_OLD.php b/src/pocketmine/level/Level_OLD.php index 4cdf51066..cc7597908 100644 --- a/src/pocketmine/level/Level_OLD.php +++ b/src/pocketmine/level/Level_OLD.php @@ -1140,69 +1140,6 @@ class Level{ return $this->level->isChunkLoaded($X, $Z); } - /** - * Loads a chunk - * - * @param int $X - * @param int $Z - * - * @return bool - */ - public function loadChunk($X, $Z){ - $index = LevelFormat::getIndex($X, $Z); - if(isset($this->usedChunks[$index])){ - return true; - }elseif($this->level->loadChunk($X, $Z) !== false){ - $this->usedChunks[$index] = []; - if(!isset($this->chunkTiles[$index])){ - $this->chunkTiles[$index] = []; - } - if(!isset($this->chunkEntities[$index])){ - $this->chunkEntities[$index] = []; - } - $tags = $this->level->getChunkNBT($X, $Z); - if(isset($tags->Entities)){ - foreach($tags->Entities as $nbt){ - 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 - - } - } - } - if(isset($tags->TileEntities)){ - foreach($tags->TileEntities as $nbt){ - 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; - } - } - } - - return true; - } - - return false; - } - /** * Unloads a chunk * diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 2502bbf88..4881cd2d6 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -38,7 +38,7 @@ interface Chunk{ public function getZ(); /** - * @return \pocketmine\level\Level + * @return \pocketmine\level\format\LevelProvider */ public function getLevel(); @@ -60,6 +60,7 @@ interface Chunk{ * @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); diff --git a/src/pocketmine/level/format/ChunkSection.php b/src/pocketmine/level/format/ChunkSection.php index ff81872fa..c2fe54e01 100644 --- a/src/pocketmine/level/format/ChunkSection.php +++ b/src/pocketmine/level/format/ChunkSection.php @@ -23,6 +23,11 @@ namespace pocketmine\level\format; interface ChunkSection{ + /** + * @return int + */ + public function getY(); + /** * @param int $x 0-15 * @param int $y 0-15 diff --git a/src/pocketmine/level/format/LevelProvider.php b/src/pocketmine/level/format/LevelProvider.php index 0793272c2..a3f5875d6 100644 --- a/src/pocketmine/level/format/LevelProvider.php +++ b/src/pocketmine/level/format/LevelProvider.php @@ -21,16 +21,16 @@ namespace pocketmine\level\format; +use pocketmine\level\Level; use pocketmine\math\Vector3; -use pocketmine\Server; interface LevelProvider{ /** - * @param Server $server + * @param Level $level * @param string $path */ - public function __construct(Server $server, $path); + public function __construct(Level $level, $path); /** @return string */ public function getPath(); @@ -57,21 +57,64 @@ interface LevelProvider{ */ public function getChunk($X, $Z, $create = false); - /** - * @return bool - */ public function saveChunks(); + /** + * @param int $X + * @param int $Z + */ + public function saveChunk($X, $Z); + public function unloadChunks(); + /** + * @param int $X + * @param int $Z + * + * @return bool + */ public function loadChunk($X, $Z); - public function unloadChunk($X, $Z); + /** + * @param int $X + * @param int $Z + * @param bool $safe + * + * @return bool + */ + public function unloadChunk($X, $Z, $safe = true); + /** + * @param int $X + * @param int $Z + * + * @return bool + */ public function isChunkGenerated($X, $Z); + /** + * @param int $X + * @param int $Z + * + * @return bool + */ + public function isChunkLoaded($X, $Z); + + /** + * @return string + */ public function getName(); + /** + * @return int + */ + public function getTime(); + + /** + * @param int $value + */ + public function setTime($value); + /** * @return Vector3 */ @@ -87,4 +130,9 @@ interface LevelProvider{ */ public function getLoadedChunks(); + /** + * @return Level + */ + public function getLevel(); + } \ No newline at end of file diff --git a/src/pocketmine/level/format/SimpleChunk.php b/src/pocketmine/level/format/SimpleChunk.php new file mode 100644 index 000000000..c2a4ed1c3 --- /dev/null +++ b/src/pocketmine/level/format/SimpleChunk.php @@ -0,0 +1,242 @@ +x = $chunkX; + $this->z = $chunkZ; + $this->flags = $flags; + for($y = 0; $y < self::$HEIGHT; ++$y){ + $this->ids[$y] = isset($ids[$y]) ? $ids[$y] : str_repeat("\x00", 4096); + $this->meta[$y] = isset($meta[$y]) ? $meta[$y] : str_repeat("\x00", 2048); + } + } + + /** + * @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->flags = ($this->flags & ~self::FLAG_GENERATED) | ($value === true ? self::FLAG_GENERATED : 0); + } + + /** + * @param bool $value + */ + public function setPopulated($value = 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->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(($y & 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){ + $i = (($y & 0x0f) << 7) + ($z << 3) + ($x >> 1); + $old_m = ord($this->meta[$y >> 4]{$i}); + if(($y & 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){ + 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); + } + +} \ No newline at end of file diff --git a/src/pocketmine/level/format/anvil/Anvil.php b/src/pocketmine/level/format/anvil/Anvil.php index b7ce9d6a1..8fa0e2c7e 100644 --- a/src/pocketmine/level/format/anvil/Anvil.php +++ b/src/pocketmine/level/format/anvil/Anvil.php @@ -22,20 +22,122 @@ namespace pocketmine\level\format\anvil; use pocketmine\level\format\generic\BaseLevelProvider; +use pocketmine\level\Level; +use pocketmine\Player; class Anvil extends BaseLevelProvider{ - protected $basePath; - public function __construct($path, $levelName){ - $this->basePath = realpath($path) . "/"; - } + /** @var RegionLoader */ + protected $regions = []; + + /** @var Chunk[] */ + protected $chunks = []; + public static function isValid($path){ - return file_exists(realpath($path) . "region/"); + return file_exists(realpath($path) . "level.dat") and file_exists(realpath($path) . "region/"); } public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){ $x = $chunkX >> 5; $z = $chunkZ >> 5; } + + public function unloadChunks(){ + $this->chunks = []; + } + + 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){ + $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); + $chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, true); //generate empty chunk if not loaded + if($chunk instanceof Chunk){ + $this->chunks[$index] = $chunk; + }else{ + return false; + } + } + + public function unloadChunk($x, $z, $safe = true){ + if($safe === true and $this->isChunkLoaded($x, $z)){ + $chunk = $this->getChunk($x, $z); + foreach($chunk->getEntities() as $entity){ + if($entity instanceof Player){ + return false; + } + } + } + + unset($this->chunks[Level::chunkHash($x, $z)]); + 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; + } + + public function getChunk($chunkX, $chunkZ, $create = false){ + $index = Level::chunkHash($chunkX, $chunkZ); + if(isset($this->chunks[$index])){ + return $this->chunks[$index]; + }elseif($create !== true){ + return null; + } + + $this->loadChunk($chunkX, $chunkZ); + return $this->getChunk($chunkX, $chunkZ, false); + } + + 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 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; + } } \ No newline at end of file diff --git a/src/pocketmine/level/format/anvil/Chunk.php b/src/pocketmine/level/format/anvil/Chunk.php index 6667edfb4..859ed984b 100644 --- a/src/pocketmine/level/format/anvil/Chunk.php +++ b/src/pocketmine/level/format/anvil/Chunk.php @@ -23,6 +23,7 @@ namespace pocketmine\level\format\anvil; use pocketmine\level\format\generic\BaseChunk; use pocketmine\level\format\generic\EmptyChunkSection; +use pocketmine\level\format\LevelProvider; use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Compound; @@ -33,7 +34,7 @@ class Chunk extends BaseChunk{ /** @var Compound */ protected $nbt; - public function __construct(Level $level, Compound $nbt){ + public function __construct(LevelProvider $level, Compound $nbt){ $this->nbt = $nbt; if($this->nbt->Entities instanceof Enum){ @@ -71,7 +72,7 @@ class Chunk extends BaseChunk{ } } - parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $sections); + parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $sections, $this->nbt["Entities"], $this->nbt["TileEntities"]); } public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false){ @@ -101,6 +102,14 @@ class Chunk extends BaseChunk{ //TODO: maxBlockY, biomeMap, biomeTemp - return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), $this->getLevel()->getTime(), $blockId, $blockData, $blockSkyLight, $blockLight, $emptySections, null, null, null, null); + //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); + } + + /** + * @return Compound + */ + public function getNBT(){ + return $this->nbt; } } \ No newline at end of file diff --git a/src/pocketmine/level/format/anvil/ChunkSection.php b/src/pocketmine/level/format/anvil/ChunkSection.php index ebe1cf469..8ddc2c6fa 100644 --- a/src/pocketmine/level/format/anvil/ChunkSection.php +++ b/src/pocketmine/level/format/anvil/ChunkSection.php @@ -39,6 +39,10 @@ class ChunkSection implements \pocketmine\level\format\ChunkSection{ $this->skyLight = (string) $nbt["SkyLight"]; } + public function getY(){ + return $this->y; + } + public function getBlockId($x, $y, $z){ return ord($this->blocks{($y << 8) + ($z << 4) + $x}); } diff --git a/src/pocketmine/level/format/anvil/RegionLoader.php b/src/pocketmine/level/format/anvil/RegionLoader.php index 9af563397..7a682828e 100644 --- a/src/pocketmine/level/format/anvil/RegionLoader.php +++ b/src/pocketmine/level/format/anvil/RegionLoader.php @@ -21,6 +21,7 @@ namespace pocketmine\level\format\anvil; +use pocketmine\level\format\LevelProvider; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\ByteArray; @@ -29,6 +30,7 @@ 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{ @@ -42,14 +44,15 @@ class RegionLoader{ protected $filePath; protected $filePointer; protected $lastSector; + /** @var LevelProvider */ + protected $levelProvider; protected $locationTable = []; - public function __construct($path, /*Level $level, */ - $regionX, $regionZ){ + public function __construct(LevelProvider $level, $regionX, $regionZ){ $this->x = $regionX; $this->z = $regionZ; - $this->filePath = /*$level->getPath()*/ - $path . "region/r.$regionX.$regionZ.mca"; + $this->levelProvider = $level; + $this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.mca"; touch($this->filePath); $this->filePointer = fopen($this->filePath, "r+b"); flock($this->filePointer, LOCK_EX); @@ -71,13 +74,17 @@ class RegionLoader{ } } + 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){ return false; } - if($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0){ + if(!$this->isChunkGenerated($index)){ if($generate === true){ //Allocate space $this->locationTable[$index][0] = ++$this->lastSector; @@ -116,8 +123,11 @@ class RegionLoader{ return false; } - return $chunk; - //$chunk = new Chunk($level, $chunk); + return new Chunk($this->levelProvider, $chunk); + } + + public function chunkExists($x, $z){ + return $this->isChunkGenerated(self::getChunkOffset($x, $z)); } public function generateChunk($x, $z){ @@ -131,6 +141,8 @@ class RegionLoader{ $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)); + //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", []); @@ -139,6 +151,10 @@ 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); $writer->setData(new Compound("", array($nbt))); $chunkData = $writer->writeCompressed(self::COMPRESSION_ZLIB, self::$COMPRESSION_LEVEL); @@ -154,6 +170,47 @@ class RegionLoader{ fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT)); } + 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()) + ]); + } + + $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); } diff --git a/src/pocketmine/level/format/generic/BaseChunk.php b/src/pocketmine/level/format/generic/BaseChunk.php index d1169e5bc..4e39884a7 100644 --- a/src/pocketmine/level/format/generic/BaseChunk.php +++ b/src/pocketmine/level/format/generic/BaseChunk.php @@ -21,27 +21,46 @@ namespace pocketmine\level\format\generic; +use pocketmine\entity\DroppedItem; +use pocketmine\entity\Entity; use pocketmine\level\format\Chunk; use pocketmine\level\format\ChunkSection; +use pocketmine\level\format\LevelProvider; use pocketmine\level\Level; +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; abstract class BaseChunk implements Chunk{ /** @var ChunkSection[] */ protected $sections = []; + + /** @var Entity[] */ + protected $entities = []; + + /** @var Tile[] */ + protected $tiles = []; + + /** @var \WeakRef */ protected $level; protected $x; protected $z; /** - * @param Level $level + * @param LevelProvider $level * @param int $x * @param int $z * @param ChunkSection[] $sections + * @param Compound[] $entities + * @param Compound[] $tiles */ - public function __construct(Level $level, $x, $z, array $sections){ - $this->level = $level; + protected function __construct(LevelProvider $level, $x, $z, array $sections, array $entities = [], array $tiles = []){ + $this->level = new \WeakRef($level); $this->x = (int) $x; $this->z = (int) $z; foreach($sections as $Y => $section){ @@ -59,6 +78,44 @@ abstract class BaseChunk implements Chunk{ return; } } + + 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 + + } + } + } + + + 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; + } + } + } } public function getX(){ @@ -69,8 +126,11 @@ abstract class BaseChunk implements Chunk{ return $this->z; } + /** + * @return LevelProvider + */ public function getLevel(){ - return $this->level; + return $this->level->valid() ? $this->level->get() : null; } public function getBlock($x, $y, $z, &$blockId, &$meta = null){ @@ -140,6 +200,59 @@ abstract class BaseChunk implements Chunk{ $this->sections[(int) $fY] = $section; } + 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; + } + 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(); + } + } + } + + /** + * @return ChunkSection[] + */ public function getSections(){ return $this->sections; } diff --git a/src/pocketmine/level/format/generic/BaseLevelProvider.php b/src/pocketmine/level/format/generic/BaseLevelProvider.php index 87245615a..cc9976fae 100644 --- a/src/pocketmine/level/format/generic/BaseLevelProvider.php +++ b/src/pocketmine/level/format/generic/BaseLevelProvider.php @@ -21,16 +21,83 @@ namespace pocketmine\level\format\generic; use pocketmine\level\format\LevelProvider; -use pocketmine\Server; +use pocketmine\level\Level; +use pocketmine\math\Vector3; +use pocketmine\nbt\NBT; +use pocketmine\nbt\tag\Compound; +use pocketmine\nbt\tag\Int; abstract class BaseLevelProvider implements LevelProvider{ - /** @var Server */ - protected $server; + /** @var Level */ + protected $level; /** @var string */ protected $path; + /** @var Compound */ + protected $levelData; - public function __construct(Server $server, $path){ - $this->server = $server; + public function __construct(Level $level, $path){ + $this->level = $level->getServer(); $this->path = $path; + @mkdir($this->path, 0777, true); + $nbt = new NBT(NBT::BIG_ENDIAN); + $nbt->readCompressed(file_get_contents($this->getPath() . "level.dat")); + $levelData = $nbt->getData(); + if($levelData->Data instanceof Compound){ + $this->levelData = $levelData->Data; + }else{ + throw new \Exception("Invalid level.dat"); + } } + + public function getPath(){ + return $this->path; + } + + public function getServer(){ + return $this->level->getServer(); + } + + public function getLevel(){ + return $this->level; + } + + public function getName(){ + return $this->levelData["LevelName"]; + } + + public function getTime(){ + return $this->levelData["Time"]; + } + + public function setTime($value){ + $this->levelData->Time = new Int("Time", (int) $value); + } + + public function getSpawn(){ + return new Vector3($this->levelData["SpawnX"], $this->levelData["SpawnY"], $this->levelData["SpawnZ"]); + } + + public function setSpawn(Vector3 $pos){ + $this->levelData->SpawnX = new Int("SpawnX", $pos->x); + $this->levelData->SpawnY = new Int("SpawnY", $pos->y); + $this->levelData->SpawnZ = new Int("SpawnZ", $pos->z); + } + + /** + * @return Compound + */ + public function getLevelData(){ + return $this->levelData; + } + + public function saveLevelData(){ + $nbt = new NBT(NBT::BIG_ENDIAN); + $nbt->setData(new Compound(null, [ + "Data" => $this->levelData + ])); + $buffer = $nbt->writeCompressed(); + @file_put_contents($this->getPath() . "level.dat", $buffer); + } + + } \ No newline at end of file diff --git a/src/pocketmine/level/generator/Flat.php b/src/pocketmine/level/generator/Flat.php index b9734b6b0..7c0b5f1dc 100644 --- a/src/pocketmine/level/generator/Flat.php +++ b/src/pocketmine/level/generator/Flat.php @@ -21,7 +21,7 @@ namespace pocketmine\level\generator; -use pocketmine\block\Air; +use pocketmine\level\generator\populator\Populator; use pocketmine\block\CoalOre; use pocketmine\block\DiamondOre; use pocketmine\block\Dirt; @@ -31,13 +31,22 @@ 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\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; class Flat extends Generator{ - private $level, $random, $structure, $chunks, $options, $floorLevel, $preset, $populators = []; + /** @var GenerationChunkManager */ + private $level; + /** @var SimpleChunk */ + private $chunk; + /** @var Random */ + private $random; + /** @var Populator[] */ + private $populators = []; + private $structure, $chunks, $options, $floorLevel, $preset; public function getSettings(){ return $this->options; @@ -75,7 +84,7 @@ class Flat extends Generator{ }*/ } - public function parsePreset($preset){ + protected function parsePreset($preset){ $this->preset = $preset; $preset = explode(";", $preset); $version = (int) $preset[0]; @@ -90,30 +99,33 @@ class Flat extends Generator{ $b = Item::fromString($b); $cnt = $matches[2][$i] === "" ? 1 : intval($matches[2][$i]); for($cY = $y, $y += $cnt; $cY < $y; ++$cY){ - $this->structure[$cY] = $b; + $this->structure[$cY] = [$b->getID(),$b->getDamage()]; } } $this->floorLevel = $y; for(; $y < 0xFF; ++$y){ - $this->structure[$y] = new Air(); + $this->structure[$y] = [0, 0]; } + $this->chunk = new SimpleChunk(null, null, SimpleChunk::FLAG_GENERATED); + for($Y = 0; $Y < 8; ++$Y){ $this->chunks[$Y] = ""; $startY = $Y << 4; $endY = $startY + 16; for($Z = 0; $Z < 16; ++$Z){ for($X = 0; $X < 16; ++$X){ - $blocks = ""; - $metas = ""; for($y = $startY; $y < $endY; ++$y){ - $blocks .= chr($this->structure[$y]->getID()); - $metas .= substr(dechex($this->structure[$y]->getDamage()), -1); + if($this->structure[$y][0] !== 0){ + $this->chunk->setBlockId($X, $y, $Z, $this->structure[$y][0]); + } + if($this->structure[$y][0] !== 0){ + $this->chunk->setBlockData($X, $y, $Z, $this->structure[$y][1]); + } } - $this->chunks[$Y] .= $blocks . hex2bin($metas) . "\x00\x00\x00\x00\x00\x00\x00\x00"; } } } @@ -135,15 +147,16 @@ class Flat extends Generator{ } } - public function init(Level $level, Random $random){ + public function init(GenerationChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; } public function generateChunk($chunkX, $chunkZ){ - for($Y = 0; $Y < 8; ++$Y){ - $this->level->setMiniChunk($chunkX, $chunkZ, $Y, $this->chunks[$Y]); - } + $chunk = clone $this->chunk; + $chunk->setX($chunkX); + $chunk->setZ($chunkZ); + $this->level->setChunk($chunkX, $chunkZ, $chunk); } public function populateChunk($chunkX, $chunkZ){ diff --git a/src/pocketmine/level/generator/GenerationChunkManager.php b/src/pocketmine/level/generator/GenerationChunkManager.php new file mode 100644 index 000000000..fc738f036 --- /dev/null +++ b/src/pocketmine/level/generator/GenerationChunkManager.php @@ -0,0 +1,187 @@ +levelID = $levelID; + $this->seed = $seed; + $this->manager = $manager; + + $this->generator = new $class($options); + $this->generator->init($this, new Random($seed)); + } + + /** + * @return int + */ + public function getSeed(){ + return $this->seed; + } + + /** + * @return int + */ + public function getID(){ + return $this->levelID; + } + + /** + * @param $chunkX + * @param $chunkZ + * + * @return SimpleChunk + */ + public function getChunk($chunkX, $chunkZ){ + $index = Level::chunkHash($chunkX, $chunkZ); + return !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index]; + } + + public function generateChunk($chunkX, $chunkZ){ + $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = new SimpleChunk($chunkX, $chunkZ, 0); + $this->generator->generateChunk($chunkX, $chunkZ); + } + + public function populateChunk($chunkX, $chunkZ){ + if(!$this->isChunkGenerated($chunkX, $chunkZ)){ + $this->generateChunk($chunkX, $chunkZ); + } + + for($z = $chunkZ - 1; $z <= $chunkZ + 1; ++$z){ + for($x = $chunkX - 1; $x <= $chunkX + 1; ++$x){ + if(!$this->isChunkGenerated($x, $z)){ + $this->generateChunk($x, $z); + } + } + } + + $this->generator->populateChunk($chunkX, $chunkZ); + } + + public function isChunkGenerated($chunkX, $chunkZ){ + return $this->getChunk($chunkX, $chunkZ)->isGenerated(); + } + + public function isChunkPopulated($chunkX, $chunkZ){ + return $this->getChunk($chunkX, $chunkZ)->isPopulated(); + } + + protected function requestChunk($chunkX, $chunkZ){ + $chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ); + $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; + return $chunk; + } + + /** + * @param int $chunkX + * @param int $chunkZ + * @param SimpleChunk $chunk + */ + public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){ + $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; + if($chunk->isGenerated() and $chunk->isPopulated()){ + //TODO: Queue to be sent + } + } + + /** + * Gets the raw block id. + * + * @param int $x + * @param int $y + * @param int $z + * + * @return int 0-255 + */ + public function getBlockIdAt($x, $y, $z){ + return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f); + } + + /** + * Sets the raw block id. + * + * @param int $x + * @param int $y + * @param int $z + * @param int $id 0-255 + */ + public function setBlockIdAt($x, $y, $z, $id){ + $this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff); + } + + /** + * Gets the raw block metadata + * + * @param int $x + * @param int $y + * @param int $z + * + * @return int 0-15 + */ + public function getBlockDataAt($x, $y, $z){ + return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f); + } + + /** + * Sets the raw block metadata. + * + * @param int $x + * @param int $y + * @param int $z + * @param int $data 0-15 + */ + public function setBlockDataAt($x, $y, $z, $data){ + $this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f); + } + + public function shutdown(){ + foreach($this->chunks as $chunk){ + //TODO: send generated chunks to be saved + } + } + + +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/GenerationManager.php b/src/pocketmine/level/generator/GenerationManager.php new file mode 100644 index 000000000..f7621ed77 --- /dev/null +++ b/src/pocketmine/level/generator/GenerationManager.php @@ -0,0 +1,257 @@ +Thread + * byte[] payload: + * string root namespace + * byte[] path + */ + const PACKET_ADD_NAMESPACE = 0x00; + + /* + * Direction: Both + * If Server->Thread, request chunk generation + * If Thread->Server, request chunk contents / loading + * byte[] payload: + * int32 levelID + * int32 chunkX + * int32 chunkZ + */ + const PACKET_REQUEST_CHUNK = 0x01; + + /* + * Direction: Both + * byte[] payload: + * int32 levelID + * int32 chunkX + * int32 chunkZ + * byte flags (1 generated, 2 populated) + * byte[] chunk (none if generated flag is not set) + */ + const PACKET_SEND_CHUNK = 0x02; + + /* + * Direction: Server->Thread + * byte[] payload: + * int32 levelID + * int32 seed + * string class that extends pocketmine\level\generator\Generator + * byte[] serialized options array + */ + const PACKET_OPEN_LEVEL = 0x03; + + /* + * Direction: Server->Thread + * byte[] payload: + * int32 levelID + */ + const PACKET_CLOSE_LEVEL = 0x04; + + /* + * Direction: Server->Thread + * no payload + */ + const PACKET_SHUTDOWN = 0xff; + + + + protected $socket; + /** @var \Logger */ + protected $logger; + /** @var \SplAutoLoader */ + protected $loader; + + /** @var GenerationChunkManager[] */ + protected $levels = []; + + /** @var \SplQueue */ + protected $requestQueue; + + protected $needsChunk = null; + + protected $shutdown = false; + + /** + * @param resource $socket + * @param \Logger $logger + * @param \SplAutoloader $loader + */ + public function __construct($socket, \Logger $logger, \SplAutoloader $loader){ + $this->socket = $socket; + $this->logger = $logger; + $this->loader = $loader; + $this->requestQueue = new \SplQueue(); + + while($this->shutdown !== true){ + if($this->requestQueue->count() > 0){ + $r = $this->requestQueue->dequeue(); + $levelID = $r[0]; + $chunkX = $r[1]; + $chunkZ = $r[2]; + $this->generateChunk($levelID, $chunkX, $chunkZ); + + } + $this->readPacket(); + } + } + + protected function openLevel($levelID, $seed, $class, array $options){ + if(!isset($this->levels[$levelID])){ + $this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options); + } + } + + protected function generateChunk($levelID, $chunkX, $chunkZ){ + if(isset($this->levels[$levelID])){ + $this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly + //TODO: wait for queue generation (to wait for extra chunk changes) + } + } + + protected function closeLevel($levelID){ + if(!isset($this->levels[$levelID])){ + $this->levels[$levelID]->shutdown(); + unset($this->levels[$levelID]); + } + } + + protected function enqueueChunk($levelID, $chunkX, $chunkZ){ + $this->requestQueue->enqueue([$levelID, $chunkX, $chunkZ]); + } + + protected function receiveChunk($levelID, SimpleChunk $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; + } + } + //TODO: set new received chunks + } + + /** + * @param $levelID + * @param $chunkX + * @param $chunkZ + * + * @return SimpleChunk + */ + public function requestChunk($levelID, $chunkX, $chunkZ){ + $this->needsChunk = [$levelID, $chunkX, $chunkZ]; + $binary = chr(self::PACKET_REQUEST_CHUNK . Binary::writeInt($levelID) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ)); + @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); + do{ + $this->readPacket(); + }while($this->shutdown !== true and !($this->needsChunk instanceof SimpleChunk)); + + $chunk = $this->needsChunk; + $this->needsChunk = null; + if($chunk instanceof SimpleChunk){ + return $chunk; + }else{ + return new SimpleChunk($chunkX, $chunkZ, 0); + } + } + + public function sendChunk($levelID, SimpleChunk $chunk){ + $binary = chr(self::PACKET_SEND_CHUNK . Binary::writeInt($levelID) . $chunk->toBinary()); + @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); + } + + protected function readPacket(){ + $len = socket_read($this->socket, 4); + if($len === false or $len === ""){ + usleep(5000); + return; + } + $packet = socket_read($this->socket, Binary::readInt($len)); + $pid = ord($packet{0}); + $offset = 1; + if($pid === self::PACKET_REQUEST_CHUNK){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $chunkX = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $chunkZ = Binary::readInt(substr($packet, $offset, 4)); + $this->enqueueChunk($levelID, $chunkX, $chunkZ); + + }elseif($pid === self::PACKET_SEND_CHUNK){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $chunk = SimpleChunk::fromBinary(substr($packet, $offset)); + $this->receiveChunk($levelID, $chunk); + + }elseif($pid === self::PACKET_OPEN_LEVEL){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $seed = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $len = Binary::readShort(substr($packet, $offset, 2)); + $offset += 2; + $class = substr($packet, $offset, $len); + $offset += $len; + $options = unserialize(substr($packet, $offset)); + $this->openLevel($levelID, $seed, $class, $options); + + }elseif($pid === self::PACKET_CLOSE_LEVEL){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $this->closeLevel($levelID); + }elseif($pid === self::PACKET_ADD_NAMESPACE){ + $len = Binary::readShort(substr($packet, $offset, 2)); + $offset += 2; + $namespace = substr($packet, $offset, $len); + $offset += $len; + $path = substr($packet, $offset); + $this->loader->add($namespace, [$path]); + }elseif($pid === self::PACKET_SHUTDOWN){ + foreach($this->levels as $level){ + $level->shutdown(); + } + $this->levels = []; + + $this->shutdown = true; + socket_close($this->socket); + } + } + + /** + * @return \Logger + */ + public function getLogger(){ + return $this->logger; + } + +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/GenerationThread.php b/src/pocketmine/level/generator/GenerationThread.php new file mode 100644 index 000000000..e20bdf93d --- /dev/null +++ b/src/pocketmine/level/generator/GenerationThread.php @@ -0,0 +1,96 @@ +externalSocket; + } + + public function getInternalSocket(){ + return $this->internalSocket; + } + + /** + * @return \ThreadedLogger + */ + public function getLogger(){ + return $this->logger; + } + + public function __construct(\ThreadedLogger $logger, \SplAutoloader $loader){ + $this->loader = $loader; + $loadPaths = []; + $this->addDependency($loadPaths, new \ReflectionClass($this->logger)); + $this->addDependency($loadPaths, new \ReflectionClass($this->loader)); + $this->loadPaths = array_reverse($loadPaths); + + $sockets = []; + if(!socket_create_pair((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? AF_INET : AF_UNIX), SOCK_STREAM, 0, $sockets)){ + throw new \Exception("Could not create IPC sockets. Reason: ".socket_strerror(socket_last_error())); + } + + $this->internalSocket = $sockets[0]; + socket_set_block($this->internalSocket); //IMPORTANT! + $this->externalSocket = $sockets[1]; + socket_set_nonblock($this->externalSocket); + + $this->start(PTHREADS_INHERIT_ALL & ~PTHREADS_INHERIT_CLASSES); + } + + protected function addDependency(array &$loadPaths, \ReflectionClass $dep){ + if($dep->getFileName() !== false){ + $loadPaths[$dep->getName()] = $dep->getFileName(); + } + + if($dep->getParentClass() instanceof \ReflectionClass){ + $this->addDependency($loadPaths, $dep->getParentClass()); + } + + foreach($dep->getInterfaces() as $interface){ + $this->addDependency($loadPaths, $interface); + } + } + + public function run(){ + //Load removed dependencies, can't use require_once() + foreach($this->loadPaths as $name => $path){ + if(!class_exists($name, false) and !class_exists($name, false)){ + require($path); + } + } + $this->loader->register(); + + $generationManager = new GenerationManager($this->getInternalSocket(), $this->getLogger(), $this->loader); + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/Generator.php b/src/pocketmine/level/generator/Generator.php index e4fb59b10..12cc70ee4 100644 --- a/src/pocketmine/level/generator/Generator.php +++ b/src/pocketmine/level/generator/Generator.php @@ -50,7 +50,7 @@ abstract class Generator{ public abstract function __construct(array $settings = []); - public abstract function init(Level $level, Random $random); + public abstract function init(GenerationChunkManager $level, Random $random); public abstract function generateChunk($chunkX, $chunkZ); diff --git a/src/pocketmine/level/generator/Normal.php b/src/pocketmine/level/generator/Normal.php index c77943760..c790c8937 100644 --- a/src/pocketmine/level/generator/Normal.php +++ b/src/pocketmine/level/generator/Normal.php @@ -37,11 +37,15 @@ use pocketmine\level\generator\populator\Tree; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; +use pocketmine\level\generator\populator\Populator; class Normal extends Generator{ + /** @var Populator[] */ private $populators = []; + /** @var GenerationChunkManager */ private $level; + /** @var Random */ private $random; private $worldHeight = 65; private $waterHeight = 63; @@ -62,7 +66,7 @@ class Normal extends Generator{ return []; } - public function init(Level $level, Random $random){ + public function init(GenerationChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; $this->random->setSeed($this->level->getSeed()); diff --git a/src/pocketmine/level/generator/object/BigTree.php b/src/pocketmine/level/generator/object/BigTree.php index b7fbad7ca..a96f069d7 100644 --- a/src/pocketmine/level/generator/object/BigTree.php +++ b/src/pocketmine/level/generator/object/BigTree.php @@ -21,6 +21,7 @@ namespace pocketmine\level\generator\object; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; @@ -39,11 +40,11 @@ class BigTree extends Tree{ private $addLogVines = false; private $addCocoaPlants = false; - public function canPlaceObject(Level $level, Vector3 $pos){ + public function canPlaceObject(ChunkManager $level, $x, $y, $z){ return false; } - public function placeObject(Level $level, Vector3 $pos, $type){ + public function placeObject(ChunkManager $level, $x, $y, $z, $type){ $this->trunkHeight = (int) ($this->totalHeight * $this->trunkHeightMultiplier); $leaves = $this->getLeafGroupPoints($level, $pos); diff --git a/src/pocketmine/level/generator/object/Ore.php b/src/pocketmine/level/generator/object/Ore.php index 6808e85f2..e55251131 100644 --- a/src/pocketmine/level/generator/object/Ore.php +++ b/src/pocketmine/level/generator/object/Ore.php @@ -21,6 +21,7 @@ namespace pocketmine\level\generator\object; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\math\VectorMath; @@ -39,20 +40,20 @@ class Ore{ return $this->type; } - public function canPlaceObject(Level $level, $x, $y, $z){ - return ($level->level->getBlockID($x, $y, $z) === 1); + public function canPlaceObject(ChunkManager $level, $x, $y, $z){ + return ($level->getBlockIdAt($x, $y, $z) === 1); } - public function placeObject(Level $level, Vector3 $pos){ + public function placeObject(ChunkManager $level, $x, $y, $z){ $clusterSize = (int) $this->type->clusterSize; $angle = $this->random->nextFloat() * M_PI; $offset = VectorMath::getDirection2D($angle)->multiply($clusterSize)->divide(8); - $x1 = $pos->x + 8 + $offset->x; - $x2 = $pos->x + 8 - $offset->x; - $z1 = $pos->z + 8 + $offset->y; - $z2 = $pos->z + 8 - $offset->y; - $y1 = $pos->y + $this->random->nextRange(0, 3) + 2; - $y2 = $pos->y + $this->random->nextRange(0, 3) + 2; + $x1 = $x + 8 + $offset->x; + $x2 = $x + 8 - $offset->x; + $z1 = $z + 8 + $offset->y; + $z2 = $z + 8 - $offset->y; + $y1 = $y + $this->random->nextRange(0, 3) + 2; + $y2 = $y + $this->random->nextRange(0, 3) + 2; for($count = 0; $count <= $clusterSize; ++$count){ $seedX = $x1 + ($x2 - $x1) * $count / $clusterSize; $seedY = $y1 + ($y2 - $y1) * $count / $clusterSize; @@ -80,8 +81,8 @@ class Ore{ $sizeZ = ($z + 0.5 - $seedZ) / $size; $sizeZ *= $sizeZ; - if(($sizeX + $sizeY + $sizeZ) < 1 and $level->level->getBlockID($x, $y, $z) === 1){ - $level->setBlockRaw(new Vector3($x, $y, $z), $this->type->material); + if(($sizeX + $sizeY + $sizeZ) < 1 and $level->getBlockIdAt($x, $y, $z) === 1){ + $level->setBlockIdAt($x, $y, $z, $this->type->material); } } } diff --git a/src/pocketmine/level/generator/object/PineTree.php b/src/pocketmine/level/generator/object/PineTree.php index e85f9823f..9c69efb95 100644 --- a/src/pocketmine/level/generator/object/PineTree.php +++ b/src/pocketmine/level/generator/object/PineTree.php @@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object; use pocketmine\block\Dirt; use pocketmine\block\Leaves; use pocketmine\block\Wood; +use pocketmine\item\Block; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; @@ -34,7 +36,7 @@ class PineTree extends Tree{ private $leavesSizeY = -1; private $leavesAbsoluteMaxRadius = -1; - public function canPlaceObject(Level $level, Vector3 $pos, Random $random){ + public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){ $this->findRandomLeavesSize($random); $checkRadius = 0; for($yy = 0; $yy < $this->totalHeight; ++$yy){ @@ -43,7 +45,7 @@ class PineTree extends Tree{ } for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){ for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){ - if(!isset($this->overridable[$level->level->getBlockID($pos->x + $xx, $pos->y + $yy, $pos->z + $zz)])){ + if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){ return false; } } @@ -59,11 +61,11 @@ class PineTree extends Tree{ $this->leavesAbsoluteMaxRadius = 2 + $random->nextRange(0, 1); } - public function placeObject(Level $level, Vector3 $pos, Random $random){ + public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ if($this->leavesSizeY === -1 or $this->leavesAbsoluteMaxRadius === -1){ $this->findRandomLeavesSize($random); } - $level->setBlockRaw(new Vector3($pos->x, $pos->y - 1, $pos->z), new Dirt()); + $level->setBlockIdAt($x, $y - 1, $z, Block::DIRT); $leavesRadius = 0; $leavesMaxRadius = 1; $leavesBottomY = $this->totalHeight - $this->leavesSizeY; @@ -73,7 +75,8 @@ class PineTree extends Tree{ for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){ for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){ if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){ - $level->setBlockRaw(new Vector3($pos->x + $xx, $pos->y + $yy, $pos->z + $zz), new Leaves($this->type)); + $level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES); + $level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type); } } } @@ -89,7 +92,8 @@ class PineTree extends Tree{ } $trunkHeightReducer = $random->nextRange(0, 3); for($yy = 0; $yy < ($this->totalHeight - $trunkHeightReducer); ++$yy){ - $level->setBlockRaw(new Vector3($pos->x, $pos->y + $yy, $pos->z), new Wood($this->type)); + $level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK); + $level->setBlockDataAt($x, $y + $yy, $z, $this->type); } } diff --git a/src/pocketmine/level/generator/object/Pond.php b/src/pocketmine/level/generator/object/Pond.php index d4697c221..0d027ba73 100644 --- a/src/pocketmine/level/generator/object/Pond.php +++ b/src/pocketmine/level/generator/object/Pond.php @@ -22,6 +22,7 @@ namespace pocketmine\level\generator\object; use pocketmine\block\Block; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; @@ -35,10 +36,10 @@ class Pond{ $this->random = $random; } - public function canPlaceObject(Level $level, Vector3 $pos){ + public function canPlaceObject(ChunkManager $level, Vector3 $pos){ } - public function placeObject(Level $level, Vector3 $pos){ + public function placeObject(ChunkManager $level, Vector3 $pos){ } } \ No newline at end of file diff --git a/src/pocketmine/level/generator/object/SmallTree.php b/src/pocketmine/level/generator/object/SmallTree.php index 911077b5f..34b0d4d96 100644 --- a/src/pocketmine/level/generator/object/SmallTree.php +++ b/src/pocketmine/level/generator/object/SmallTree.php @@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object; use pocketmine\block\Dirt; use pocketmine\block\Leaves; use pocketmine\block\Wood; +use pocketmine\item\Block; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; @@ -38,7 +40,7 @@ class SmallTree extends Tree{ private $addLogVines = false; private $addCocoaPlants = false; - public function canPlaceObject(Level $level, Vector3 $pos, Random $random){ + public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){ $radiusToCheck = 0; for($yy = 0; $yy < $this->trunkHeight + 3; ++$yy){ if($yy == 1 or $yy === $this->trunkHeight){ @@ -46,7 +48,7 @@ class SmallTree extends Tree{ } for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){ for($zz = -$radiusToCheck; $zz < ($radiusToCheck + 1); ++$zz){ - if(!isset($this->overridable[$level->level->getBlockID($pos->x + $xx, $pos->y + $yy, $pos->z + $zz)])){ + if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){ return false; } } @@ -56,10 +58,9 @@ class SmallTree extends Tree{ return true; } - public function placeObject(Level $level, Vector3 $pos, Random $random){ + public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ // The base dirt block - $dirtpos = new Vector3($pos->x, $pos->y - 1, $pos->z); - $level->setBlockRaw($dirtpos, new Dirt()); + $level->setBlockIdAt($x, $y, $z, Block::DIRT); // Adjust the tree trunk's height randomly // plot [-14:11] int( x / 8 ) + 5 @@ -84,10 +85,8 @@ class SmallTree extends Tree{ for($xx = -$bRadius; $xx <= $bRadius; ++$xx){ for($zz = -$bRadius; $zz <= $bRadius; ++$zz){ if(sqrt(($xx * $xx) + ($zz * $zz)) <= $radius){ - $leafpos = new Vector3($pos->x + $xx, - $pos->y + $yy, - $pos->z + $zz); - $level->setBlockRaw($leafpos, new Leaves($this->type)); + $level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES); + $level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type); } } } @@ -96,8 +95,8 @@ class SmallTree extends Tree{ // Place the trunk last if($leaflevel > 1){ - $trunkpos = new Vector3($pos->x, $pos->y + $yy, $pos->z); - $level->setBlockRaw($trunkpos, new Wood($this->type)); + $level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK); + $level->setBlockDataAt($x, $y + $yy, $z, $this->type); } } } diff --git a/src/pocketmine/level/generator/object/SpruceTree.php b/src/pocketmine/level/generator/object/SpruceTree.php index 803be97c8..a54e60c24 100644 --- a/src/pocketmine/level/generator/object/SpruceTree.php +++ b/src/pocketmine/level/generator/object/SpruceTree.php @@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object; use pocketmine\block\Dirt; use pocketmine\block\Leaves; use pocketmine\block\Wood; +use pocketmine\item\Block; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; @@ -34,7 +36,7 @@ class SpruceTree extends Tree{ private $leavesBottomY = -1; private $leavesMaxRadius = -1; - public function canPlaceObject(Level $level, Vector3 $pos, Random $random){ + public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){ $this->findRandomLeavesSize($random); $checkRadius = 0; for($yy = 0; $yy < $this->totalHeight + 2; ++$yy){ @@ -43,7 +45,7 @@ class SpruceTree extends Tree{ } for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){ for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){ - if(!isset($this->overridable[$level->level->getBlockID($pos->x + $xx, $pos->y + $yy, $pos->z + $zz)])){ + if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){ return false; } } @@ -59,28 +61,30 @@ class SpruceTree extends Tree{ $this->leavesMaxRadius = 1 + $random->nextRange(0, 1); } - public function placeObject(Level $level, Vector3 $pos, Random $random){ + public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ if($this->leavesBottomY === -1 or $this->leavesMaxRadius === -1){ $this->findRandomLeavesSize($random); } - $level->setBlockRaw(new Vector3($pos->x, $pos->y - 1, $pos->z), new Dirt()); + $level->setBlockIdAt($x, $y - 1, $z, Block::DIRT); $leavesRadius = 0; for($yy = $this->totalHeight; $yy >= $this->leavesBottomY; --$yy){ for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){ for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){ if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){ - $level->setBlockRaw(new Vector3($pos->x + $xx, $pos->y + $yy, $pos->z + $zz), new Leaves($this->type)); + $level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES); + $level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type); } } } - if($leavesRadius > 0 and $yy === ($pos->y + $this->leavesBottomY + 1)){ + if($leavesRadius > 0 and $yy === ($y + $this->leavesBottomY + 1)){ --$leavesRadius; }elseif($leavesRadius < $this->leavesMaxRadius){ ++$leavesRadius; } } for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){ - $level->setBlockRaw(new Vector3($pos->x, $pos->y + $yy, $pos->z), new Wood($this->type)); + $level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK); + $level->setBlockDataAt($x, $y + $yy, $z, $this->type); } } diff --git a/src/pocketmine/level/generator/object/TallGrass.php b/src/pocketmine/level/generator/object/TallGrass.php index a6ac29f8a..aaa0a6ac5 100644 --- a/src/pocketmine/level/generator/object/TallGrass.php +++ b/src/pocketmine/level/generator/object/TallGrass.php @@ -22,27 +22,29 @@ namespace pocketmine\level\generator\object; use pocketmine\block\Block; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; class TallGrass{ - public static function growGrass(Level $level, Vector3 $pos, Random $random, $count = 15, $radius = 10){ - $arr = array( - Block::get(Block::DANDELION, 0), - Block::get(Block::CYAN_FLOWER, 0), - Block::get(Block::TALL_GRASS, 1), - Block::get(Block::TALL_GRASS, 1), - Block::get(Block::TALL_GRASS, 1), - Block::get(Block::TALL_GRASS, 1) - ); + public static function growGrass(ChunkManager $level, Vector3 $pos, Random $random, $count = 15, $radius = 10){ + $arr = [ + [Block::DANDELION, 0], + [Block::CYAN_FLOWER, 0], + [Block::TALL_GRASS, 1], + [Block::TALL_GRASS, 1], + [Block::TALL_GRASS, 1], + [Block::TALL_GRASS, 1] + ]; $arrC = count($arr) - 1; for($c = 0; $c < $count; ++$c){ $x = $random->nextRange($pos->x - $radius, $pos->x + $radius); $z = $random->nextRange($pos->z - $radius, $pos->z + $radius); - if($level->level->getBlockID($x, $pos->y + 1, $z) === Block::AIR and $level->level->getBlockID($x, $pos->y, $z) === Block::GRASS){ + if($level->getBlockIdAt($x, $pos->y + 1, $z) === Block::AIR and $level->getBlockIdAt($x, $pos->y, $z) === Block::GRASS){ $t = $arr[$random->nextRange(0, $arrC)]; - $level->setBlockRaw(new Vector3($x, $pos->y + 1, $z), $t); + $level->setBlockIdAt($x, $pos->y + 1, $z, $t[0]); + $level->setBlockDataAt($x, $pos->y + 1, $z, $t[1]); } } } diff --git a/src/pocketmine/level/generator/object/Tree.php b/src/pocketmine/level/generator/object/Tree.php index f9349c582..26c397aa0 100644 --- a/src/pocketmine/level/generator/object/Tree.php +++ b/src/pocketmine/level/generator/object/Tree.php @@ -22,6 +22,7 @@ namespace pocketmine\level\generator\object; use pocketmine\block\Sapling; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; @@ -36,7 +37,7 @@ class Tree{ 18 => true, ); - public static function growTree(Level $level, Vector3 $pos, Random $random, $type = 0){ + public static function growTree(ChunkManager $level, $x, $y, $z, Random $random, $type = 0){ switch($type & 0x03){ case Sapling::SPRUCE: if($random->nextRange(0, 1) === 1){ @@ -62,8 +63,8 @@ class Tree{ //} break; } - if($tree->canPlaceObject($level, $pos, $random)){ - $tree->placeObject($level, $pos, $random); + if($tree->canPlaceObject($level, $x, $y, $z, $random)){ + $tree->placeObject($level, $x, $y, $z, $random); } } } \ No newline at end of file diff --git a/src/pocketmine/level/generator/populator/Mineshaft.php b/src/pocketmine/level/generator/populator/Mineshaft.php index 6acebb8c2..0f2567ddb 100644 --- a/src/pocketmine/level/generator/populator/Mineshaft.php +++ b/src/pocketmine/level/generator/populator/Mineshaft.php @@ -21,6 +21,7 @@ namespace pocketmine\level\generator\populator; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\utils\Random; @@ -31,7 +32,7 @@ class Mineshaft extends Populator{ private static $BASE_Y = 35; private static $RAND_Y = 11; - public function populate(Level $level, $chunkX, $chunkZ, Random $random){ + public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){ if($random->nextRange(0, self::$ODD) === 0){ //$mineshaft = new Mineshaft($random); } diff --git a/src/pocketmine/level/generator/populator/Ore.php b/src/pocketmine/level/generator/populator/Ore.php index 5959f2f0c..a330bc4b2 100644 --- a/src/pocketmine/level/generator/populator/Ore.php +++ b/src/pocketmine/level/generator/populator/Ore.php @@ -21,6 +21,7 @@ namespace pocketmine\level\generator\populator; +use pocketmine\level\ChunkManager; use pocketmine\level\generator\object\Ore as ObjectOre; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; @@ -29,7 +30,7 @@ use pocketmine\utils\Random; class Ore extends Populator{ private $oreTypes = []; - public function populate(Level $level, $chunkX, $chunkZ, Random $random){ + public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){ foreach($this->oreTypes as $type){ $ore = new ObjectOre($random, $type); for($i = 0; $i < $ore->type->clusterCount; ++$i){ @@ -37,7 +38,7 @@ class Ore extends Populator{ $y = $random->nextRange($ore->type->minHeight, $ore->type->maxHeight); $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15); if($ore->canPlaceObject($level, $x, $y, $z)){ - $ore->placeObject($level, new Vector3($x, $y, $z)); + $ore->placeObject($level, $x, $y, $z); } } } diff --git a/src/pocketmine/level/generator/populator/Pond.php b/src/pocketmine/level/generator/populator/Pond.php index dbc63a22c..b7c7cb1e7 100644 --- a/src/pocketmine/level/generator/populator/Pond.php +++ b/src/pocketmine/level/generator/populator/Pond.php @@ -22,6 +22,7 @@ namespace pocketmine\level\generator\populator; use pocketmine\block\Water; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; @@ -31,16 +32,14 @@ class Pond extends Populator{ private $lavaOdd = 4; private $lavaSurfaceOdd = 4; - public function populate(Level $level, $chunkX, $chunkZ, Random $random){ + public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){ if($random->nextRange(0, $this->waterOdd) === 0){ - $v = new Vector3( - $random->nextRange($chunkX << 4, ($chunkX << 4) + 16), - $random->nextRange(0, 128), - $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 16) - ); + $x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 16); + $y = $random->nextRange(0, 128); + $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 16); $pond = new \pocketmine\level\generator\object\Pond($random, new Water()); - if($pond->canPlaceObject($level, $v)){ - $pond->placeObject($level, $v); + if($pond->canPlaceObject($level, $x, $y, $z)){ + $pond->placeObject($level, $x, $y, $z); } } } diff --git a/src/pocketmine/level/generator/populator/Populator.php b/src/pocketmine/level/generator/populator/Populator.php index fa44c8e63..0d58e40e5 100644 --- a/src/pocketmine/level/generator/populator/Populator.php +++ b/src/pocketmine/level/generator/populator/Populator.php @@ -24,9 +24,10 @@ */ namespace pocketmine\level\generator\populator; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\utils\Random; abstract class Populator{ - public abstract function populate(Level $level, $chunkX, $chunkZ, Random $random); + public abstract function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random); } \ No newline at end of file diff --git a/src/pocketmine/level/generator/populator/TallGrass.php b/src/pocketmine/level/generator/populator/TallGrass.php index 17b6b3254..a327020c5 100644 --- a/src/pocketmine/level/generator/populator/TallGrass.php +++ b/src/pocketmine/level/generator/populator/TallGrass.php @@ -23,12 +23,13 @@ namespace pocketmine\level\generator\populator; use pocketmine\block\Block; use pocketmine\block\TallGrass as BlockTallGrass; +use pocketmine\level\ChunkManager; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; class TallGrass extends Populator{ - /** @var Level */ + /** @var ChunkManager */ private $level; private $randomAmount; private $baseAmount; @@ -41,7 +42,7 @@ class TallGrass extends Populator{ $this->baseAmount = $amount; } - public function populate(Level $level, $chunkX, $chunkZ, Random $random){ + public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){ $this->level = $level; $amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount; for($i = 0; $i < $amount; ++$i){ @@ -51,22 +52,23 @@ class TallGrass extends Populator{ $xx = $x - 7 + $random->nextRange(0, 15); $zz = $z - 7 + $random->nextRange(0, 15); $yy = $this->getHighestWorkableBlock($xx, $zz); - $vector = new Vector3($xx, $yy, $zz); - if($yy !== -1 and $this->canTallGrassStay($this->level->getBlockRaw($vector))){ - $this->level->setBlockRaw($vector, new BlockTallGrass(1)); + + if($yy !== -1 and $this->canTallGrassStay($xx, $yy, $zz)){ + $this->level->setBlockIdAt($xx, $yy, $zz, Block::TALL_GRASS); + $this->level->setBlockDataAt($xx, $yy, $zz, 1); } } } } - private function canTallGrassStay(Block $block){ - return $block->getID() === Block::AIR and $block->getSide(0)->getID() === Block::GRASS; + private function canTallGrassStay($x, $y, $z){ + return $this->level->getBlockIdAt($x, $y, $z) === Block::AIR and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::GRASS; } private function getHighestWorkableBlock($x, $z){ for($y = 128; $y > 0; --$y){ - $b = $this->level->getBlockRaw(new Vector3($x, $y, $z)); - if($b->getID() === Block::AIR or $b->getID() === Block::LEAVES){ + $b = $this->level->getBlockIdAt($x, $y, $z); + if($b === Block::AIR or $b === Block::LEAVES){ if(--$y <= 0){ return -1; } diff --git a/src/pocketmine/level/generator/populator/Tree.php b/src/pocketmine/level/generator/populator/Tree.php index 6369faf38..70a5923f7 100644 --- a/src/pocketmine/level/generator/populator/Tree.php +++ b/src/pocketmine/level/generator/populator/Tree.php @@ -23,12 +23,14 @@ namespace pocketmine\level\generator\populator; use pocketmine\block\Block; use pocketmine\block\Sapling; +use pocketmine\level\ChunkManager; use pocketmine\level\generator\object\Tree as ObjectTree; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; class Tree extends Populator{ + /** @var ChunkManager */ private $level; private $randomAmount; private $baseAmount; @@ -41,7 +43,7 @@ class Tree extends Populator{ $this->baseAmount = $amount; } - public function populate(Level $level, $chunkX, $chunkZ, Random $random){ + public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){ $this->level = $level; $amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount; for($i = 0; $i < $amount; ++$i){ @@ -56,14 +58,14 @@ class Tree extends Populator{ }else{ $meta = Sapling::OAK; } - ObjectTree::growTree($this->level, new Vector3($x, $y, $z), $random, $meta); + ObjectTree::growTree($this->level, $x, $y, $z, $random, $meta); } } private function getHighestWorkableBlock($x, $z){ for($y = 128; $y > 0; --$y){ - $b = $this->level->getBlockRaw(new Vector3($x, $y, $z)); - if($b->getID() !== Block::DIRT and $b->getID() !== Block::GRASS){ + $b = $this->level->getBlockIdAt($x, $y, $z); + if($b !== Block::DIRT and $b !== Block::GRASS){ if(--$y <= 0){ return -1; } diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index 37dcbcb56..b04c7d9ca 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -25,6 +25,7 @@ use pocketmine\inventory\ChestInventory; use pocketmine\inventory\DoubleChestInventory; use pocketmine\inventory\InventoryHolder; use pocketmine\item\Item; +use pocketmine\level\format\Chunk; use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\nbt\NBT; @@ -44,9 +45,9 @@ class Chest extends Spawnable implements InventoryHolder, Container{ /** @var DoubleChestInventory */ protected $doubleInventory = null; - public function __construct(Level $level, Compound $nbt){ + public function __construct(Chunk $chunk, Compound $nbt){ $nbt["id"] = Tile::CHEST; - parent::__construct($level, $nbt); + parent::__construct($chunk, $nbt); $this->inventory = new ChestInventory($this); for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); diff --git a/src/pocketmine/tile/Container.php b/src/pocketmine/tile/Container.php index 75270751e..1b5c4909f 100644 --- a/src/pocketmine/tile/Container.php +++ b/src/pocketmine/tile/Container.php @@ -26,9 +26,22 @@ use pocketmine\item\Item; use pocketmine\Network; interface Container{ + + /** + * @param int $index + * + * @return Item + */ public function getItem($index); + /** + * @param int $index + * @param Item $item + */ public function setItem($index, Item $item); + /** + * @return int + */ public function getSize(); } diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index 314df9047..0a60f710e 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -26,6 +26,7 @@ use pocketmine\inventory\FurnaceInventory; use pocketmine\inventory\FurnaceRecipe; use pocketmine\inventory\InventoryHolder; use pocketmine\item\Item; +use pocketmine\level\format\Chunk; use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Byte; @@ -37,9 +38,9 @@ class Furnace extends Tile implements InventoryHolder, Container{ /** @var FurnaceInventory */ protected $inventory; - public function __construct(Level $level, Compound $nbt){ + public function __construct(Chunk $chunk, Compound $nbt){ $nbt["id"] = Tile::FURNACE; - parent::__construct($level, $nbt); + parent::__construct($chunk, $nbt); $this->inventory = new FurnaceInventory($this); for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); diff --git a/src/pocketmine/tile/Sign.php b/src/pocketmine/tile/Sign.php index 76b5fe0ec..aad1924e7 100644 --- a/src/pocketmine/tile/Sign.php +++ b/src/pocketmine/tile/Sign.php @@ -21,6 +21,7 @@ namespace pocketmine\tile; +use pocketmine\level\format\Chunk; use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Compound; @@ -31,9 +32,9 @@ use pocketmine\Player; class Sign extends Spawnable{ - public function __construct(Level $level, Compound $nbt){ + public function __construct(Chunk $chunk, Compound $nbt){ $nbt["id"] = Tile::SIGN; - parent::__construct($level, $nbt); + parent::__construct($chunk, $nbt); } public function setText($line1 = "", $line2 = "", $line3 = "", $line4 = ""){ @@ -42,7 +43,6 @@ class Sign extends Spawnable{ $this->namedtag->Text3 = $line3; $this->namedtag->Text4 = $line4; $this->spawnToAll(); - $this->server->handle("tile.update", $this); return true; } diff --git a/src/pocketmine/tile/Spawnable.php b/src/pocketmine/tile/Spawnable.php index 4c6fa11cc..2a6bf2cd5 100644 --- a/src/pocketmine/tile/Spawnable.php +++ b/src/pocketmine/tile/Spawnable.php @@ -21,6 +21,7 @@ namespace pocketmine\tile; +use pocketmine\level\format\Chunk; use pocketmine\level\Level; use pocketmine\nbt\tag\Compound; use pocketmine\Player; @@ -28,8 +29,8 @@ use pocketmine\Player; abstract class Spawnable extends Tile{ public abstract function spawnTo(Player $player); - public function __construct(Level $level, Compound $nbt){ - parent::__construct($level, $nbt); + public function __construct(Chunk $chunk, Compound $nbt){ + parent::__construct($chunk, $nbt); $this->spawnToAll(); } diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index eb453c7d1..c56354fd4 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -25,11 +25,10 @@ */ namespace pocketmine\tile; -use pocketmine\level\format\pmf\LevelFormat; +use pocketmine\level\format\Chunk; use pocketmine\level\Level; use pocketmine\level\Position; use pocketmine\nbt\tag\Compound; -use pocketmine\Server; abstract class Tile extends Position{ const SIGN = "Sign"; @@ -45,7 +44,8 @@ abstract class Tile extends Position{ */ public static $needUpdate = []; - public $chunkIndex; + /** @var Chunk */ + public $chunk; public $name; public $id; public $x; @@ -63,9 +63,10 @@ abstract class Tile extends Position{ } - public function __construct(Level $level, Compound $nbt){ - $this->server = Server::getInstance(); - $this->setLevel($level, true); //Strong reference + public function __construct(Chunk $chunk, Compound $nbt){ + $this->server = $chunk->getLevel()->getLevel()->getServer(); + $this->chunk = $chunk; + $this->setLevel($chunk->getLevel()->getLevel(), true); //Strong reference $this->namedtag = $nbt; $this->closed = false; $this->name = ""; @@ -75,10 +76,8 @@ abstract class Tile extends Position{ $this->y = (int) $this->namedtag["y"]; $this->z = (int) $this->namedtag["z"]; - $index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4); - $this->chunkIndex = $index; + $this->chunk->addTile($this); $this->getLevel()->addTile($this); - $this->getLevel()->chunkTiles[$this->chunkIndex][$this->id] = $this; } public function saveNBT(){ @@ -100,7 +99,7 @@ abstract class Tile extends Position{ $this->closed = true; unset(Tile::$needUpdate[$this->id]); $this->getLevel()->removeTile($this); - unset($this->getLevel()->chunkTiles[$this->chunkIndex][$this->id]); + $this->chunk->removeTile($this); } } diff --git a/src/raklib b/src/raklib index 7f6711a75..345009779 160000 --- a/src/raklib +++ b/src/raklib @@ -1 +1 @@ -Subproject commit 7f6711a75ef0e64537cdeb302013386dd2b99c5f +Subproject commit 345009779b039fa9c0238fd0a57df5eeff2a2753