mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-15 10:19:39 +00:00
Merge branch 'Level-Rewrite' into 0.9.0
This commit is contained in:
commit
392eb74901
@ -637,20 +637,25 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
$index = key($this->chunksOrder);
|
||||
LevelFormat::getXZ($index, $X, $Z);
|
||||
$this->getLevel()->loadChunk($X, $Z);
|
||||
if(!$this->getLevel()->isChunkPopulated($X, $Z)){
|
||||
$this->getLevel()->loadChunk($X - 1, $Z);
|
||||
$this->getLevel()->loadChunk($X + 1, $Z);
|
||||
$this->getLevel()->loadChunk($X, $Z - 1);
|
||||
$this->getLevel()->loadChunk($X, $Z + 1);
|
||||
$this->getLevel()->loadChunk($X + 1, $Z + 1);
|
||||
$this->getLevel()->loadChunk($X + 1, $Z - 1);
|
||||
$this->getLevel()->loadChunk($X - 1, $Z - 1);
|
||||
$this->getLevel()->loadChunk($X - 1, $Z + 1);
|
||||
$radius = 1;
|
||||
for($z = $Z - $radius; $z <= ($Z + $radius); ++$z){
|
||||
for($x = $X - $radius; $x <= ($X + $radius); ++$x){
|
||||
$this->getLevel()->loadChunk($x, $z);
|
||||
if(!$this->getLevel()->isChunkPopulated($x, $z)){
|
||||
$this->getLevel()->loadChunk($x - 1, $z);
|
||||
$this->getLevel()->loadChunk($x + 1, $z);
|
||||
$this->getLevel()->loadChunk($x, $z - 1);
|
||||
$this->getLevel()->loadChunk($x, $z + 1);
|
||||
$this->getLevel()->loadChunk($x + 1, $z + 1);
|
||||
$this->getLevel()->loadChunk($x + 1, $z - 1);
|
||||
$this->getLevel()->loadChunk($x - 1, $z - 1);
|
||||
$this->getLevel()->loadChunk($x - 1, $z + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($lastChunk as $index => $Yndex){
|
||||
if($Yndex !== 0xff){
|
||||
if($Yndex === 0){
|
||||
$X = null;
|
||||
$Z = null;
|
||||
LevelFormat::getXZ($index, $X, $Z);
|
||||
@ -1188,6 +1193,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
$this->server->getPluginManager()->callEvent(new PlayerJoinEvent($this, $this->username . " joined the game"));
|
||||
|
||||
|
||||
$this->orderChunks();
|
||||
$this->tasks[] = $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask(array($this, "orderChunks")), 30);
|
||||
$this->getNextChunk();
|
||||
|
@ -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;
|
||||
@ -70,7 +71,13 @@ abstract class Entity extends Position implements Metadatable{
|
||||
public $passenger = null;
|
||||
public $vehicle = null;
|
||||
|
||||
public $chunkIndex;
|
||||
/** @var int */
|
||||
public $chunkX;
|
||||
/** @var int */
|
||||
public $chunkZ;
|
||||
|
||||
/** @var Chunk */
|
||||
public $chunk;
|
||||
|
||||
public $lastX;
|
||||
public $lastY;
|
||||
@ -132,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(
|
||||
@ -178,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));
|
||||
@ -219,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;
|
||||
}
|
||||
}
|
||||
@ -576,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){
|
||||
@ -602,7 +608,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
$this->spawnToAll();
|
||||
$this->chunkIndex = false;
|
||||
$this->chunk = null;
|
||||
}
|
||||
|
||||
public function getPosition(){
|
||||
@ -847,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);
|
||||
@ -868,7 +874,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
$this->getLevel()->chunkEntities[$this->chunkIndex][$this->id] = $this;
|
||||
$this->chunk->addEntity($this);
|
||||
}
|
||||
|
||||
$this->scheduleUpdate();
|
||||
@ -961,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));
|
||||
}
|
||||
|
39
src/pocketmine/event/level/LevelEvent.php
Normal file
39
src/pocketmine/event/level/LevelEvent.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Level related events
|
||||
*/
|
||||
namespace pocketmine\event\level;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
|
||||
abstract class LevelEvent extends Event{
|
||||
/** @var \pocketmine\level\Level */
|
||||
protected $level;
|
||||
|
||||
/**
|
||||
* @return \pocketmine\level\Level
|
||||
*/
|
||||
public function getLevel(){
|
||||
return $this->level;
|
||||
}
|
||||
}
|
48
src/pocketmine/event/level/SpawnChangeEvent.php
Normal file
48
src/pocketmine/event/level/SpawnChangeEvent.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?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\event\level;
|
||||
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
|
||||
/**
|
||||
* An event that is called when a level spawn changes.
|
||||
* The previous spawn is included
|
||||
*/
|
||||
class SpawnChangeEvent extends LevelEvent{
|
||||
public static $handlerList = null;
|
||||
|
||||
/** @var Position */
|
||||
private $previousSpawn;
|
||||
|
||||
public function __construct(Level $level, Position $previousSpawn){
|
||||
$this->level = $level;
|
||||
$this->previousSpawn = $previousSpawn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Position
|
||||
*/
|
||||
public function getPreviousSpawn(){
|
||||
return $this->previousSpawn;
|
||||
}
|
||||
}
|
66
src/pocketmine/level/ChunkManager.php
Normal file
66
src/pocketmine/level/ChunkManager.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level;
|
||||
|
||||
interface ChunkManager{
|
||||
/**
|
||||
* Gets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockIdAt($x, $y, $z);
|
||||
|
||||
/**
|
||||
* Sets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockIdAt($x, $y, $z, $id);
|
||||
|
||||
/**
|
||||
* Gets the raw block metadata
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockDataAt($x, $y, $z);
|
||||
|
||||
/**
|
||||
* Sets the raw block metadata.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockDataAt($x, $y, $z, $data);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
1447
src/pocketmine/level/Level_OLD.php
Normal file
1447
src/pocketmine/level/Level_OLD.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -61,8 +61,8 @@ class WorldGenerator{
|
||||
public function generate(){
|
||||
$this->generator->init($this->level, $this->random);
|
||||
|
||||
for($Z = 7; $Z <= 9; ++$Z){
|
||||
for($X = 7; $X <= 9; ++$X){
|
||||
for($Z = 6; $Z <= 10; ++$Z){
|
||||
for($X = 6; $X <= 10; ++$X){
|
||||
$this->level->level->loadChunk($X, $Z);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,9 @@
|
||||
|
||||
namespace pocketmine\level\format;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
interface Chunk{
|
||||
const SECTION_COUNT = 8;
|
||||
|
||||
@ -35,7 +38,7 @@ interface Chunk{
|
||||
public function getZ();
|
||||
|
||||
/**
|
||||
* @return \pocketmine\level\Level
|
||||
* @return \pocketmine\level\format\LevelProvider
|
||||
*/
|
||||
public function getLevel();
|
||||
|
||||
@ -57,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);
|
||||
|
||||
@ -139,9 +143,22 @@ interface Chunk{
|
||||
/**
|
||||
* Thread-safe read-only chunk
|
||||
*
|
||||
* @param bool $includeMaxBlockY
|
||||
* @param bool $includeBiome
|
||||
* @param bool $includeBiomeTemp
|
||||
*
|
||||
* @return ChunkSnapshot
|
||||
*/
|
||||
public function getChunkSnapshot();
|
||||
public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false);
|
||||
|
||||
|
||||
public function addEntity(Entity $entity);
|
||||
|
||||
public function removeEntity(Entity $entity);
|
||||
|
||||
public function addTile(Tile $tile);
|
||||
|
||||
public function removeTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @return \pocketmine\entity\Entity[]
|
||||
@ -199,4 +216,9 @@ interface Chunk{
|
||||
*/
|
||||
public function setSection($fY, ChunkSection $section);
|
||||
|
||||
/**
|
||||
* @return ChunkSection[]
|
||||
*/
|
||||
public function getSections();
|
||||
|
||||
}
|
@ -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
|
||||
@ -131,4 +136,12 @@ interface ChunkSection{
|
||||
*/
|
||||
public function getBlockDataColumn($x, $z);
|
||||
|
||||
public function getIdArray();
|
||||
|
||||
public function getDataArray();
|
||||
|
||||
public function getSkyLightArray();
|
||||
|
||||
public function getLightArray();
|
||||
|
||||
}
|
@ -90,28 +90,4 @@ interface ChunkSnapshot{
|
||||
*/
|
||||
public function getBiome($x, $z);
|
||||
|
||||
/**
|
||||
* Tests whether a section (mini-chunk) is empty
|
||||
*
|
||||
* @param $fY 0-7, (Y / 16)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSectionEmpty($fY);
|
||||
|
||||
/**
|
||||
* @param int $Y 0-7
|
||||
*
|
||||
* @return ChunkSection
|
||||
*/
|
||||
public function getSection($Y);
|
||||
|
||||
/**
|
||||
* @param int $Y 0-7
|
||||
* @param ChunkSection $section
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setSection($Y, ChunkSection $section);
|
||||
|
||||
}
|
@ -21,19 +21,19 @@
|
||||
|
||||
namespace pocketmine\level\format;
|
||||
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Server;
|
||||
|
||||
/**
|
||||
* All Level formats must implement this interface
|
||||
*/
|
||||
interface LevelFormat{
|
||||
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();
|
||||
|
||||
/**
|
||||
* Tells if the path is a valid level.
|
||||
@ -53,33 +53,86 @@ interface LevelFormat{
|
||||
* @param int $Z absolute Chunk Z value
|
||||
* @param bool $create Whether to generate the chunk if it does not exist
|
||||
*
|
||||
* @return ChunkSnapshot
|
||||
* @return Chunk
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function getSpawn();
|
||||
|
||||
public function getName();
|
||||
/**
|
||||
* @param Vector3 $pos
|
||||
*/
|
||||
public function setSpawn(Vector3 $pos);
|
||||
|
||||
/**
|
||||
* @return ChunkSnapshot
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getLoadedChunks();
|
||||
|
||||
/**
|
||||
* @return Level
|
||||
*/
|
||||
public function getLevel();
|
||||
|
||||
}
|
242
src/pocketmine/level/format/SimpleChunk.php
Normal file
242
src/pocketmine/level/format/SimpleChunk.php
Normal file
@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format;
|
||||
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class SimpleChunk{
|
||||
|
||||
const FLAG_GENERATED = 1;
|
||||
const FLAG_POPULATED = 2;
|
||||
|
||||
protected static $HEIGHT = 8;
|
||||
|
||||
/** @var string[] */
|
||||
protected $ids;
|
||||
/** @var string[] */
|
||||
protected $meta;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
|
||||
protected $flags = 0;
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param int $flags
|
||||
* @param string[] $ids
|
||||
* @param string[] $meta
|
||||
*/
|
||||
public function __construct($chunkX, $chunkZ, $flags = 0, array $ids = [], array $meta = []){
|
||||
$this->x = $chunkX;
|
||||
$this->z = $chunkZ;
|
||||
$this->flags = $flags;
|
||||
for($y = 0; $y < self::$HEIGHT; ++$y){
|
||||
$this->ids[$y] = isset($ids[$y]) ? $ids[$y] : str_repeat("\x00", 4096);
|
||||
$this->meta[$y] = isset($meta[$y]) ? $meta[$y] : str_repeat("\x00", 2048);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
*/
|
||||
public function setX($x){
|
||||
$this->x = $x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $z
|
||||
*/
|
||||
public function setZ($z){
|
||||
$this->z = $z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGenerated(){
|
||||
return ($this->flags & self::FLAG_GENERATED) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPopulated(){
|
||||
return ($this->flags & self::FLAG_POPULATED) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setGenerated($value = true){
|
||||
$this->flags = ($this->flags & ~self::FLAG_GENERATED) | ($value === true ? self::FLAG_GENERATED : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setPopulated($value = true){
|
||||
$this->flags = ($this->flags & ~self::FLAG_POPULATED) | ($value === true ? self::FLAG_POPULATED : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord(@$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $blockId 0-255
|
||||
*/
|
||||
public function setBlockId($x, $y, $z, $blockId){
|
||||
@$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x} = chr($blockId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData($x, $y, $z){
|
||||
$m = ord($this->meta[$y >> 4]{(($y & 0x0f) << 7) + ($z << 3) + ($x >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $m & 0x0F;
|
||||
}else{
|
||||
return $m >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
$i = (($y & 0x0f) << 7) + ($z << 3) + ($x >> 1);
|
||||
$old_m = ord($this->meta[$y >> 4]{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->meta[$y >> 4]{$i} = chr(($old_m & 0xf0) | ($data & 0x0f));
|
||||
}else{
|
||||
$this->meta[$y >> 4]{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $y 0-7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSectionIds($y){
|
||||
return $this->ids[$y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $y 0-7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSectionData($y){
|
||||
return $this->meta[$y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $y 0-7
|
||||
* @param string $ids
|
||||
* @param string $meta
|
||||
*/
|
||||
public function setSection($y, $ids = null, $meta = null){
|
||||
if($ids !== null){
|
||||
$this->ids[$y] = $ids;
|
||||
}
|
||||
|
||||
if($meta !== null){
|
||||
$this->meta[$y] = $meta;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toBinary(){
|
||||
$binary = Binary::writeInt($this->x) . Binary::writeInt($this->z) . chr($this->flags);
|
||||
if($this->isGenerated()){
|
||||
for($y = 0; $y < self::$HEIGHT; ++$y){
|
||||
$binary .= $this->getSectionIds($y);
|
||||
$binary .= $this->getSectionData($y);
|
||||
}
|
||||
}
|
||||
|
||||
return $binary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $binary
|
||||
*
|
||||
* @return SimpleChunk
|
||||
*/
|
||||
public static function fromBinary($binary){
|
||||
$offset = 0;
|
||||
$chunkX = Binary::readInt(substr($binary, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkZ = Binary::readInt(substr($binary, $offset, 4));
|
||||
$offset += 4;
|
||||
$flags = ord($binary{$offset++});
|
||||
$ids = [];
|
||||
$meta = [];
|
||||
if(($flags & self::FLAG_GENERATED) > 0){
|
||||
for($y = 0; $y < self::$HEIGHT; ++$y){
|
||||
$ids[$y] = substr($binary, $offset, 4096);
|
||||
$offset += 4096;
|
||||
$meta[$y] = substr($binary, $offset, 2048);
|
||||
$offset += 2048;
|
||||
}
|
||||
}
|
||||
return new SimpleChunk($chunkX, $chunkZ, $flags, $ids, $meta);
|
||||
}
|
||||
|
||||
}
|
@ -21,21 +21,123 @@
|
||||
|
||||
namespace pocketmine\level\format\anvil;
|
||||
|
||||
use pocketmine\level\format\LevelFormat;
|
||||
use pocketmine\level\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Anvil implements LevelFormat{
|
||||
protected $basePath;
|
||||
class Anvil extends BaseLevelProvider{
|
||||
|
||||
/** @var RegionLoader */
|
||||
protected $regions = [];
|
||||
|
||||
/** @var Chunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
public function __construct($path, $levelName){
|
||||
$this->basePath = realpath($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){
|
||||
$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;
|
||||
}
|
||||
}
|
@ -22,6 +22,8 @@
|
||||
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;
|
||||
@ -32,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){
|
||||
@ -70,6 +72,44 @@ 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){
|
||||
$blockId = "";
|
||||
$blockData = "";
|
||||
$blockSkyLight = "";
|
||||
$blockLight = "";
|
||||
$emptySections = [false, false, false, false, false, false, false, false];
|
||||
|
||||
$emptyBlocks = str_repeat("\x00", 4096);
|
||||
$emptyHalf = str_repeat("\x00", 2048);
|
||||
|
||||
foreach($this->sections as $i => $section){
|
||||
if($section instanceof EmptyChunkSection){
|
||||
$blockId .= $emptyBlocks;
|
||||
$blockData .= $emptyHalf;
|
||||
$blockSkyLight .= $emptyHalf;
|
||||
$blockLight .= $emptyHalf;
|
||||
$emptySections[$i] = true;
|
||||
}else{
|
||||
$blockId .= $section->getIdArray();
|
||||
$blockData .= $section->getDataArray();
|
||||
$blockSkyLight .= $section->getSkyLightArray();
|
||||
$blockLight .= $section->getLightArray();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: maxBlockY, biomeMap, biomeTemp
|
||||
|
||||
//TODO: time
|
||||
return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), 0/*$this->getLevel()->getTime()*/, $blockId, $blockData, $blockSkyLight, $blockLight, $emptySections, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Compound
|
||||
*/
|
||||
public function getNBT(){
|
||||
return $this->nbt;
|
||||
}
|
||||
}
|
@ -39,6 +39,10 @@ class ChunkSection implements \pocketmine\level\format\ChunkSection{
|
||||
$this->skyLight = (string) $nbt["SkyLight"];
|
||||
}
|
||||
|
||||
public function getY(){
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blocks{($y << 8) + ($z << 4) + $x});
|
||||
}
|
||||
@ -152,4 +156,20 @@ class ChunkSection implements \pocketmine\level\format\ChunkSection{
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getIdArray(){
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
public function getDataArray(){
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getSkyLightArray(){
|
||||
return $this->skyLight;
|
||||
}
|
||||
|
||||
public function getLightArray(){
|
||||
return $this->blockLight;
|
||||
}
|
||||
|
||||
}
|
78
src/pocketmine/level/format/anvil/ChunkSnapshot.php
Normal file
78
src/pocketmine/level/format/anvil/ChunkSnapshot.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\anvil;
|
||||
|
||||
use pocketmine\level\format\generic\BaseChunkSnapshot;
|
||||
|
||||
class ChunkSnapshot extends BaseChunkSnapshot{
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blockId{
|
||||
(($y >> 4) << 12) //get section index
|
||||
+ ($y << 8) + ($z << 4) + $x //get block index in section
|
||||
});
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
$data = ord($this->blockData{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $data & 0x0F;
|
||||
}else{
|
||||
return $data >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
$level = ord($this->skyLight{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $level & 0x0F;
|
||||
}else{
|
||||
return $level >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
$level = ord($this->light{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $level & 0x0F;
|
||||
}else{
|
||||
return $level >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBiome(){
|
||||
return 0; //TODO
|
||||
}
|
||||
|
||||
public function getHighestBlockAt($x, $z){
|
||||
return 127; //TODO
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\format\anvil;
|
||||
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\ByteArray;
|
||||
@ -29,6 +30,7 @@ use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class RegionLoader{
|
||||
@ -42,14 +44,15 @@ class RegionLoader{
|
||||
protected $filePath;
|
||||
protected $filePointer;
|
||||
protected $lastSector;
|
||||
/** @var LevelProvider */
|
||||
protected $levelProvider;
|
||||
protected $locationTable = [];
|
||||
|
||||
public function __construct($path, /*Level $level, */
|
||||
$regionX, $regionZ){
|
||||
public function __construct(LevelProvider $level, $regionX, $regionZ){
|
||||
$this->x = $regionX;
|
||||
$this->z = $regionZ;
|
||||
$this->filePath = /*$level->getPath()*/
|
||||
$path . "region/r.$regionX.$regionZ.mca";
|
||||
$this->levelProvider = $level;
|
||||
$this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.mca";
|
||||
touch($this->filePath);
|
||||
$this->filePointer = fopen($this->filePath, "r+b");
|
||||
flock($this->filePointer, LOCK_EX);
|
||||
@ -71,13 +74,17 @@ class RegionLoader{
|
||||
}
|
||||
}
|
||||
|
||||
protected function isChunkGenerated($index){
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
}
|
||||
|
||||
public function readChunk($x, $z, $generate = true){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($index < 0 or $index >= 4096){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0){
|
||||
if(!$this->isChunkGenerated($index)){
|
||||
if($generate === true){
|
||||
//Allocate space
|
||||
$this->locationTable[$index][0] = ++$this->lastSector;
|
||||
@ -116,8 +123,11 @@ class RegionLoader{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $chunk;
|
||||
//$chunk = new Chunk($level, $chunk);
|
||||
return new Chunk($this->levelProvider, $chunk);
|
||||
}
|
||||
|
||||
public function chunkExists($x, $z){
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
}
|
||||
|
||||
public function generateChunk($x, $z){
|
||||
@ -131,6 +141,8 @@ class RegionLoader{
|
||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
|
||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
//TODO: check type and name
|
||||
//$nbt->GrassMap = new IntArray("GrassMap", array_fill(0, 256, 127));
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
$nbt->Entities = new Enum("Entities", []);
|
||||
@ -139,6 +151,10 @@ class RegionLoader{
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
$this->saveChunk($x, $z, $nbt);
|
||||
}
|
||||
|
||||
protected function saveChunk($x, $z, Compound $nbt){
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$writer->setData(new Compound("", array($nbt)));
|
||||
$chunkData = $writer->writeCompressed(self::COMPRESSION_ZLIB, self::$COMPRESSION_LEVEL);
|
||||
@ -154,6 +170,47 @@ class RegionLoader{
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
}
|
||||
|
||||
public function writeChunk(Chunk $chunk){
|
||||
$nbt = $chunk->getNBT();
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
foreach($chunk->getSections() as $section){
|
||||
$nbt->Sections[$section->getY()] = new Compound(null, [
|
||||
"Y" => new Byte("Y", $section->getY()),
|
||||
"Blocks" => new ByteArray("Blocks", $section->getIdArray()),
|
||||
"Data" => new ByteArray("Data", $section->getDataArray()),
|
||||
"BlockLight" => new ByteArray("BlockLight", $section->getLightArray()),
|
||||
"SkyLight" => new ByteArray("SkyLight", $section->getSkyLightArray())
|
||||
]);
|
||||
}
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if(!($entity instanceof Player) and $entity->closed !== true){
|
||||
$entity->saveNBT();
|
||||
$entities[] = $entity->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("Entities", $entities);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
|
||||
$tiles = [];
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile->closed !== true){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("TileEntities", $tiles);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $nbt);
|
||||
}
|
||||
|
||||
protected static function getChunkOffset($x, $z){
|
||||
return $x + ($z << 5);
|
||||
}
|
||||
|
@ -21,27 +21,46 @@
|
||||
|
||||
namespace pocketmine\level\format\generic;
|
||||
|
||||
use pocketmine\entity\DroppedItem;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\ChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\tile\Chest;
|
||||
use pocketmine\tile\Furnace;
|
||||
use pocketmine\tile\Sign;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
abstract class BaseChunk implements Chunk{
|
||||
|
||||
/** @var ChunkSection[] */
|
||||
protected $sections = [];
|
||||
|
||||
/** @var Entity[] */
|
||||
protected $entities = [];
|
||||
|
||||
/** @var Tile[] */
|
||||
protected $tiles = [];
|
||||
|
||||
/** @var \WeakRef<LevelProvider> */
|
||||
protected $level;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
* @param LevelProvider $level
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param ChunkSection[] $sections
|
||||
* @param Compound[] $entities
|
||||
* @param Compound[] $tiles
|
||||
*/
|
||||
public function __construct(Level $level, $x, $z, array $sections){
|
||||
$this->level = $level;
|
||||
protected function __construct(LevelProvider $level, $x, $z, array $sections, array $entities = [], array $tiles = []){
|
||||
$this->level = new \WeakRef($level);
|
||||
$this->x = (int) $x;
|
||||
$this->z = (int) $z;
|
||||
foreach($sections as $Y => $section){
|
||||
@ -59,6 +78,44 @@ abstract class BaseChunk implements Chunk{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($entities as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($nbt->id instanceof String){ //New format
|
||||
switch($nbt["id"]){
|
||||
case "Item":
|
||||
(new DroppedItem($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
}
|
||||
}else{ //Old format
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach($tiles as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
switch($nbt["id"]){
|
||||
case Tile::CHEST:
|
||||
new Chest($this, $nbt);
|
||||
break;
|
||||
case Tile::FURNACE:
|
||||
new Furnace($this, $nbt);
|
||||
break;
|
||||
case Tile::SIGN:
|
||||
new Sign($this, $nbt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
@ -69,18 +126,37 @@ 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){
|
||||
$this->sections[$y >> 4]->getBlock($x, $y - ($y >> 4), $z, $blockId, $meta);
|
||||
return $this->sections[$y >> 4]->getBlock($x, $y - ($y >> 4), $z, $blockId, $meta);
|
||||
}
|
||||
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null){
|
||||
$this->sections[$y >> 4]->setBlock($x, $y - ($y >> 4), $z, $blockId, $meta);
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getBlockId($x, $y - ($y >> 4), $z);
|
||||
}
|
||||
|
||||
public function setBlockId($x, $y, $z, $id){
|
||||
$this->sections[$y >> 4]->setBlockId($x, $y - ($y >> 4), $z, $id);
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getBlockData($x, $y - ($y >> 4), $z);
|
||||
}
|
||||
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
$this->sections[$y >> 4]->setBlockData($x, $y - ($y >> 4), $z, $data);
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getBlockSkyLight($x, $y - ($y >> 4), $z);
|
||||
}
|
||||
@ -123,4 +199,62 @@ abstract class BaseChunk implements Chunk{
|
||||
public function setSection($fY, ChunkSection $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(){
|
||||
return $this->sections;
|
||||
}
|
||||
|
||||
}
|
64
src/pocketmine/level/format/generic/BaseChunkSnapshot.php
Normal file
64
src/pocketmine/level/format/generic/BaseChunkSnapshot.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\generic;
|
||||
|
||||
use pocketmine\level\format\ChunkSnapshot;
|
||||
|
||||
abstract class BaseChunkSnapshot implements ChunkSnapshot{
|
||||
|
||||
protected $blockId;
|
||||
protected $blockData;
|
||||
protected $skyLight;
|
||||
protected $light;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
protected $levelName;
|
||||
protected $levelTime;
|
||||
|
||||
public function __construct($x, $z, $levelName, $levelTime, $blockId, $blockData, $skyLight, $light, $heightMap, $biome, $biomeTemp, $biomeRain){
|
||||
$this->x = $x;
|
||||
$this->z = $z;
|
||||
$this->levelName = $levelName;
|
||||
$this->levelTime = $levelTime;
|
||||
$this->blockId = $blockId;
|
||||
$this->blockData = $blockData;
|
||||
$this->skyLight = $skyLight;
|
||||
$this->light = $light;
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
public function getLevelName(){
|
||||
return $this->levelName;
|
||||
}
|
||||
|
||||
public function getLevelTime(){
|
||||
return $this->levelTime;
|
||||
}
|
||||
}
|
103
src/pocketmine/level/format/generic/BaseLevelProvider.php
Normal file
103
src/pocketmine/level/format/generic/BaseLevelProvider.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\generic;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
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 Level */
|
||||
protected $level;
|
||||
/** @var string */
|
||||
protected $path;
|
||||
/** @var Compound */
|
||||
protected $levelData;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -39,6 +39,22 @@ class EmptyChunkSection implements ChunkSection{
|
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
public function getIdArray(){
|
||||
return str_repeat("\x00", 4096);
|
||||
}
|
||||
|
||||
public function getDataArray(){
|
||||
return str_repeat("\x00", 2048);
|
||||
}
|
||||
|
||||
public function getSkyLightArray(){
|
||||
return str_repeat("\x00", 2048);
|
||||
}
|
||||
|
||||
public function getLightArray(){
|
||||
return str_repeat("\x00", 2048);
|
||||
}
|
||||
|
||||
final public function setBlockId($x, $y, $z, $id){
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
@ -76,7 +85,7 @@ class Flat extends Generator{
|
||||
}*/
|
||||
}
|
||||
|
||||
public function parsePreset($preset){
|
||||
protected function parsePreset($preset){
|
||||
$this->preset = $preset;
|
||||
$preset = explode(";", $preset);
|
||||
$version = (int) $preset[0];
|
||||
@ -91,30 +100,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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,15 +148,16 @@ class Flat extends Generator{
|
||||
}
|
||||
}
|
||||
|
||||
public function init(Level $level, Random $random){
|
||||
public function init(GenerationChunkManager $level, Random $random){
|
||||
$this->level = $level;
|
||||
$this->random = $random;
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
for($Y = 0; $Y < 8; ++$Y){
|
||||
$this->level->setMiniChunk($chunkX, $chunkZ, $Y, $this->chunks[$Y]);
|
||||
}
|
||||
$chunk = clone $this->chunk;
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
$this->level->setChunk($chunkX, $chunkZ, $chunk);
|
||||
}
|
||||
|
||||
public function populateChunk($chunkX, $chunkZ){
|
||||
|
187
src/pocketmine/level/generator/GenerationChunkManager.php
Normal file
187
src/pocketmine/level/generator/GenerationChunkManager.php
Normal file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class GenerationChunkManager implements ChunkManager{
|
||||
|
||||
protected $levelID;
|
||||
|
||||
/** @var SimpleChunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
/** @var Generator */
|
||||
protected $generator;
|
||||
|
||||
/** @var GenerationManager */
|
||||
protected $manager;
|
||||
|
||||
protected $seed;
|
||||
|
||||
public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){
|
||||
if(!is_subclass_of($class, "pocketmine\\level\\generator\\Generator")){
|
||||
throw new \Exception("Class is not a subclass of Generator");
|
||||
}
|
||||
|
||||
$this->levelID = $levelID;
|
||||
$this->seed = $seed;
|
||||
$this->manager = $manager;
|
||||
|
||||
$this->generator = new $class($options);
|
||||
$this->generator->init($this, new Random($seed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSeed(){
|
||||
return $this->seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getID(){
|
||||
return $this->levelID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return SimpleChunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
return !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index];
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = new SimpleChunk($chunkX, $chunkZ, 0);
|
||||
$this->generator->generateChunk($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
public function populateChunk($chunkX, $chunkZ){
|
||||
if(!$this->isChunkGenerated($chunkX, $chunkZ)){
|
||||
$this->generateChunk($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
for($z = $chunkZ - 1; $z <= $chunkZ + 1; ++$z){
|
||||
for($x = $chunkX - 1; $x <= $chunkX + 1; ++$x){
|
||||
if(!$this->isChunkGenerated($x, $z)){
|
||||
$this->generateChunk($x, $z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->generator->populateChunk($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
return $this->getChunk($chunkX, $chunkZ)->isGenerated();
|
||||
}
|
||||
|
||||
public function isChunkPopulated($chunkX, $chunkZ){
|
||||
return $this->getChunk($chunkX, $chunkZ)->isPopulated();
|
||||
}
|
||||
|
||||
protected function requestChunk($chunkX, $chunkZ){
|
||||
$chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param SimpleChunk $chunk
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
if($chunk->isGenerated() and $chunk->isPopulated()){
|
||||
//TODO: Queue to be sent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockIdAt($x, $y, $z){
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockIdAt($x, $y, $z, $id){
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw block metadata
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockDataAt($x, $y, $z){
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw block metadata.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockDataAt($x, $y, $z, $data){
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f);
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
foreach($this->chunks as $chunk){
|
||||
//TODO: send generated chunks to be saved
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
257
src/pocketmine/level/generator/GenerationManager.php
Normal file
257
src/pocketmine/level/generator/GenerationManager.php
Normal file
@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class GenerationManager{
|
||||
|
||||
|
||||
/*
|
||||
* IPC protocol:
|
||||
* int32 (total length)
|
||||
* byte (packet id)
|
||||
* byte[] (length - 1 bytes)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Direction: Server->Thread
|
||||
* byte[] payload:
|
||||
* string root namespace
|
||||
* byte[] path
|
||||
*/
|
||||
const PACKET_ADD_NAMESPACE = 0x00;
|
||||
|
||||
/*
|
||||
* Direction: Both
|
||||
* If Server->Thread, request chunk generation
|
||||
* If Thread->Server, request chunk contents / loading
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
* int32 chunkX
|
||||
* int32 chunkZ
|
||||
*/
|
||||
const PACKET_REQUEST_CHUNK = 0x01;
|
||||
|
||||
/*
|
||||
* Direction: Both
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
* int32 chunkX
|
||||
* int32 chunkZ
|
||||
* byte flags (1 generated, 2 populated)
|
||||
* byte[] chunk (none if generated flag is not set)
|
||||
*/
|
||||
const PACKET_SEND_CHUNK = 0x02;
|
||||
|
||||
/*
|
||||
* Direction: Server->Thread
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
* int32 seed
|
||||
* string class that extends pocketmine\level\generator\Generator
|
||||
* byte[] serialized options array
|
||||
*/
|
||||
const PACKET_OPEN_LEVEL = 0x03;
|
||||
|
||||
/*
|
||||
* Direction: Server->Thread
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
*/
|
||||
const PACKET_CLOSE_LEVEL = 0x04;
|
||||
|
||||
/*
|
||||
* Direction: Server->Thread
|
||||
* no payload
|
||||
*/
|
||||
const PACKET_SHUTDOWN = 0xff;
|
||||
|
||||
|
||||
|
||||
protected $socket;
|
||||
/** @var \Logger */
|
||||
protected $logger;
|
||||
/** @var \SplAutoLoader */
|
||||
protected $loader;
|
||||
|
||||
/** @var GenerationChunkManager[] */
|
||||
protected $levels = [];
|
||||
|
||||
/** @var \SplQueue */
|
||||
protected $requestQueue;
|
||||
|
||||
protected $needsChunk = null;
|
||||
|
||||
protected $shutdown = false;
|
||||
|
||||
/**
|
||||
* @param resource $socket
|
||||
* @param \Logger $logger
|
||||
* @param \SplAutoloader $loader
|
||||
*/
|
||||
public function __construct($socket, \Logger $logger, \SplAutoloader $loader){
|
||||
$this->socket = $socket;
|
||||
$this->logger = $logger;
|
||||
$this->loader = $loader;
|
||||
$this->requestQueue = new \SplQueue();
|
||||
|
||||
while($this->shutdown !== true){
|
||||
if($this->requestQueue->count() > 0){
|
||||
$r = $this->requestQueue->dequeue();
|
||||
$levelID = $r[0];
|
||||
$chunkX = $r[1];
|
||||
$chunkZ = $r[2];
|
||||
$this->generateChunk($levelID, $chunkX, $chunkZ);
|
||||
|
||||
}
|
||||
$this->readPacket();
|
||||
}
|
||||
}
|
||||
|
||||
protected function openLevel($levelID, $seed, $class, array $options){
|
||||
if(!isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options);
|
||||
}
|
||||
}
|
||||
|
||||
protected function generateChunk($levelID, $chunkX, $chunkZ){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly
|
||||
//TODO: wait for queue generation (to wait for extra chunk changes)
|
||||
}
|
||||
}
|
||||
|
||||
protected function closeLevel($levelID){
|
||||
if(!isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->shutdown();
|
||||
unset($this->levels[$levelID]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function enqueueChunk($levelID, $chunkX, $chunkZ){
|
||||
$this->requestQueue->enqueue([$levelID, $chunkX, $chunkZ]);
|
||||
}
|
||||
|
||||
protected function receiveChunk($levelID, SimpleChunk $chunk){
|
||||
if($this->needsChunk !== null and $this->needsChunk[0] === $levelID){
|
||||
if($this->needsChunk[1] === $chunk->getX() and $this->needsChunk[2] === $chunk->getZ()){
|
||||
$this->needsChunk = $chunk;
|
||||
}
|
||||
}
|
||||
//TODO: set new received chunks
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $levelID
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return SimpleChunk
|
||||
*/
|
||||
public function requestChunk($levelID, $chunkX, $chunkZ){
|
||||
$this->needsChunk = [$levelID, $chunkX, $chunkZ];
|
||||
$binary = chr(self::PACKET_REQUEST_CHUNK . Binary::writeInt($levelID) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ));
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
|
||||
do{
|
||||
$this->readPacket();
|
||||
}while($this->shutdown !== true and !($this->needsChunk instanceof SimpleChunk));
|
||||
|
||||
$chunk = $this->needsChunk;
|
||||
$this->needsChunk = null;
|
||||
if($chunk instanceof SimpleChunk){
|
||||
return $chunk;
|
||||
}else{
|
||||
return new SimpleChunk($chunkX, $chunkZ, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function sendChunk($levelID, SimpleChunk $chunk){
|
||||
$binary = chr(self::PACKET_SEND_CHUNK . Binary::writeInt($levelID) . $chunk->toBinary());
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
|
||||
}
|
||||
|
||||
protected function readPacket(){
|
||||
$len = socket_read($this->socket, 4);
|
||||
if($len === false or $len === ""){
|
||||
usleep(5000);
|
||||
return;
|
||||
}
|
||||
$packet = socket_read($this->socket, Binary::readInt($len));
|
||||
$pid = ord($packet{0});
|
||||
$offset = 1;
|
||||
if($pid === self::PACKET_REQUEST_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkX = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkZ = Binary::readInt(substr($packet, $offset, 4));
|
||||
$this->enqueueChunk($levelID, $chunkX, $chunkZ);
|
||||
|
||||
}elseif($pid === self::PACKET_SEND_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunk = SimpleChunk::fromBinary(substr($packet, $offset));
|
||||
$this->receiveChunk($levelID, $chunk);
|
||||
|
||||
}elseif($pid === self::PACKET_OPEN_LEVEL){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$seed = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$len = Binary::readShort(substr($packet, $offset, 2));
|
||||
$offset += 2;
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$options = unserialize(substr($packet, $offset));
|
||||
$this->openLevel($levelID, $seed, $class, $options);
|
||||
|
||||
}elseif($pid === self::PACKET_CLOSE_LEVEL){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$this->closeLevel($levelID);
|
||||
}elseif($pid === self::PACKET_ADD_NAMESPACE){
|
||||
$len = Binary::readShort(substr($packet, $offset, 2));
|
||||
$offset += 2;
|
||||
$namespace = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$path = substr($packet, $offset);
|
||||
$this->loader->add($namespace, [$path]);
|
||||
}elseif($pid === self::PACKET_SHUTDOWN){
|
||||
foreach($this->levels as $level){
|
||||
$level->shutdown();
|
||||
}
|
||||
$this->levels = [];
|
||||
|
||||
$this->shutdown = true;
|
||||
socket_close($this->socket);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Logger
|
||||
*/
|
||||
public function getLogger(){
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
}
|
96
src/pocketmine/level/generator/GenerationThread.php
Normal file
96
src/pocketmine/level/generator/GenerationThread.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
|
||||
class GenerationThread extends \Thread{
|
||||
|
||||
protected $loadPaths;
|
||||
/** @var \SplAutoloader */
|
||||
protected $loader;
|
||||
/** @var \ThreadedLogger */
|
||||
protected $logger;
|
||||
|
||||
protected $externalSocket;
|
||||
protected $internalSocket;
|
||||
|
||||
public function getExternalSocket(){
|
||||
return $this->externalSocket;
|
||||
}
|
||||
|
||||
public function getInternalSocket(){
|
||||
return $this->internalSocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ThreadedLogger
|
||||
*/
|
||||
public function getLogger(){
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
public function __construct(\ThreadedLogger $logger, \SplAutoloader $loader){
|
||||
$this->loader = $loader;
|
||||
$loadPaths = [];
|
||||
$this->addDependency($loadPaths, new \ReflectionClass($this->logger));
|
||||
$this->addDependency($loadPaths, new \ReflectionClass($this->loader));
|
||||
$this->loadPaths = array_reverse($loadPaths);
|
||||
|
||||
$sockets = [];
|
||||
if(!socket_create_pair((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? AF_INET : AF_UNIX), SOCK_STREAM, 0, $sockets)){
|
||||
throw new \Exception("Could not create IPC sockets. Reason: ".socket_strerror(socket_last_error()));
|
||||
}
|
||||
|
||||
$this->internalSocket = $sockets[0];
|
||||
socket_set_block($this->internalSocket); //IMPORTANT!
|
||||
$this->externalSocket = $sockets[1];
|
||||
socket_set_nonblock($this->externalSocket);
|
||||
|
||||
$this->start(PTHREADS_INHERIT_ALL & ~PTHREADS_INHERIT_CLASSES);
|
||||
}
|
||||
|
||||
protected function addDependency(array &$loadPaths, \ReflectionClass $dep){
|
||||
if($dep->getFileName() !== false){
|
||||
$loadPaths[$dep->getName()] = $dep->getFileName();
|
||||
}
|
||||
|
||||
if($dep->getParentClass() instanceof \ReflectionClass){
|
||||
$this->addDependency($loadPaths, $dep->getParentClass());
|
||||
}
|
||||
|
||||
foreach($dep->getInterfaces() as $interface){
|
||||
$this->addDependency($loadPaths, $interface);
|
||||
}
|
||||
}
|
||||
|
||||
public function run(){
|
||||
//Load removed dependencies, can't use require_once()
|
||||
foreach($this->loadPaths as $name => $path){
|
||||
if(!class_exists($name, false) and !class_exists($name, false)){
|
||||
require($path);
|
||||
}
|
||||
}
|
||||
$this->loader->register();
|
||||
|
||||
$generationManager = new GenerationManager($this->getInternalSocket(), $this->getLogger(), $this->loader);
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ abstract class Generator{
|
||||
|
||||
public abstract function __construct(array $settings = []);
|
||||
|
||||
public abstract function init(Level $level, Random $random);
|
||||
public abstract function init(GenerationChunkManager $level, Random $random);
|
||||
|
||||
public abstract function generateChunk($chunkX, $chunkZ);
|
||||
|
||||
|
@ -37,11 +37,15 @@ use pocketmine\level\generator\populator\Tree;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
use pocketmine\level\generator\populator\Populator;
|
||||
|
||||
class Normal extends Generator{
|
||||
|
||||
/** @var Populator[] */
|
||||
private $populators = [];
|
||||
/** @var GenerationChunkManager */
|
||||
private $level;
|
||||
/** @var Random */
|
||||
private $random;
|
||||
private $worldHeight = 65;
|
||||
private $waterHeight = 63;
|
||||
@ -62,7 +66,7 @@ class Normal extends Generator{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function init(Level $level, Random $random){
|
||||
public function init(GenerationChunkManager $level, Random $random){
|
||||
$this->level = $level;
|
||||
$this->random = $random;
|
||||
$this->random->setSeed($this->level->getSeed());
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
|
||||
@ -39,11 +40,11 @@ class BigTree extends Tree{
|
||||
private $addLogVines = false;
|
||||
private $addCocoaPlants = false;
|
||||
|
||||
public function canPlaceObject(Level $level, Vector3 $pos){
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function placeObject(Level $level, Vector3 $pos, $type){
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, $type){
|
||||
|
||||
$this->trunkHeight = (int) ($this->totalHeight * $this->trunkHeightMultiplier);
|
||||
$leaves = $this->getLeafGroupPoints($level, $pos);
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\math\VectorMath;
|
||||
@ -39,20 +40,20 @@ class Ore{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function canPlaceObject(Level $level, $x, $y, $z){
|
||||
return ($level->level->getBlockID($x, $y, $z) !== 0);
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z){
|
||||
return ($level->getBlockIdAt($x, $y, $z) === 1);
|
||||
}
|
||||
|
||||
public function placeObject(Level $level, Vector3 $pos){
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z){
|
||||
$clusterSize = (int) $this->type->clusterSize;
|
||||
$angle = $this->random->nextFloat() * M_PI;
|
||||
$offset = VectorMath::getDirection2D($angle)->multiply($clusterSize)->divide(8);
|
||||
$x1 = $pos->x + 8 + $offset->x;
|
||||
$x2 = $pos->x + 8 - $offset->x;
|
||||
$z1 = $pos->z + 8 + $offset->y;
|
||||
$z2 = $pos->z + 8 - $offset->y;
|
||||
$y1 = $pos->y + $this->random->nextRange(0, 3) + 2;
|
||||
$y2 = $pos->y + $this->random->nextRange(0, 3) + 2;
|
||||
$x1 = $x + 8 + $offset->x;
|
||||
$x2 = $x + 8 - $offset->x;
|
||||
$z1 = $z + 8 + $offset->y;
|
||||
$z2 = $z + 8 - $offset->y;
|
||||
$y1 = $y + $this->random->nextRange(0, 3) + 2;
|
||||
$y2 = $y + $this->random->nextRange(0, 3) + 2;
|
||||
for($count = 0; $count <= $clusterSize; ++$count){
|
||||
$seedX = $x1 + ($x2 - $x1) * $count / $clusterSize;
|
||||
$seedY = $y1 + ($y2 - $y1) * $count / $clusterSize;
|
||||
@ -80,8 +81,8 @@ class Ore{
|
||||
$sizeZ = ($z + 0.5 - $seedZ) / $size;
|
||||
$sizeZ *= $sizeZ;
|
||||
|
||||
if(($sizeX + $sizeY + $sizeZ) < 1 and $level->level->getBlockID($x, $y, $z) === 1){
|
||||
$level->setBlockRaw(new Vector3($x, $y, $z), $this->type->material);
|
||||
if(($sizeX + $sizeY + $sizeZ) < 1 and $level->getBlockIdAt($x, $y, $z) === 1){
|
||||
$level->setBlockIdAt($x, $y, $z, $this->type->material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object;
|
||||
use pocketmine\block\Dirt;
|
||||
use pocketmine\block\Leaves;
|
||||
use pocketmine\block\Wood;
|
||||
use pocketmine\item\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
@ -34,7 +36,7 @@ class PineTree extends Tree{
|
||||
private $leavesSizeY = -1;
|
||||
private $leavesAbsoluteMaxRadius = -1;
|
||||
|
||||
public function canPlaceObject(Level $level, Vector3 $pos, Random $random){
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$this->findRandomLeavesSize($random);
|
||||
$checkRadius = 0;
|
||||
for($yy = 0; $yy < $this->totalHeight; ++$yy){
|
||||
@ -43,7 +45,7 @@ class PineTree extends Tree{
|
||||
}
|
||||
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
|
||||
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){
|
||||
if(!isset($this->overridable[$level->level->getBlockID($pos->x + $xx, $pos->y + $yy, $pos->z + $zz)])){
|
||||
if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -59,11 +61,11 @@ class PineTree extends Tree{
|
||||
$this->leavesAbsoluteMaxRadius = 2 + $random->nextRange(0, 1);
|
||||
}
|
||||
|
||||
public function placeObject(Level $level, Vector3 $pos, Random $random){
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
if($this->leavesSizeY === -1 or $this->leavesAbsoluteMaxRadius === -1){
|
||||
$this->findRandomLeavesSize($random);
|
||||
}
|
||||
$level->setBlockRaw(new Vector3($pos->x, $pos->y - 1, $pos->z), new Dirt());
|
||||
$level->setBlockIdAt($x, $y - 1, $z, Block::DIRT);
|
||||
$leavesRadius = 0;
|
||||
$leavesMaxRadius = 1;
|
||||
$leavesBottomY = $this->totalHeight - $this->leavesSizeY;
|
||||
@ -73,7 +75,8 @@ class PineTree extends Tree{
|
||||
for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){
|
||||
for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){
|
||||
if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){
|
||||
$level->setBlockRaw(new Vector3($pos->x + $xx, $pos->y + $yy, $pos->z + $zz), new Leaves($this->type));
|
||||
$level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
|
||||
$level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,7 +92,8 @@ class PineTree extends Tree{
|
||||
}
|
||||
$trunkHeightReducer = $random->nextRange(0, 3);
|
||||
for($yy = 0; $yy < ($this->totalHeight - $trunkHeightReducer); ++$yy){
|
||||
$level->setBlockRaw(new Vector3($pos->x, $pos->y + $yy, $pos->z), new Wood($this->type));
|
||||
$level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK);
|
||||
$level->setBlockDataAt($x, $y + $yy, $z, $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
@ -35,10 +36,10 @@ class Pond{
|
||||
$this->random = $random;
|
||||
}
|
||||
|
||||
public function canPlaceObject(Level $level, Vector3 $pos){
|
||||
public function canPlaceObject(ChunkManager $level, Vector3 $pos){
|
||||
}
|
||||
|
||||
public function placeObject(Level $level, Vector3 $pos){
|
||||
public function placeObject(ChunkManager $level, Vector3 $pos){
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object;
|
||||
use pocketmine\block\Dirt;
|
||||
use pocketmine\block\Leaves;
|
||||
use pocketmine\block\Wood;
|
||||
use pocketmine\item\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
@ -38,7 +40,7 @@ class SmallTree extends Tree{
|
||||
private $addLogVines = false;
|
||||
private $addCocoaPlants = false;
|
||||
|
||||
public function canPlaceObject(Level $level, Vector3 $pos, Random $random){
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$radiusToCheck = 0;
|
||||
for($yy = 0; $yy < $this->trunkHeight + 3; ++$yy){
|
||||
if($yy == 1 or $yy === $this->trunkHeight){
|
||||
@ -46,7 +48,7 @@ class SmallTree extends Tree{
|
||||
}
|
||||
for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){
|
||||
for($zz = -$radiusToCheck; $zz < ($radiusToCheck + 1); ++$zz){
|
||||
if(!isset($this->overridable[$level->level->getBlockID($pos->x + $xx, $pos->y + $yy, $pos->z + $zz)])){
|
||||
if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -56,10 +58,9 @@ class SmallTree extends Tree{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function placeObject(Level $level, Vector3 $pos, Random $random){
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
// The base dirt block
|
||||
$dirtpos = new Vector3($pos->x, $pos->y - 1, $pos->z);
|
||||
$level->setBlockRaw($dirtpos, new Dirt());
|
||||
$level->setBlockIdAt($x, $y, $z, Block::DIRT);
|
||||
|
||||
// Adjust the tree trunk's height randomly
|
||||
// plot [-14:11] int( x / 8 ) + 5
|
||||
@ -84,10 +85,8 @@ class SmallTree extends Tree{
|
||||
for($xx = -$bRadius; $xx <= $bRadius; ++$xx){
|
||||
for($zz = -$bRadius; $zz <= $bRadius; ++$zz){
|
||||
if(sqrt(($xx * $xx) + ($zz * $zz)) <= $radius){
|
||||
$leafpos = new Vector3($pos->x + $xx,
|
||||
$pos->y + $yy,
|
||||
$pos->z + $zz);
|
||||
$level->setBlockRaw($leafpos, new Leaves($this->type));
|
||||
$level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
|
||||
$level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,8 +95,8 @@ class SmallTree extends Tree{
|
||||
|
||||
// Place the trunk last
|
||||
if($leaflevel > 1){
|
||||
$trunkpos = new Vector3($pos->x, $pos->y + $yy, $pos->z);
|
||||
$level->setBlockRaw($trunkpos, new Wood($this->type));
|
||||
$level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK);
|
||||
$level->setBlockDataAt($x, $y + $yy, $z, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ namespace pocketmine\level\generator\object;
|
||||
use pocketmine\block\Dirt;
|
||||
use pocketmine\block\Leaves;
|
||||
use pocketmine\block\Wood;
|
||||
use pocketmine\item\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
@ -34,7 +36,7 @@ class SpruceTree extends Tree{
|
||||
private $leavesBottomY = -1;
|
||||
private $leavesMaxRadius = -1;
|
||||
|
||||
public function canPlaceObject(Level $level, Vector3 $pos, Random $random){
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$this->findRandomLeavesSize($random);
|
||||
$checkRadius = 0;
|
||||
for($yy = 0; $yy < $this->totalHeight + 2; ++$yy){
|
||||
@ -43,7 +45,7 @@ class SpruceTree extends Tree{
|
||||
}
|
||||
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
|
||||
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){
|
||||
if(!isset($this->overridable[$level->level->getBlockID($pos->x + $xx, $pos->y + $yy, $pos->z + $zz)])){
|
||||
if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -59,28 +61,30 @@ class SpruceTree extends Tree{
|
||||
$this->leavesMaxRadius = 1 + $random->nextRange(0, 1);
|
||||
}
|
||||
|
||||
public function placeObject(Level $level, Vector3 $pos, Random $random){
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
if($this->leavesBottomY === -1 or $this->leavesMaxRadius === -1){
|
||||
$this->findRandomLeavesSize($random);
|
||||
}
|
||||
$level->setBlockRaw(new Vector3($pos->x, $pos->y - 1, $pos->z), new Dirt());
|
||||
$level->setBlockIdAt($x, $y - 1, $z, Block::DIRT);
|
||||
$leavesRadius = 0;
|
||||
for($yy = $this->totalHeight; $yy >= $this->leavesBottomY; --$yy){
|
||||
for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){
|
||||
for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){
|
||||
if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){
|
||||
$level->setBlockRaw(new Vector3($pos->x + $xx, $pos->y + $yy, $pos->z + $zz), new Leaves($this->type));
|
||||
$level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
|
||||
$level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if($leavesRadius > 0 and $yy === ($pos->y + $this->leavesBottomY + 1)){
|
||||
if($leavesRadius > 0 and $yy === ($y + $this->leavesBottomY + 1)){
|
||||
--$leavesRadius;
|
||||
}elseif($leavesRadius < $this->leavesMaxRadius){
|
||||
++$leavesRadius;
|
||||
}
|
||||
}
|
||||
for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){
|
||||
$level->setBlockRaw(new Vector3($pos->x, $pos->y + $yy, $pos->z), new Wood($this->type));
|
||||
$level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK);
|
||||
$level->setBlockDataAt($x, $y + $yy, $z, $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,27 +22,29 @@
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class TallGrass{
|
||||
public static function growGrass(Level $level, Vector3 $pos, Random $random, $count = 15, $radius = 10){
|
||||
$arr = array(
|
||||
Block::get(Block::DANDELION, 0),
|
||||
Block::get(Block::CYAN_FLOWER, 0),
|
||||
Block::get(Block::TALL_GRASS, 1),
|
||||
Block::get(Block::TALL_GRASS, 1),
|
||||
Block::get(Block::TALL_GRASS, 1),
|
||||
Block::get(Block::TALL_GRASS, 1)
|
||||
);
|
||||
public static function growGrass(ChunkManager $level, Vector3 $pos, Random $random, $count = 15, $radius = 10){
|
||||
$arr = [
|
||||
[Block::DANDELION, 0],
|
||||
[Block::CYAN_FLOWER, 0],
|
||||
[Block::TALL_GRASS, 1],
|
||||
[Block::TALL_GRASS, 1],
|
||||
[Block::TALL_GRASS, 1],
|
||||
[Block::TALL_GRASS, 1]
|
||||
];
|
||||
$arrC = count($arr) - 1;
|
||||
for($c = 0; $c < $count; ++$c){
|
||||
$x = $random->nextRange($pos->x - $radius, $pos->x + $radius);
|
||||
$z = $random->nextRange($pos->z - $radius, $pos->z + $radius);
|
||||
if($level->level->getBlockID($x, $pos->y + 1, $z) === Block::AIR and $level->level->getBlockID($x, $pos->y, $z) === Block::GRASS){
|
||||
if($level->getBlockIdAt($x, $pos->y + 1, $z) === Block::AIR and $level->getBlockIdAt($x, $pos->y, $z) === Block::GRASS){
|
||||
$t = $arr[$random->nextRange(0, $arrC)];
|
||||
$level->setBlockRaw(new Vector3($x, $pos->y + 1, $z), $t);
|
||||
$level->setBlockIdAt($x, $pos->y + 1, $z, $t[0]);
|
||||
$level->setBlockDataAt($x, $pos->y + 1, $z, $t[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Sapling;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
@ -36,7 +37,7 @@ class Tree{
|
||||
18 => true,
|
||||
);
|
||||
|
||||
public static function growTree(Level $level, Vector3 $pos, Random $random, $type = 0){
|
||||
public static function growTree(ChunkManager $level, $x, $y, $z, Random $random, $type = 0){
|
||||
switch($type & 0x03){
|
||||
case Sapling::SPRUCE:
|
||||
if($random->nextRange(0, 1) === 1){
|
||||
@ -62,8 +63,8 @@ class Tree{
|
||||
//}
|
||||
break;
|
||||
}
|
||||
if($tree->canPlaceObject($level, $pos, $random)){
|
||||
$tree->placeObject($level, $pos, $random);
|
||||
if($tree->canPlaceObject($level, $x, $y, $z, $random)){
|
||||
$tree->placeObject($level, $x, $y, $z, $random);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\generator\populator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
@ -31,7 +32,7 @@ class Mineshaft extends Populator{
|
||||
private static $BASE_Y = 35;
|
||||
private static $RAND_Y = 11;
|
||||
|
||||
public function populate(Level $level, $chunkX, $chunkZ, Random $random){
|
||||
public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
|
||||
if($random->nextRange(0, self::$ODD) === 0){
|
||||
//$mineshaft = new Mineshaft($random);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\generator\populator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\generator\object\Ore as ObjectOre;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
@ -29,7 +30,7 @@ use pocketmine\utils\Random;
|
||||
class Ore extends Populator{
|
||||
private $oreTypes = [];
|
||||
|
||||
public function populate(Level $level, $chunkX, $chunkZ, Random $random){
|
||||
public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
|
||||
foreach($this->oreTypes as $type){
|
||||
$ore = new ObjectOre($random, $type);
|
||||
for($i = 0; $i < $ore->type->clusterCount; ++$i){
|
||||
@ -37,7 +38,7 @@ class Ore extends Populator{
|
||||
$y = $random->nextRange($ore->type->minHeight, $ore->type->maxHeight);
|
||||
$z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15);
|
||||
if($ore->canPlaceObject($level, $x, $y, $z)){
|
||||
$ore->placeObject($level, new Vector3($x, $y, $z));
|
||||
$ore->placeObject($level, $x, $y, $z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\level\generator\populator;
|
||||
|
||||
use pocketmine\block\Water;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
@ -31,16 +32,14 @@ class Pond extends Populator{
|
||||
private $lavaOdd = 4;
|
||||
private $lavaSurfaceOdd = 4;
|
||||
|
||||
public function populate(Level $level, $chunkX, $chunkZ, Random $random){
|
||||
public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
|
||||
if($random->nextRange(0, $this->waterOdd) === 0){
|
||||
$v = new Vector3(
|
||||
$random->nextRange($chunkX << 4, ($chunkX << 4) + 16),
|
||||
$random->nextRange(0, 128),
|
||||
$random->nextRange($chunkZ << 4, ($chunkZ << 4) + 16)
|
||||
);
|
||||
$x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 16);
|
||||
$y = $random->nextRange(0, 128);
|
||||
$z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 16);
|
||||
$pond = new \pocketmine\level\generator\object\Pond($random, new Water());
|
||||
if($pond->canPlaceObject($level, $v)){
|
||||
$pond->placeObject($level, $v);
|
||||
if($pond->canPlaceObject($level, $x, $y, $z)){
|
||||
$pond->placeObject($level, $x, $y, $z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,10 @@
|
||||
*/
|
||||
namespace pocketmine\level\generator\populator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
abstract class Populator{
|
||||
public abstract function populate(Level $level, $chunkX, $chunkZ, Random $random);
|
||||
public abstract function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random);
|
||||
}
|
@ -23,12 +23,13 @@ namespace pocketmine\level\generator\populator;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\TallGrass as BlockTallGrass;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class TallGrass extends Populator{
|
||||
/** @var Level */
|
||||
/** @var ChunkManager */
|
||||
private $level;
|
||||
private $randomAmount;
|
||||
private $baseAmount;
|
||||
@ -41,7 +42,7 @@ class TallGrass extends Populator{
|
||||
$this->baseAmount = $amount;
|
||||
}
|
||||
|
||||
public function populate(Level $level, $chunkX, $chunkZ, Random $random){
|
||||
public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
|
||||
$this->level = $level;
|
||||
$amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount;
|
||||
for($i = 0; $i < $amount; ++$i){
|
||||
@ -51,22 +52,23 @@ class TallGrass extends Populator{
|
||||
$xx = $x - 7 + $random->nextRange(0, 15);
|
||||
$zz = $z - 7 + $random->nextRange(0, 15);
|
||||
$yy = $this->getHighestWorkableBlock($xx, $zz);
|
||||
$vector = new Vector3($xx, $yy, $zz);
|
||||
if($yy !== -1 and $this->canTallGrassStay($this->level->getBlockRaw($vector))){
|
||||
$this->level->setBlockRaw($vector, new BlockTallGrass(1));
|
||||
|
||||
if($yy !== -1 and $this->canTallGrassStay($xx, $yy, $zz)){
|
||||
$this->level->setBlockIdAt($xx, $yy, $zz, Block::TALL_GRASS);
|
||||
$this->level->setBlockDataAt($xx, $yy, $zz, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function canTallGrassStay(Block $block){
|
||||
return $block->getID() === Block::AIR and $block->getSide(0)->getID() === Block::GRASS;
|
||||
private function canTallGrassStay($x, $y, $z){
|
||||
return $this->level->getBlockIdAt($x, $y, $z) === Block::AIR and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::GRASS;
|
||||
}
|
||||
|
||||
private function getHighestWorkableBlock($x, $z){
|
||||
for($y = 128; $y > 0; --$y){
|
||||
$b = $this->level->getBlockRaw(new Vector3($x, $y, $z));
|
||||
if($b->getID() === Block::AIR or $b->getID() === Block::LEAVES){
|
||||
$b = $this->level->getBlockIdAt($x, $y, $z);
|
||||
if($b === Block::AIR or $b === Block::LEAVES){
|
||||
if(--$y <= 0){
|
||||
return -1;
|
||||
}
|
||||
|
@ -23,12 +23,14 @@ namespace pocketmine\level\generator\populator;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Sapling;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\generator\object\Tree as ObjectTree;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class Tree extends Populator{
|
||||
/** @var ChunkManager */
|
||||
private $level;
|
||||
private $randomAmount;
|
||||
private $baseAmount;
|
||||
@ -41,7 +43,7 @@ class Tree extends Populator{
|
||||
$this->baseAmount = $amount;
|
||||
}
|
||||
|
||||
public function populate(Level $level, $chunkX, $chunkZ, Random $random){
|
||||
public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
|
||||
$this->level = $level;
|
||||
$amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount;
|
||||
for($i = 0; $i < $amount; ++$i){
|
||||
@ -56,14 +58,14 @@ class Tree extends Populator{
|
||||
}else{
|
||||
$meta = Sapling::OAK;
|
||||
}
|
||||
ObjectTree::growTree($this->level, new Vector3($x, $y, $z), $random, $meta);
|
||||
ObjectTree::growTree($this->level, $x, $y, $z, $random, $meta);
|
||||
}
|
||||
}
|
||||
|
||||
private function getHighestWorkableBlock($x, $z){
|
||||
for($y = 128; $y > 0; --$y){
|
||||
$b = $this->level->getBlockRaw(new Vector3($x, $y, $z));
|
||||
if($b->getID() !== Block::DIRT and $b->getID() !== Block::GRASS){
|
||||
$b = $this->level->getBlockIdAt($x, $y, $z);
|
||||
if($b !== Block::DIRT and $b !== Block::GRASS){
|
||||
if(--$y <= 0){
|
||||
return -1;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ use pocketmine\inventory\ChestInventory;
|
||||
use pocketmine\inventory\DoubleChestInventory;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\nbt\NBT;
|
||||
@ -44,9 +45,9 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
/** @var DoubleChestInventory */
|
||||
protected $doubleInventory = null;
|
||||
|
||||
public function __construct(Level $level, Compound $nbt){
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::CHEST;
|
||||
parent::__construct($level, $nbt);
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->inventory = new ChestInventory($this);
|
||||
for($i = 0; $i < $this->getSize(); ++$i){
|
||||
$this->inventory->setItem($i, $this->getItem($i));
|
||||
|
@ -26,9 +26,22 @@ use pocketmine\item\Item;
|
||||
use pocketmine\Network;
|
||||
|
||||
interface Container{
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
*
|
||||
* @return Item
|
||||
*/
|
||||
public function getItem($index);
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
* @param Item $item
|
||||
*/
|
||||
public function setItem($index, Item $item);
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSize();
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ use pocketmine\inventory\FurnaceInventory;
|
||||
use pocketmine\inventory\FurnaceRecipe;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
@ -37,9 +38,9 @@ class Furnace extends Tile implements InventoryHolder, Container{
|
||||
/** @var FurnaceInventory */
|
||||
protected $inventory;
|
||||
|
||||
public function __construct(Level $level, Compound $nbt){
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::FURNACE;
|
||||
parent::__construct($level, $nbt);
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->inventory = new FurnaceInventory($this);
|
||||
for($i = 0; $i < $this->getSize(); ++$i){
|
||||
$this->inventory->setItem($i, $this->getItem($i));
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
@ -31,9 +32,9 @@ use pocketmine\Player;
|
||||
|
||||
class Sign extends Spawnable{
|
||||
|
||||
public function __construct(Level $level, Compound $nbt){
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::SIGN;
|
||||
parent::__construct($level, $nbt);
|
||||
parent::__construct($chunk, $nbt);
|
||||
}
|
||||
|
||||
public function setText($line1 = "", $line2 = "", $line3 = "", $line4 = ""){
|
||||
@ -42,7 +43,6 @@ class Sign extends Spawnable{
|
||||
$this->namedtag->Text3 = $line3;
|
||||
$this->namedtag->Text4 = $line4;
|
||||
$this->spawnToAll();
|
||||
$this->server->handle("tile.update", $this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\Player;
|
||||
@ -28,8 +29,8 @@ use pocketmine\Player;
|
||||
abstract class Spawnable extends Tile{
|
||||
public abstract function spawnTo(Player $player);
|
||||
|
||||
public function __construct(Level $level, Compound $nbt){
|
||||
parent::__construct($level, $nbt);
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->spawnToAll();
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,10 @@
|
||||
*/
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\pmf\LevelFormat;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\Server;
|
||||
|
||||
abstract class Tile extends Position{
|
||||
const SIGN = "Sign";
|
||||
@ -45,7 +44,8 @@ abstract class Tile extends Position{
|
||||
*/
|
||||
public static $needUpdate = [];
|
||||
|
||||
public $chunkIndex;
|
||||
/** @var Chunk */
|
||||
public $chunk;
|
||||
public $name;
|
||||
public $id;
|
||||
public $x;
|
||||
@ -63,9 +63,10 @@ abstract class Tile extends Position{
|
||||
}
|
||||
|
||||
|
||||
public function __construct(Level $level, Compound $nbt){
|
||||
$this->server = Server::getInstance();
|
||||
$this->setLevel($level, true); //Strong reference
|
||||
public function __construct(Chunk $chunk, Compound $nbt){
|
||||
$this->server = $chunk->getLevel()->getLevel()->getServer();
|
||||
$this->chunk = $chunk;
|
||||
$this->setLevel($chunk->getLevel()->getLevel(), true); //Strong reference
|
||||
$this->namedtag = $nbt;
|
||||
$this->closed = false;
|
||||
$this->name = "";
|
||||
@ -75,10 +76,8 @@ abstract class Tile extends Position{
|
||||
$this->y = (int) $this->namedtag["y"];
|
||||
$this->z = (int) $this->namedtag["z"];
|
||||
|
||||
$index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4);
|
||||
$this->chunkIndex = $index;
|
||||
$this->chunk->addTile($this);
|
||||
$this->getLevel()->addTile($this);
|
||||
$this->getLevel()->chunkTiles[$this->chunkIndex][$this->id] = $this;
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
@ -100,7 +99,7 @@ abstract class Tile extends Position{
|
||||
$this->closed = true;
|
||||
unset(Tile::$needUpdate[$this->id]);
|
||||
$this->getLevel()->removeTile($this);
|
||||
unset($this->getLevel()->chunkTiles[$this->chunkIndex][$this->id]);
|
||||
$this->chunk->removeTile($this);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user