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

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;
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);
}
/**

View File

@ -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
*

View File

@ -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);

View File

@ -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

View File

@ -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();
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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){

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 init(Level $level, Random $random);
public abstract function init(GenerationChunkManager $level, Random $random);
public abstract function generateChunk($chunkX, $chunkZ);

View File

@ -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());

View File

@ -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);

View File

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

View File

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

View File

@ -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){
}
}

View File

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

View File

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

View File

@ -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]);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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));

View File

@ -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();
}

View File

@ -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));

View File

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

View File

@ -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();
}

View File

@ -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