mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-15 18:29:46 +00:00
Updated Levels :D
This commit is contained in:
parent
920e2a7c7e
commit
115b4cf4ac
@ -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));
|
||||
}
|
||||
|
66
src/pocketmine/level/ChunkManager.php
Normal file
66
src/pocketmine/level/ChunkManager.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?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;
|
||||
|
||||
interface ChunkManager{
|
||||
/**
|
||||
* Gets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockIdAt($x, $y, $z);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Gets the raw block metadata
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockDataAt($x, $y, $z);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
@ -24,11 +24,8 @@
|
||||
*/
|
||||
namespace pocketmine\level;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Human;
|
||||
use pocketmine\event\level\SpawnChangeEvent;
|
||||
use pocketmine\level\format\BaseLevelProvider;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\level\generator\Generator;
|
||||
@ -42,7 +39,7 @@ use pocketmine\tile\Tile;
|
||||
use pocketmine\level\format\Chunk;
|
||||
|
||||
|
||||
class Level implements Metadatable{
|
||||
class Level implements ChunkManager, Metadatable{
|
||||
|
||||
private static $levelIdCounter = 1;
|
||||
|
||||
@ -80,7 +77,6 @@ class Level implements Metadatable{
|
||||
|
||||
/**
|
||||
* Returns the chunk unique hash/key
|
||||
* TODO: return integer values (port from PMF)
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
@ -95,17 +91,31 @@ class Level implements Metadatable{
|
||||
* Init the default level data
|
||||
*
|
||||
* @param Server $server
|
||||
* @param LevelProvider $provider
|
||||
* @param string $path
|
||||
* @param string $provider Class that extends LevelProvider
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Server $server, LevelProvider $provider){
|
||||
public function __construct(Server $server, $path, $provider){
|
||||
$this->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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
}
|
242
src/pocketmine/level/format/SimpleChunk.php
Normal file
242
src/pocketmine/level/format/SimpleChunk.php
Normal file
@ -0,0 +1,242 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* @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]) ? $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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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});
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<LevelProvider> */
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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){
|
||||
|
187
src/pocketmine/level/generator/GenerationChunkManager.php
Normal file
187
src/pocketmine/level/generator/GenerationChunkManager.php
Normal file
@ -0,0 +1,187 @@
|
||||
<?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\generator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class GenerationChunkManager implements ChunkManager{
|
||||
|
||||
protected $levelID;
|
||||
|
||||
/** @var SimpleChunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
/** @var Generator */
|
||||
protected $generator;
|
||||
|
||||
/** @var GenerationManager */
|
||||
protected $manager;
|
||||
|
||||
protected $seed;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
$this->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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
257
src/pocketmine/level/generator/GenerationManager.php
Normal file
257
src/pocketmine/level/generator/GenerationManager.php
Normal file
@ -0,0 +1,257 @@
|
||||
<?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\generator;
|
||||
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class GenerationManager{
|
||||
|
||||
|
||||
/*
|
||||
* IPC protocol:
|
||||
* int32 (total length)
|
||||
* byte (packet id)
|
||||
* byte[] (length - 1 bytes)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Direction: Server->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;
|
||||
}
|
||||
|
||||
}
|
96
src/pocketmine/level/generator/GenerationThread.php
Normal file
96
src/pocketmine/level/generator/GenerationThread.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?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\generator;
|
||||
|
||||
|
||||
class GenerationThread extends \Thread{
|
||||
|
||||
protected $loadPaths;
|
||||
/** @var \SplAutoloader */
|
||||
protected $loader;
|
||||
/** @var \ThreadedLogger */
|
||||
protected $logger;
|
||||
|
||||
protected $externalSocket;
|
||||
protected $internalSocket;
|
||||
|
||||
public function getExternalSocket(){
|
||||
return $this->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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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){
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 7f6711a75ef0e64537cdeb302013386dd2b99c5f
|
||||
Subproject commit 345009779b039fa9c0238fd0a57df5eeff2a2753
|
Loading…
x
Reference in New Issue
Block a user