Updated Levels :D

This commit is contained in:
Shoghi Cervantes 2014-06-09 11:35:52 +02:00
parent 920e2a7c7e
commit 115b4cf4ac
41 changed files with 1492 additions and 270 deletions

View File

@ -31,6 +31,7 @@ use pocketmine\event\entity\EntityMotionEvent;
use pocketmine\event\entity\EntityMoveEvent; use pocketmine\event\entity\EntityMoveEvent;
use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntitySpawnEvent;
use pocketmine\event\entity\EntityTeleportEvent; use pocketmine\event\entity\EntityTeleportEvent;
use pocketmine\level\format\Chunk;
use pocketmine\level\format\pmf\LevelFormat; use pocketmine\level\format\pmf\LevelFormat;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\level\Position; use pocketmine\level\Position;
@ -75,11 +76,8 @@ abstract class Entity extends Position implements Metadatable{
/** @var int */ /** @var int */
public $chunkZ; public $chunkZ;
/** /** @var Chunk */
* TODO: REMOVE public $chunk;
* @var int
*/
public $chunkIndex;
public $lastX; public $lastX;
public $lastY; public $lastY;
@ -141,12 +139,13 @@ abstract class Entity extends Position implements Metadatable{
public $closed = false; public $closed = false;
public function __construct(Level $level, Compound $nbt){ public function __construct(Chunk $chunk, Compound $nbt){
$this->id = Entity::$entityCount++; $this->id = Entity::$entityCount++;
$this->justCreated = true; $this->justCreated = true;
$this->namedtag = $nbt; $this->namedtag = $nbt;
$this->setLevel($level, true); //Create a hard reference $this->chunk = $chunk;
$this->server = Server::getInstance(); $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->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
$this->setPositionAndRotation(new Vector3( $this->setPositionAndRotation(new Vector3(
@ -187,11 +186,9 @@ abstract class Entity extends Position implements Metadatable{
} }
$this->invulnerable = $this->namedtag["Invulnerable"] > 0 ? true : false; $this->invulnerable = $this->namedtag["Invulnerable"] > 0 ? true : false;
$index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4); $this->chunk->addEntity($this);
$this->chunkIndex = $index;
$this->getLevel()->addEntity($this); $this->getLevel()->addEntity($this);
$this->initEntity(); $this->initEntity();
$this->getLevel()->chunkEntities[$this->chunkIndex][$this->id] = $this;
$this->lastUpdate = $this->spawnTime = microtime(true); $this->lastUpdate = $this->spawnTime = microtime(true);
$this->justCreated = false; $this->justCreated = false;
$this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this)); $this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this));
@ -228,7 +225,7 @@ abstract class Entity extends Position implements Metadatable{
* @param Player $player * @param Player $player
*/ */
public function spawnTo(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; $this->hasSpawned[$player->getID()] = $player;
} }
} }
@ -585,7 +582,7 @@ abstract class Entity extends Position implements Metadatable{
} }
$this->getLevel()->removeEntity($this); $this->getLevel()->removeEntity($this);
unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]); $this->chunk->removeEntity($this);
$this->despawnFromAll(); $this->despawnFromAll();
if($this instanceof Player){ if($this instanceof Player){
foreach($this->chunksLoaded as $index => $Yndex){ foreach($this->chunksLoaded as $index => $Yndex){
@ -611,7 +608,7 @@ abstract class Entity extends Position implements Metadatable{
$this->dataPacket($pk); $this->dataPacket($pk);
} }
$this->spawnToAll(); $this->spawnToAll();
$this->chunkIndex = false; $this->chunk = null;
} }
public function getPosition(){ 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); $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->chunk === null or ($this->chunk->getX() !== ($this->x >> 4) and $this->chunk->getZ() !== ($this->z >> 4))){
if($this->chunkIndex !== false){ if($this->chunk instanceof Chunk){
unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]); $this->chunk->removeEntity($this);
} }
$this->chunkIndex = $index;
$this->getLevel()->loadChunk($this->x >> 4, $this->z >> 4); $this->getLevel()->loadChunk($this->x >> 4, $this->z >> 4);
$this->chunk = $this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4);
if(!$this->justCreated){ if(!$this->justCreated){
$newChunk = $this->getLevel()->getUsingChunk($this->x >> 4, $this->z >> 4); $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(); $this->scheduleUpdate();
@ -970,8 +967,10 @@ abstract class Entity extends Position implements Metadatable{
if($this->closed === false){ if($this->closed === false){
$this->closed = true; $this->closed = true;
unset(Entity::$needUpdate[$this->id]); unset(Entity::$needUpdate[$this->id]);
if($this->chunk instanceof Chunk){
$this->chunk->removeEntity($this);
}
$this->getLevel()->removeEntity($this); $this->getLevel()->removeEntity($this);
unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]);
$this->despawnFromAll(); $this->despawnFromAll();
$this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this)); $this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this));
} }

View 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);
}

View File

@ -24,11 +24,8 @@
*/ */
namespace pocketmine\level; namespace pocketmine\level;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\entity\Human;
use pocketmine\event\level\SpawnChangeEvent; use pocketmine\event\level\SpawnChangeEvent;
use pocketmine\level\format\BaseLevelProvider;
use pocketmine\level\format\LevelProvider; use pocketmine\level\format\LevelProvider;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\level\generator\Generator; use pocketmine\level\generator\Generator;
@ -42,7 +39,7 @@ use pocketmine\tile\Tile;
use pocketmine\level\format\Chunk; use pocketmine\level\format\Chunk;
class Level implements Metadatable{ class Level implements ChunkManager, Metadatable{
private static $levelIdCounter = 1; private static $levelIdCounter = 1;
@ -80,7 +77,6 @@ class Level implements Metadatable{
/** /**
* Returns the chunk unique hash/key * Returns the chunk unique hash/key
* TODO: return integer values (port from PMF)
* *
* @param int $x * @param int $x
* @param int $z * @param int $z
@ -95,17 +91,31 @@ class Level implements Metadatable{
* Init the default level data * Init the default level data
* *
* @param Server $server * @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->levelId = static::$levelIdCounter++;
$this->server = $server; $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->players = new \SplObjectStorage();
$this->entities = new \SplObjectStorage(); $this->entities = new \SplObjectStorage();
$this->tiles = new \SplObjectStorage(); $this->tiles = new \SplObjectStorage();
} }
/**
* @return Server
*/
public function getServer(){
return $this->server;
}
/** /**
* @return LevelProvider * @return LevelProvider
*/ */
@ -136,35 +146,6 @@ class Level implements Metadatable{
return Block::get($blockId, $meta, Position::fromObject(clone $pos, $this)); 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, * Sets on Vector3 the data from a Block object,
* does block updates and puts the changes to the send queue. * does block updates and puts the changes to the send queue.
@ -173,7 +154,8 @@ class Level implements Metadatable{
* @param Block $block * @param Block $block
*/ */
public function setBlock(Vector3 $pos, 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 updates
//block change send queue //block change send queue
//etc. //etc.
@ -288,7 +270,7 @@ class Level implements Metadatable{
* *
* @return Chunk * @return Chunk
*/ */
protected function getChunkAt($x, $z, $create = false){ public function getChunkAt($x, $z, $create = false){
$this->provider->getChunk($x, $z, $create); $this->provider->getChunk($x, $z, $create);
} }
@ -314,8 +296,7 @@ class Level implements Metadatable{
* @return bool * @return bool
*/ */
public function isChunkLoaded($x, $z){ public function isChunkLoaded($x, $z){
//TODO return $this->provider->isChunkLoaded($x, $z);
return false;
} }
/** /**

View File

@ -1140,69 +1140,6 @@ class Level{
return $this->level->isChunkLoaded($X, $Z); 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 * Unloads a chunk
* *

View File

@ -38,7 +38,7 @@ interface Chunk{
public function getZ(); public function getZ();
/** /**
* @return \pocketmine\level\Level * @return \pocketmine\level\format\LevelProvider
*/ */
public function getLevel(); public function getLevel();
@ -60,6 +60,7 @@ interface Chunk{
* @param int $z 0-15 * @param int $z 0-15
* @param int $blockId , if null, do not change * @param int $blockId , if null, do not change
* @param int $meta 0-15, 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); public function setBlock($x, $y, $z, $blockId = null, $meta = null);

View File

@ -23,6 +23,11 @@ namespace pocketmine\level\format;
interface ChunkSection{ interface ChunkSection{
/**
* @return int
*/
public function getY();
/** /**
* @param int $x 0-15 * @param int $x 0-15
* @param int $y 0-15 * @param int $y 0-15

View File

@ -21,16 +21,16 @@
namespace pocketmine\level\format; namespace pocketmine\level\format;
use pocketmine\level\Level;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\Server;
interface LevelProvider{ interface LevelProvider{
/** /**
* @param Server $server * @param Level $level
* @param string $path * @param string $path
*/ */
public function __construct(Server $server, $path); public function __construct(Level $level, $path);
/** @return string */ /** @return string */
public function getPath(); public function getPath();
@ -57,21 +57,64 @@ interface LevelProvider{
*/ */
public function getChunk($X, $Z, $create = false); public function getChunk($X, $Z, $create = false);
/**
* @return bool
*/
public function saveChunks(); public function saveChunks();
/**
* @param int $X
* @param int $Z
*/
public function saveChunk($X, $Z);
public function unloadChunks(); public function unloadChunks();
/**
* @param int $X
* @param int $Z
*
* @return bool
*/
public function loadChunk($X, $Z); 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); public function isChunkGenerated($X, $Z);
/**
* @param int $X
* @param int $Z
*
* @return bool
*/
public function isChunkLoaded($X, $Z);
/**
* @return string
*/
public function getName(); public function getName();
/**
* @return int
*/
public function getTime();
/**
* @param int $value
*/
public function setTime($value);
/** /**
* @return Vector3 * @return Vector3
*/ */
@ -87,4 +130,9 @@ interface LevelProvider{
*/ */
public function getLoadedChunks(); public function getLoadedChunks();
/**
* @return Level
*/
public function getLevel();
} }

View 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);
}
}

View File

@ -22,20 +22,122 @@
namespace pocketmine\level\format\anvil; namespace pocketmine\level\format\anvil;
use pocketmine\level\format\generic\BaseLevelProvider; use pocketmine\level\format\generic\BaseLevelProvider;
use pocketmine\level\Level;
use pocketmine\Player;
class Anvil extends BaseLevelProvider{ class Anvil extends BaseLevelProvider{
protected $basePath;
public function __construct($path, $levelName){ /** @var RegionLoader */
$this->basePath = realpath($path) . "/"; protected $regions = [];
}
/** @var Chunk[] */
protected $chunks = [];
public static function isValid($path){ 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){ public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){
$x = $chunkX >> 5; $x = $chunkX >> 5;
$z = $chunkZ >> 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;
}
} }

View File

@ -23,6 +23,7 @@ namespace pocketmine\level\format\anvil;
use pocketmine\level\format\generic\BaseChunk; use pocketmine\level\format\generic\BaseChunk;
use pocketmine\level\format\generic\EmptyChunkSection; use pocketmine\level\format\generic\EmptyChunkSection;
use pocketmine\level\format\LevelProvider;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Compound;
@ -33,7 +34,7 @@ class Chunk extends BaseChunk{
/** @var Compound */ /** @var Compound */
protected $nbt; protected $nbt;
public function __construct(Level $level, Compound $nbt){ public function __construct(LevelProvider $level, Compound $nbt){
$this->nbt = $nbt; $this->nbt = $nbt;
if($this->nbt->Entities instanceof Enum){ 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){ public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false){
@ -101,6 +102,14 @@ class Chunk extends BaseChunk{
//TODO: maxBlockY, biomeMap, biomeTemp //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;
} }
} }

View File

@ -39,6 +39,10 @@ class ChunkSection implements \pocketmine\level\format\ChunkSection{
$this->skyLight = (string) $nbt["SkyLight"]; $this->skyLight = (string) $nbt["SkyLight"];
} }
public function getY(){
return $this->y;
}
public function getBlockId($x, $y, $z){ public function getBlockId($x, $y, $z){
return ord($this->blocks{($y << 8) + ($z << 4) + $x}); return ord($this->blocks{($y << 8) + ($z << 4) + $x});
} }

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\format\anvil; namespace pocketmine\level\format\anvil;
use pocketmine\level\format\LevelProvider;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\Byte;
use pocketmine\nbt\tag\ByteArray; use pocketmine\nbt\tag\ByteArray;
@ -29,6 +30,7 @@ use pocketmine\nbt\tag\Enum;
use pocketmine\nbt\tag\Int; use pocketmine\nbt\tag\Int;
use pocketmine\nbt\tag\IntArray; use pocketmine\nbt\tag\IntArray;
use pocketmine\nbt\tag\Long; use pocketmine\nbt\tag\Long;
use pocketmine\Player;
use pocketmine\utils\Binary; use pocketmine\utils\Binary;
class RegionLoader{ class RegionLoader{
@ -42,14 +44,15 @@ class RegionLoader{
protected $filePath; protected $filePath;
protected $filePointer; protected $filePointer;
protected $lastSector; protected $lastSector;
/** @var LevelProvider */
protected $levelProvider;
protected $locationTable = []; protected $locationTable = [];
public function __construct($path, /*Level $level, */ public function __construct(LevelProvider $level, $regionX, $regionZ){
$regionX, $regionZ){
$this->x = $regionX; $this->x = $regionX;
$this->z = $regionZ; $this->z = $regionZ;
$this->filePath = /*$level->getPath()*/ $this->levelProvider = $level;
$path . "region/r.$regionX.$regionZ.mca"; $this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.mca";
touch($this->filePath); touch($this->filePath);
$this->filePointer = fopen($this->filePath, "r+b"); $this->filePointer = fopen($this->filePath, "r+b");
flock($this->filePointer, LOCK_EX); 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){ public function readChunk($x, $z, $generate = true){
$index = self::getChunkOffset($x, $z); $index = self::getChunkOffset($x, $z);
if($index < 0 or $index >= 4096){ if($index < 0 or $index >= 4096){
return false; return false;
} }
if($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0){ if(!$this->isChunkGenerated($index)){
if($generate === true){ if($generate === true){
//Allocate space //Allocate space
$this->locationTable[$index][0] = ++$this->lastSector; $this->locationTable[$index][0] = ++$this->lastSector;
@ -116,8 +123,11 @@ class RegionLoader{
return false; return false;
} }
return $chunk; return new Chunk($this->levelProvider, $chunk);
//$chunk = new Chunk($level, $chunk); }
public function chunkExists($x, $z){
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
} }
public function generateChunk($x, $z){ public function generateChunk($x, $z){
@ -131,6 +141,8 @@ class RegionLoader{
$nbt->InhabitedTime = new Long("InhabitedTime", 0); $nbt->InhabitedTime = new Long("InhabitedTime", 0);
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256)); $nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127)); $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 = new Enum("Sections", []);
$nbt->Sections->setTagType(NBT::TAG_Compound); $nbt->Sections->setTagType(NBT::TAG_Compound);
$nbt->Entities = new Enum("Entities", []); $nbt->Entities = new Enum("Entities", []);
@ -139,6 +151,10 @@ class RegionLoader{
$nbt->TileEntities->setTagType(NBT::TAG_Compound); $nbt->TileEntities->setTagType(NBT::TAG_Compound);
$nbt->TileTicks = new Enum("TileTicks", []); $nbt->TileTicks = new Enum("TileTicks", []);
$nbt->TileTicks->setTagType(NBT::TAG_Compound); $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 = new NBT(NBT::BIG_ENDIAN);
$writer->setData(new Compound("", array($nbt))); $writer->setData(new Compound("", array($nbt)));
$chunkData = $writer->writeCompressed(self::COMPRESSION_ZLIB, self::$COMPRESSION_LEVEL); $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)); 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){ protected static function getChunkOffset($x, $z){
return $x + ($z << 5); return $x + ($z << 5);
} }

View File

@ -21,27 +21,46 @@
namespace pocketmine\level\format\generic; namespace pocketmine\level\format\generic;
use pocketmine\entity\DroppedItem;
use pocketmine\entity\Entity;
use pocketmine\level\format\Chunk; use pocketmine\level\format\Chunk;
use pocketmine\level\format\ChunkSection; use pocketmine\level\format\ChunkSection;
use pocketmine\level\format\LevelProvider;
use pocketmine\level\Level; 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{ abstract class BaseChunk implements Chunk{
/** @var ChunkSection[] */ /** @var ChunkSection[] */
protected $sections = []; protected $sections = [];
/** @var Entity[] */
protected $entities = [];
/** @var Tile[] */
protected $tiles = [];
/** @var \WeakRef<LevelProvider> */
protected $level; protected $level;
protected $x; protected $x;
protected $z; protected $z;
/** /**
* @param Level $level * @param LevelProvider $level
* @param int $x * @param int $x
* @param int $z * @param int $z
* @param ChunkSection[] $sections * @param ChunkSection[] $sections
* @param Compound[] $entities
* @param Compound[] $tiles
*/ */
public function __construct(Level $level, $x, $z, array $sections){ protected function __construct(LevelProvider $level, $x, $z, array $sections, array $entities = [], array $tiles = []){
$this->level = $level; $this->level = new \WeakRef($level);
$this->x = (int) $x; $this->x = (int) $x;
$this->z = (int) $z; $this->z = (int) $z;
foreach($sections as $Y => $section){ foreach($sections as $Y => $section){
@ -59,6 +78,44 @@ abstract class BaseChunk implements Chunk{
return; 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(){ public function getX(){
@ -69,8 +126,11 @@ abstract class BaseChunk implements Chunk{
return $this->z; return $this->z;
} }
/**
* @return LevelProvider
*/
public function getLevel(){ public function getLevel(){
return $this->level; return $this->level->valid() ? $this->level->get() : null;
} }
public function getBlock($x, $y, $z, &$blockId, &$meta = null){ public function getBlock($x, $y, $z, &$blockId, &$meta = null){
@ -140,6 +200,59 @@ abstract class BaseChunk implements Chunk{
$this->sections[(int) $fY] = $section; $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(){ public function getSections(){
return $this->sections; return $this->sections;
} }

View File

@ -21,16 +21,83 @@
namespace pocketmine\level\format\generic; namespace pocketmine\level\format\generic;
use pocketmine\level\format\LevelProvider; 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{ abstract class BaseLevelProvider implements LevelProvider{
/** @var Server */ /** @var Level */
protected $server; protected $level;
/** @var string */ /** @var string */
protected $path; protected $path;
/** @var Compound */
protected $levelData;
public function __construct(Server $server, $path){ public function __construct(Level $level, $path){
$this->server = $server; $this->level = $level->getServer();
$this->path = $path; $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);
}
} }

View File

@ -21,7 +21,7 @@
namespace pocketmine\level\generator; namespace pocketmine\level\generator;
use pocketmine\block\Air; use pocketmine\level\generator\populator\Populator;
use pocketmine\block\CoalOre; use pocketmine\block\CoalOre;
use pocketmine\block\DiamondOre; use pocketmine\block\DiamondOre;
use pocketmine\block\Dirt; use pocketmine\block\Dirt;
@ -31,13 +31,22 @@ use pocketmine\block\IronOre;
use pocketmine\block\LapisOre; use pocketmine\block\LapisOre;
use pocketmine\block\RedstoneOre; use pocketmine\block\RedstoneOre;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\format\SimpleChunk;
use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Ore;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
class Flat extends Generator{ 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(){ public function getSettings(){
return $this->options; return $this->options;
@ -75,7 +84,7 @@ class Flat extends Generator{
}*/ }*/
} }
public function parsePreset($preset){ protected function parsePreset($preset){
$this->preset = $preset; $this->preset = $preset;
$preset = explode(";", $preset); $preset = explode(";", $preset);
$version = (int) $preset[0]; $version = (int) $preset[0];
@ -90,30 +99,33 @@ class Flat extends Generator{
$b = Item::fromString($b); $b = Item::fromString($b);
$cnt = $matches[2][$i] === "" ? 1 : intval($matches[2][$i]); $cnt = $matches[2][$i] === "" ? 1 : intval($matches[2][$i]);
for($cY = $y, $y += $cnt; $cY < $y; ++$cY){ for($cY = $y, $y += $cnt; $cY < $y; ++$cY){
$this->structure[$cY] = $b; $this->structure[$cY] = [$b->getID(),$b->getDamage()];
} }
} }
$this->floorLevel = $y; $this->floorLevel = $y;
for(; $y < 0xFF; ++$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){ for($Y = 0; $Y < 8; ++$Y){
$this->chunks[$Y] = ""; $this->chunks[$Y] = "";
$startY = $Y << 4; $startY = $Y << 4;
$endY = $startY + 16; $endY = $startY + 16;
for($Z = 0; $Z < 16; ++$Z){ for($Z = 0; $Z < 16; ++$Z){
for($X = 0; $X < 16; ++$X){ for($X = 0; $X < 16; ++$X){
$blocks = "";
$metas = "";
for($y = $startY; $y < $endY; ++$y){ for($y = $startY; $y < $endY; ++$y){
$blocks .= chr($this->structure[$y]->getID()); if($this->structure[$y][0] !== 0){
$metas .= substr(dechex($this->structure[$y]->getDamage()), -1); $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->level = $level;
$this->random = $random; $this->random = $random;
} }
public function generateChunk($chunkX, $chunkZ){ public function generateChunk($chunkX, $chunkZ){
for($Y = 0; $Y < 8; ++$Y){ $chunk = clone $this->chunk;
$this->level->setMiniChunk($chunkX, $chunkZ, $Y, $this->chunks[$Y]); $chunk->setX($chunkX);
} $chunk->setZ($chunkZ);
$this->level->setChunk($chunkX, $chunkZ, $chunk);
} }
public function populateChunk($chunkX, $chunkZ){ public function populateChunk($chunkX, $chunkZ){

View 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
}
}
}

View 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;
}
}

View 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);
}
}

View File

@ -50,7 +50,7 @@ abstract class Generator{
public abstract function __construct(array $settings = []); 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); public abstract function generateChunk($chunkX, $chunkZ);

View File

@ -37,11 +37,15 @@ use pocketmine\level\generator\populator\Tree;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
use pocketmine\level\generator\populator\Populator;
class Normal extends Generator{ class Normal extends Generator{
/** @var Populator[] */
private $populators = []; private $populators = [];
/** @var GenerationChunkManager */
private $level; private $level;
/** @var Random */
private $random; private $random;
private $worldHeight = 65; private $worldHeight = 65;
private $waterHeight = 63; private $waterHeight = 63;
@ -62,7 +66,7 @@ class Normal extends Generator{
return []; return [];
} }
public function init(Level $level, Random $random){ public function init(GenerationChunkManager $level, Random $random){
$this->level = $level; $this->level = $level;
$this->random = $random; $this->random = $random;
$this->random->setSeed($this->level->getSeed()); $this->random->setSeed($this->level->getSeed());

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\generator\object; namespace pocketmine\level\generator\object;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
@ -39,11 +40,11 @@ class BigTree extends Tree{
private $addLogVines = false; private $addLogVines = false;
private $addCocoaPlants = false; private $addCocoaPlants = false;
public function canPlaceObject(Level $level, Vector3 $pos){ public function canPlaceObject(ChunkManager $level, $x, $y, $z){
return false; 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); $this->trunkHeight = (int) ($this->totalHeight * $this->trunkHeightMultiplier);
$leaves = $this->getLeafGroupPoints($level, $pos); $leaves = $this->getLeafGroupPoints($level, $pos);

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\generator\object; namespace pocketmine\level\generator\object;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\math\VectorMath; use pocketmine\math\VectorMath;
@ -39,20 +40,20 @@ class Ore{
return $this->type; return $this->type;
} }
public function canPlaceObject(Level $level, $x, $y, $z){ public function canPlaceObject(ChunkManager $level, $x, $y, $z){
return ($level->level->getBlockID($x, $y, $z) === 1); 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; $clusterSize = (int) $this->type->clusterSize;
$angle = $this->random->nextFloat() * M_PI; $angle = $this->random->nextFloat() * M_PI;
$offset = VectorMath::getDirection2D($angle)->multiply($clusterSize)->divide(8); $offset = VectorMath::getDirection2D($angle)->multiply($clusterSize)->divide(8);
$x1 = $pos->x + 8 + $offset->x; $x1 = $x + 8 + $offset->x;
$x2 = $pos->x + 8 - $offset->x; $x2 = $x + 8 - $offset->x;
$z1 = $pos->z + 8 + $offset->y; $z1 = $z + 8 + $offset->y;
$z2 = $pos->z + 8 - $offset->y; $z2 = $z + 8 - $offset->y;
$y1 = $pos->y + $this->random->nextRange(0, 3) + 2; $y1 = $y + $this->random->nextRange(0, 3) + 2;
$y2 = $pos->y + $this->random->nextRange(0, 3) + 2; $y2 = $y + $this->random->nextRange(0, 3) + 2;
for($count = 0; $count <= $clusterSize; ++$count){ for($count = 0; $count <= $clusterSize; ++$count){
$seedX = $x1 + ($x2 - $x1) * $count / $clusterSize; $seedX = $x1 + ($x2 - $x1) * $count / $clusterSize;
$seedY = $y1 + ($y2 - $y1) * $count / $clusterSize; $seedY = $y1 + ($y2 - $y1) * $count / $clusterSize;
@ -80,8 +81,8 @@ class Ore{
$sizeZ = ($z + 0.5 - $seedZ) / $size; $sizeZ = ($z + 0.5 - $seedZ) / $size;
$sizeZ *= $sizeZ; $sizeZ *= $sizeZ;
if(($sizeX + $sizeY + $sizeZ) < 1 and $level->level->getBlockID($x, $y, $z) === 1){ if(($sizeX + $sizeY + $sizeZ) < 1 and $level->getBlockIdAt($x, $y, $z) === 1){
$level->setBlockRaw(new Vector3($x, $y, $z), $this->type->material); $level->setBlockIdAt($x, $y, $z, $this->type->material);
} }
} }
} }

View File

@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object;
use pocketmine\block\Dirt; use pocketmine\block\Dirt;
use pocketmine\block\Leaves; use pocketmine\block\Leaves;
use pocketmine\block\Wood; use pocketmine\block\Wood;
use pocketmine\item\Block;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -34,7 +36,7 @@ class PineTree extends Tree{
private $leavesSizeY = -1; private $leavesSizeY = -1;
private $leavesAbsoluteMaxRadius = -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); $this->findRandomLeavesSize($random);
$checkRadius = 0; $checkRadius = 0;
for($yy = 0; $yy < $this->totalHeight; ++$yy){ for($yy = 0; $yy < $this->totalHeight; ++$yy){
@ -43,7 +45,7 @@ class PineTree extends Tree{
} }
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){ for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){ 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; return false;
} }
} }
@ -59,11 +61,11 @@ class PineTree extends Tree{
$this->leavesAbsoluteMaxRadius = 2 + $random->nextRange(0, 1); $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){ if($this->leavesSizeY === -1 or $this->leavesAbsoluteMaxRadius === -1){
$this->findRandomLeavesSize($random); $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; $leavesRadius = 0;
$leavesMaxRadius = 1; $leavesMaxRadius = 1;
$leavesBottomY = $this->totalHeight - $this->leavesSizeY; $leavesBottomY = $this->totalHeight - $this->leavesSizeY;
@ -73,7 +75,8 @@ class PineTree extends Tree{
for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){ for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){
for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){ for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){
if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){ 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); $trunkHeightReducer = $random->nextRange(0, 3);
for($yy = 0; $yy < ($this->totalHeight - $trunkHeightReducer); ++$yy){ 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);
} }
} }

View File

@ -22,6 +22,7 @@
namespace pocketmine\level\generator\object; namespace pocketmine\level\generator\object;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -35,10 +36,10 @@ class Pond{
$this->random = $random; $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){
} }
} }

View File

@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object;
use pocketmine\block\Dirt; use pocketmine\block\Dirt;
use pocketmine\block\Leaves; use pocketmine\block\Leaves;
use pocketmine\block\Wood; use pocketmine\block\Wood;
use pocketmine\item\Block;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -38,7 +40,7 @@ class SmallTree extends Tree{
private $addLogVines = false; private $addLogVines = false;
private $addCocoaPlants = 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; $radiusToCheck = 0;
for($yy = 0; $yy < $this->trunkHeight + 3; ++$yy){ for($yy = 0; $yy < $this->trunkHeight + 3; ++$yy){
if($yy == 1 or $yy === $this->trunkHeight){ if($yy == 1 or $yy === $this->trunkHeight){
@ -46,7 +48,7 @@ class SmallTree extends Tree{
} }
for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){ for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){
for($zz = -$radiusToCheck; $zz < ($radiusToCheck + 1); ++$zz){ 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; return false;
} }
} }
@ -56,10 +58,9 @@ class SmallTree extends Tree{
return true; 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 // The base dirt block
$dirtpos = new Vector3($pos->x, $pos->y - 1, $pos->z); $level->setBlockIdAt($x, $y, $z, Block::DIRT);
$level->setBlockRaw($dirtpos, new Dirt());
// Adjust the tree trunk's height randomly // Adjust the tree trunk's height randomly
// plot [-14:11] int( x / 8 ) + 5 // plot [-14:11] int( x / 8 ) + 5
@ -84,10 +85,8 @@ class SmallTree extends Tree{
for($xx = -$bRadius; $xx <= $bRadius; ++$xx){ for($xx = -$bRadius; $xx <= $bRadius; ++$xx){
for($zz = -$bRadius; $zz <= $bRadius; ++$zz){ for($zz = -$bRadius; $zz <= $bRadius; ++$zz){
if(sqrt(($xx * $xx) + ($zz * $zz)) <= $radius){ if(sqrt(($xx * $xx) + ($zz * $zz)) <= $radius){
$leafpos = new Vector3($pos->x + $xx, $level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
$pos->y + $yy, $level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
$pos->z + $zz);
$level->setBlockRaw($leafpos, new Leaves($this->type));
} }
} }
} }
@ -96,8 +95,8 @@ class SmallTree extends Tree{
// Place the trunk last // Place the trunk last
if($leaflevel > 1){ if($leaflevel > 1){
$trunkpos = new Vector3($pos->x, $pos->y + $yy, $pos->z); $level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK);
$level->setBlockRaw($trunkpos, new Wood($this->type)); $level->setBlockDataAt($x, $y + $yy, $z, $this->type);
} }
} }
} }

View File

@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object;
use pocketmine\block\Dirt; use pocketmine\block\Dirt;
use pocketmine\block\Leaves; use pocketmine\block\Leaves;
use pocketmine\block\Wood; use pocketmine\block\Wood;
use pocketmine\item\Block;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -34,7 +36,7 @@ class SpruceTree extends Tree{
private $leavesBottomY = -1; private $leavesBottomY = -1;
private $leavesMaxRadius = -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); $this->findRandomLeavesSize($random);
$checkRadius = 0; $checkRadius = 0;
for($yy = 0; $yy < $this->totalHeight + 2; ++$yy){ for($yy = 0; $yy < $this->totalHeight + 2; ++$yy){
@ -43,7 +45,7 @@ class SpruceTree extends Tree{
} }
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){ for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){ 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; return false;
} }
} }
@ -59,28 +61,30 @@ class SpruceTree extends Tree{
$this->leavesMaxRadius = 1 + $random->nextRange(0, 1); $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){ if($this->leavesBottomY === -1 or $this->leavesMaxRadius === -1){
$this->findRandomLeavesSize($random); $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; $leavesRadius = 0;
for($yy = $this->totalHeight; $yy >= $this->leavesBottomY; --$yy){ for($yy = $this->totalHeight; $yy >= $this->leavesBottomY; --$yy){
for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){ for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){
for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){ for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){
if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){ 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; --$leavesRadius;
}elseif($leavesRadius < $this->leavesMaxRadius){ }elseif($leavesRadius < $this->leavesMaxRadius){
++$leavesRadius; ++$leavesRadius;
} }
} }
for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){ 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);
} }
} }

View File

@ -22,27 +22,29 @@
namespace pocketmine\level\generator\object; namespace pocketmine\level\generator\object;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
class TallGrass{ class TallGrass{
public static function growGrass(Level $level, Vector3 $pos, Random $random, $count = 15, $radius = 10){ public static function growGrass(ChunkManager $level, Vector3 $pos, Random $random, $count = 15, $radius = 10){
$arr = array( $arr = [
Block::get(Block::DANDELION, 0), [Block::DANDELION, 0],
Block::get(Block::CYAN_FLOWER, 0), [Block::CYAN_FLOWER, 0],
Block::get(Block::TALL_GRASS, 1), [Block::TALL_GRASS, 1],
Block::get(Block::TALL_GRASS, 1), [Block::TALL_GRASS, 1],
Block::get(Block::TALL_GRASS, 1), [Block::TALL_GRASS, 1],
Block::get(Block::TALL_GRASS, 1) [Block::TALL_GRASS, 1]
); ];
$arrC = count($arr) - 1; $arrC = count($arr) - 1;
for($c = 0; $c < $count; ++$c){ for($c = 0; $c < $count; ++$c){
$x = $random->nextRange($pos->x - $radius, $pos->x + $radius); $x = $random->nextRange($pos->x - $radius, $pos->x + $radius);
$z = $random->nextRange($pos->z - $radius, $pos->z + $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)]; $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]);
} }
} }
} }

View File

@ -22,6 +22,7 @@
namespace pocketmine\level\generator\object; namespace pocketmine\level\generator\object;
use pocketmine\block\Sapling; use pocketmine\block\Sapling;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -36,7 +37,7 @@ class Tree{
18 => true, 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){ switch($type & 0x03){
case Sapling::SPRUCE: case Sapling::SPRUCE:
if($random->nextRange(0, 1) === 1){ if($random->nextRange(0, 1) === 1){
@ -62,8 +63,8 @@ class Tree{
//} //}
break; break;
} }
if($tree->canPlaceObject($level, $pos, $random)){ if($tree->canPlaceObject($level, $x, $y, $z, $random)){
$tree->placeObject($level, $pos, $random); $tree->placeObject($level, $x, $y, $z, $random);
} }
} }
} }

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\generator\populator; namespace pocketmine\level\generator\populator;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -31,7 +32,7 @@ class Mineshaft extends Populator{
private static $BASE_Y = 35; private static $BASE_Y = 35;
private static $RAND_Y = 11; 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){ if($random->nextRange(0, self::$ODD) === 0){
//$mineshaft = new Mineshaft($random); //$mineshaft = new Mineshaft($random);
} }

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\generator\populator; namespace pocketmine\level\generator\populator;
use pocketmine\level\ChunkManager;
use pocketmine\level\generator\object\Ore as ObjectOre; use pocketmine\level\generator\object\Ore as ObjectOre;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
@ -29,7 +30,7 @@ use pocketmine\utils\Random;
class Ore extends Populator{ class Ore extends Populator{
private $oreTypes = []; 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){ foreach($this->oreTypes as $type){
$ore = new ObjectOre($random, $type); $ore = new ObjectOre($random, $type);
for($i = 0; $i < $ore->type->clusterCount; ++$i){ 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); $y = $random->nextRange($ore->type->minHeight, $ore->type->maxHeight);
$z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15); $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15);
if($ore->canPlaceObject($level, $x, $y, $z)){ if($ore->canPlaceObject($level, $x, $y, $z)){
$ore->placeObject($level, new Vector3($x, $y, $z)); $ore->placeObject($level, $x, $y, $z);
} }
} }
} }

View File

@ -22,6 +22,7 @@
namespace pocketmine\level\generator\populator; namespace pocketmine\level\generator\populator;
use pocketmine\block\Water; use pocketmine\block\Water;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -31,16 +32,14 @@ class Pond extends Populator{
private $lavaOdd = 4; private $lavaOdd = 4;
private $lavaSurfaceOdd = 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){ if($random->nextRange(0, $this->waterOdd) === 0){
$v = new Vector3( $x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 16);
$random->nextRange($chunkX << 4, ($chunkX << 4) + 16), $y = $random->nextRange(0, 128);
$random->nextRange(0, 128), $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 16);
$random->nextRange($chunkZ << 4, ($chunkZ << 4) + 16)
);
$pond = new \pocketmine\level\generator\object\Pond($random, new Water()); $pond = new \pocketmine\level\generator\object\Pond($random, new Water());
if($pond->canPlaceObject($level, $v)){ if($pond->canPlaceObject($level, $x, $y, $z)){
$pond->placeObject($level, $v); $pond->placeObject($level, $x, $y, $z);
} }
} }
} }

View File

@ -24,9 +24,10 @@
*/ */
namespace pocketmine\level\generator\populator; namespace pocketmine\level\generator\populator;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\utils\Random; use pocketmine\utils\Random;
abstract class Populator{ abstract class Populator{
public abstract function populate(Level $level, $chunkX, $chunkZ, Random $random); public abstract function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random);
} }

View File

@ -23,12 +23,13 @@ namespace pocketmine\level\generator\populator;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\TallGrass as BlockTallGrass; use pocketmine\block\TallGrass as BlockTallGrass;
use pocketmine\level\ChunkManager;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
class TallGrass extends Populator{ class TallGrass extends Populator{
/** @var Level */ /** @var ChunkManager */
private $level; private $level;
private $randomAmount; private $randomAmount;
private $baseAmount; private $baseAmount;
@ -41,7 +42,7 @@ class TallGrass extends Populator{
$this->baseAmount = $amount; $this->baseAmount = $amount;
} }
public function populate(Level $level, $chunkX, $chunkZ, Random $random){ public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
$this->level = $level; $this->level = $level;
$amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount; $amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount;
for($i = 0; $i < $amount; ++$i){ for($i = 0; $i < $amount; ++$i){
@ -51,22 +52,23 @@ class TallGrass extends Populator{
$xx = $x - 7 + $random->nextRange(0, 15); $xx = $x - 7 + $random->nextRange(0, 15);
$zz = $z - 7 + $random->nextRange(0, 15); $zz = $z - 7 + $random->nextRange(0, 15);
$yy = $this->getHighestWorkableBlock($xx, $zz); $yy = $this->getHighestWorkableBlock($xx, $zz);
$vector = new Vector3($xx, $yy, $zz);
if($yy !== -1 and $this->canTallGrassStay($this->level->getBlockRaw($vector))){ if($yy !== -1 and $this->canTallGrassStay($xx, $yy, $zz)){
$this->level->setBlockRaw($vector, new BlockTallGrass(1)); $this->level->setBlockIdAt($xx, $yy, $zz, Block::TALL_GRASS);
$this->level->setBlockDataAt($xx, $yy, $zz, 1);
} }
} }
} }
} }
private function canTallGrassStay(Block $block){ private function canTallGrassStay($x, $y, $z){
return $block->getID() === Block::AIR and $block->getSide(0)->getID() === Block::GRASS; return $this->level->getBlockIdAt($x, $y, $z) === Block::AIR and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::GRASS;
} }
private function getHighestWorkableBlock($x, $z){ private function getHighestWorkableBlock($x, $z){
for($y = 128; $y > 0; --$y){ for($y = 128; $y > 0; --$y){
$b = $this->level->getBlockRaw(new Vector3($x, $y, $z)); $b = $this->level->getBlockIdAt($x, $y, $z);
if($b->getID() === Block::AIR or $b->getID() === Block::LEAVES){ if($b === Block::AIR or $b === Block::LEAVES){
if(--$y <= 0){ if(--$y <= 0){
return -1; return -1;
} }

View File

@ -23,12 +23,14 @@ namespace pocketmine\level\generator\populator;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\Sapling; use pocketmine\block\Sapling;
use pocketmine\level\ChunkManager;
use pocketmine\level\generator\object\Tree as ObjectTree; use pocketmine\level\generator\object\Tree as ObjectTree;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
class Tree extends Populator{ class Tree extends Populator{
/** @var ChunkManager */
private $level; private $level;
private $randomAmount; private $randomAmount;
private $baseAmount; private $baseAmount;
@ -41,7 +43,7 @@ class Tree extends Populator{
$this->baseAmount = $amount; $this->baseAmount = $amount;
} }
public function populate(Level $level, $chunkX, $chunkZ, Random $random){ public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
$this->level = $level; $this->level = $level;
$amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount; $amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount;
for($i = 0; $i < $amount; ++$i){ for($i = 0; $i < $amount; ++$i){
@ -56,14 +58,14 @@ class Tree extends Populator{
}else{ }else{
$meta = Sapling::OAK; $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){ private function getHighestWorkableBlock($x, $z){
for($y = 128; $y > 0; --$y){ for($y = 128; $y > 0; --$y){
$b = $this->level->getBlockRaw(new Vector3($x, $y, $z)); $b = $this->level->getBlockIdAt($x, $y, $z);
if($b->getID() !== Block::DIRT and $b->getID() !== Block::GRASS){ if($b !== Block::DIRT and $b !== Block::GRASS){
if(--$y <= 0){ if(--$y <= 0){
return -1; return -1;
} }

View File

@ -25,6 +25,7 @@ use pocketmine\inventory\ChestInventory;
use pocketmine\inventory\DoubleChestInventory; use pocketmine\inventory\DoubleChestInventory;
use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\InventoryHolder;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\format\Chunk;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
@ -44,9 +45,9 @@ class Chest extends Spawnable implements InventoryHolder, Container{
/** @var DoubleChestInventory */ /** @var DoubleChestInventory */
protected $doubleInventory = null; protected $doubleInventory = null;
public function __construct(Level $level, Compound $nbt){ public function __construct(Chunk $chunk, Compound $nbt){
$nbt["id"] = Tile::CHEST; $nbt["id"] = Tile::CHEST;
parent::__construct($level, $nbt); parent::__construct($chunk, $nbt);
$this->inventory = new ChestInventory($this); $this->inventory = new ChestInventory($this);
for($i = 0; $i < $this->getSize(); ++$i){ for($i = 0; $i < $this->getSize(); ++$i){
$this->inventory->setItem($i, $this->getItem($i)); $this->inventory->setItem($i, $this->getItem($i));

View File

@ -26,9 +26,22 @@ use pocketmine\item\Item;
use pocketmine\Network; use pocketmine\Network;
interface Container{ interface Container{
/**
* @param int $index
*
* @return Item
*/
public function getItem($index); public function getItem($index);
/**
* @param int $index
* @param Item $item
*/
public function setItem($index, Item $item); public function setItem($index, Item $item);
/**
* @return int
*/
public function getSize(); public function getSize();
} }

View File

@ -26,6 +26,7 @@ use pocketmine\inventory\FurnaceInventory;
use pocketmine\inventory\FurnaceRecipe; use pocketmine\inventory\FurnaceRecipe;
use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\InventoryHolder;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\format\Chunk;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\Byte;
@ -37,9 +38,9 @@ class Furnace extends Tile implements InventoryHolder, Container{
/** @var FurnaceInventory */ /** @var FurnaceInventory */
protected $inventory; protected $inventory;
public function __construct(Level $level, Compound $nbt){ public function __construct(Chunk $chunk, Compound $nbt){
$nbt["id"] = Tile::FURNACE; $nbt["id"] = Tile::FURNACE;
parent::__construct($level, $nbt); parent::__construct($chunk, $nbt);
$this->inventory = new FurnaceInventory($this); $this->inventory = new FurnaceInventory($this);
for($i = 0; $i < $this->getSize(); ++$i){ for($i = 0; $i < $this->getSize(); ++$i){
$this->inventory->setItem($i, $this->getItem($i)); $this->inventory->setItem($i, $this->getItem($i));

View File

@ -21,6 +21,7 @@
namespace pocketmine\tile; namespace pocketmine\tile;
use pocketmine\level\format\Chunk;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Compound;
@ -31,9 +32,9 @@ use pocketmine\Player;
class Sign extends Spawnable{ class Sign extends Spawnable{
public function __construct(Level $level, Compound $nbt){ public function __construct(Chunk $chunk, Compound $nbt){
$nbt["id"] = Tile::SIGN; $nbt["id"] = Tile::SIGN;
parent::__construct($level, $nbt); parent::__construct($chunk, $nbt);
} }
public function setText($line1 = "", $line2 = "", $line3 = "", $line4 = ""){ public function setText($line1 = "", $line2 = "", $line3 = "", $line4 = ""){
@ -42,7 +43,6 @@ class Sign extends Spawnable{
$this->namedtag->Text3 = $line3; $this->namedtag->Text3 = $line3;
$this->namedtag->Text4 = $line4; $this->namedtag->Text4 = $line4;
$this->spawnToAll(); $this->spawnToAll();
$this->server->handle("tile.update", $this);
return true; return true;
} }

View File

@ -21,6 +21,7 @@
namespace pocketmine\tile; namespace pocketmine\tile;
use pocketmine\level\format\Chunk;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Compound;
use pocketmine\Player; use pocketmine\Player;
@ -28,8 +29,8 @@ use pocketmine\Player;
abstract class Spawnable extends Tile{ abstract class Spawnable extends Tile{
public abstract function spawnTo(Player $player); public abstract function spawnTo(Player $player);
public function __construct(Level $level, Compound $nbt){ public function __construct(Chunk $chunk, Compound $nbt){
parent::__construct($level, $nbt); parent::__construct($chunk, $nbt);
$this->spawnToAll(); $this->spawnToAll();
} }

View File

@ -25,11 +25,10 @@
*/ */
namespace pocketmine\tile; namespace pocketmine\tile;
use pocketmine\level\format\pmf\LevelFormat; use pocketmine\level\format\Chunk;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\level\Position; use pocketmine\level\Position;
use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Compound;
use pocketmine\Server;
abstract class Tile extends Position{ abstract class Tile extends Position{
const SIGN = "Sign"; const SIGN = "Sign";
@ -45,7 +44,8 @@ abstract class Tile extends Position{
*/ */
public static $needUpdate = []; public static $needUpdate = [];
public $chunkIndex; /** @var Chunk */
public $chunk;
public $name; public $name;
public $id; public $id;
public $x; public $x;
@ -63,9 +63,10 @@ abstract class Tile extends Position{
} }
public function __construct(Level $level, Compound $nbt){ public function __construct(Chunk $chunk, Compound $nbt){
$this->server = Server::getInstance(); $this->server = $chunk->getLevel()->getLevel()->getServer();
$this->setLevel($level, true); //Strong reference $this->chunk = $chunk;
$this->setLevel($chunk->getLevel()->getLevel(), true); //Strong reference
$this->namedtag = $nbt; $this->namedtag = $nbt;
$this->closed = false; $this->closed = false;
$this->name = ""; $this->name = "";
@ -75,10 +76,8 @@ abstract class Tile extends Position{
$this->y = (int) $this->namedtag["y"]; $this->y = (int) $this->namedtag["y"];
$this->z = (int) $this->namedtag["z"]; $this->z = (int) $this->namedtag["z"];
$index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4); $this->chunk->addTile($this);
$this->chunkIndex = $index;
$this->getLevel()->addTile($this); $this->getLevel()->addTile($this);
$this->getLevel()->chunkTiles[$this->chunkIndex][$this->id] = $this;
} }
public function saveNBT(){ public function saveNBT(){
@ -100,7 +99,7 @@ abstract class Tile extends Position{
$this->closed = true; $this->closed = true;
unset(Tile::$needUpdate[$this->id]); unset(Tile::$needUpdate[$this->id]);
$this->getLevel()->removeTile($this); $this->getLevel()->removeTile($this);
unset($this->getLevel()->chunkTiles[$this->chunkIndex][$this->id]); $this->chunk->removeTile($this);
} }
} }

@ -1 +1 @@
Subproject commit 7f6711a75ef0e64537cdeb302013386dd2b99c5f Subproject commit 345009779b039fa9c0238fd0a57df5eeff2a2753