Merge branch 'master' into mcpe-1.2.5

This commit is contained in:
Dylan K. Taylor 2017-11-14 20:06:00 +00:00
commit 73cd195e76
29 changed files with 423 additions and 424 deletions

7
.gitmodules vendored
View File

@ -1,10 +1,3 @@
[submodule "src/raklib"]
path = src/raklib
url = https://github.com/pmmp/RakLib.git
branch = master
[submodule "src/spl"]
path = src/spl
url = https://github.com/pmmp/PocketMine-SPL.git
[submodule "src/pocketmine/lang/locale"] [submodule "src/pocketmine/lang/locale"]
path = src/pocketmine/lang/locale path = src/pocketmine/lang/locale
url = https://github.com/pmmp/PocketMine-Language.git url = https://github.com/pmmp/PocketMine-Language.git

View File

@ -5,7 +5,7 @@
"homepage": "https://pmmp.io", "homepage": "https://pmmp.io",
"license": "LGPL-3.0", "license": "LGPL-3.0",
"require": { "require": {
"php": ">=7.2", "php": ">=7.2.0RC3",
"ext-bcmath": "*", "ext-bcmath": "*",
"ext-curl": "*", "ext-curl": "*",
"ext-hash": "*", "ext-hash": "*",
@ -20,14 +20,23 @@
"ext-spl": "*", "ext-spl": "*",
"ext-yaml": ">=2.0.0", "ext-yaml": ">=2.0.0",
"ext-zip": "*", "ext-zip": "*",
"ext-zlib": ">=1.2.11" "ext-zlib": ">=1.2.11",
"pmmp/raklib": "^0.9.0",
"pmmp/pocketmine-spl": "^0.0.2"
}, },
"autoload": { "autoload": {
"exclude-from-classmap": [
"src/spl/stubs"
],
"psr-0": { "psr-0": {
"": ["src", "src/spl"] "": ["src"]
} }
} },
"repositories": [
{
"type": "vcs",
"url": "https://github.com/pmmp/RakLib"
},
{
"type": "vcs",
"url": "https://github.com/pmmp/PocketMine-SPL"
}
]
} }

77
composer.lock generated
View File

@ -4,18 +4,89 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "d4fecad9dce5314493052c870c8cf059", "content-hash": "55bdbaf13ac4ea71f0705f03f715172d",
"packages": [], "packages": [
{
"name": "pmmp/pocketmine-spl",
"version": "0.0.2",
"source": {
"type": "git",
"url": "https://github.com/pmmp/PocketMine-SPL.git",
"reference": "065b631d63e2c4ddb3141f63a07a9ecbab8357d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/PocketMine-SPL/zipball/065b631d63e2c4ddb3141f63a07a9ecbab8357d1",
"reference": "065b631d63e2c4ddb3141f63a07a9ecbab8357d1",
"shasum": ""
},
"type": "library",
"autoload": {
"exclude-from-classmap": [
"stubs"
],
"classmap": [
"./"
]
},
"license": [
"LGPL-3.0"
],
"description": "Standard library files required by PocketMine-MP and related projects",
"support": {
"source": "https://github.com/pmmp/PocketMine-SPL/tree/0.0.2"
},
"time": "2017-11-14T18:56:38+00:00"
},
{
"name": "pmmp/raklib",
"version": "0.9.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
"reference": "08470471eb16b4325fa02415fd12c5060eba9c34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/08470471eb16b4325fa02415fd12c5060eba9c34",
"reference": "08470471eb16b4325fa02415fd12c5060eba9c34",
"shasum": ""
},
"require": {
"ext-bcmath": "*",
"ext-pthreads": ">=3.1.7dev",
"ext-sockets": "*",
"php": ">=7.2.0RC3",
"pmmp/pocketmine-spl": "^0.0.2"
},
"type": "library",
"autoload": {
"classmap": [
"./"
]
},
"license": [
"GPL-3.0"
],
"description": "A RakNet server implementation written in PHP",
"support": {
"source": "https://github.com/pmmp/RakLib/tree/0.9.0",
"issues": "https://github.com/pmmp/RakLib/issues"
},
"time": "2017-11-14T19:03:14+00:00"
}
],
"packages-dev": [], "packages-dev": [],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": { "stability-flags": {
"php": 5,
"ext-pthreads": 20 "ext-pthreads": 20
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.2", "php": ">=7.2.0RC3",
"ext-bcmath": "*", "ext-bcmath": "*",
"ext-curl": "*", "ext-curl": "*",
"ext-hash": "*", "ext-hash": "*",

View File

@ -97,9 +97,7 @@ use pocketmine\metadata\MetadataValue;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\LongTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\PlayerNetworkSessionAdapter; use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket; use pocketmine\network\mcpe\protocol\AnimatePacket;
@ -1331,7 +1329,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->resetFallDistance(); $this->resetFallDistance();
$this->namedtag->playerGameType = new IntTag("playerGameType", $this->gamemode); $this->namedtag->setInt("playerGameType", $this->gamemode);
if(!$client){ //Gamemode changed by server, do not send for client changes if(!$client){ //Gamemode changed by server, do not send for client changes
$this->sendGamemode(); $this->sendGamemode();
}else{ }else{
@ -1893,15 +1891,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->namedtag = $this->server->getOfflinePlayerData($this->username); $this->namedtag = $this->server->getOfflinePlayerData($this->username);
$this->playedBefore = ($this->namedtag["lastPlayed"] - $this->namedtag["firstPlayed"]) > 1; // microtime(true) - microtime(true) may have less than one millisecond difference $this->playedBefore = ($this->namedtag["lastPlayed"] - $this->namedtag["firstPlayed"]) > 1; // microtime(true) - microtime(true) may have less than one millisecond difference
if(!isset($this->namedtag->NameTag)){ $this->namedtag->setString("NameTag", $this->username);
$this->namedtag->NameTag = new StringTag("NameTag", $this->username);
}else{ $this->gamemode = $this->namedtag->getInt("playerGameType", self::SURVIVAL) & 0x03;
$this->namedtag["NameTag"] = $this->username;
}
$this->gamemode = $this->namedtag["playerGameType"] & 0x03;
if($this->server->getForceGamemode()){ if($this->server->getForceGamemode()){
$this->gamemode = $this->server->getGamemode(); $this->gamemode = $this->server->getGamemode();
$this->namedtag->playerGameType = new IntTag("playerGameType", $this->gamemode); $this->namedtag->setInt("playerGameType", $this->gamemode);
} }
$this->allowFlight = $this->isCreative(); $this->allowFlight = $this->isCreative();
@ -1918,12 +1913,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->achievements = []; $this->achievements = [];
$achievements = $this->namedtag->getCompoundTag("Achievements") ?? [];
/** @var ByteTag $achievement */ /** @var ByteTag $achievement */
foreach($this->namedtag->Achievements as $achievement){ foreach($achievements as $achievement){
$this->achievements[$achievement->getName()] = $achievement->getValue() !== 0; $this->achievements[$achievement->getName()] = $achievement->getValue() !== 0;
} }
$this->namedtag->lastPlayed = new LongTag("lastPlayed", (int) floor(microtime(true) * 1000)); $this->namedtag->setLong("lastPlayed", (int) floor(microtime(true) * 1000));
if($this->server->getAutoSave()){ if($this->server->getAutoSave()){
$this->server->saveOfflinePlayerData($this->username, $this->namedtag, true); $this->server->saveOfflinePlayerData($this->username, $this->namedtag, true);
} }
@ -1950,8 +1946,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
} }
if(!$this->hasValidSpawnPosition()){ if(!$this->hasValidSpawnPosition()){
if(isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName((string) $this->namedtag["SpawnLevel"])) instanceof Level){ if(($level = $this->server->getLevelByName($this->namedtag->getString("SpawnLevel", ""))) instanceof Level){
$this->spawnPosition = new WeakPosition($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level); $this->spawnPosition = new WeakPosition($this->namedtag->getInt("SpawnX"), $this->namedtag->getInt("SpawnY"), $this->namedtag->getInt("SpawnZ"), $level);
}else{ }else{
$this->spawnPosition = WeakPosition::fromObject($this->level->getSafeSpawn()); $this->spawnPosition = WeakPosition::fromObject($this->level->getSafeSpawn());
} }
@ -3499,22 +3495,24 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
parent::saveNBT(); parent::saveNBT();
if($this->isValid()){ if($this->isValid()){
$this->namedtag->Level = new StringTag("Level", $this->level->getFolderName()); $this->namedtag->setString("Level", $this->level->getFolderName());
} }
if($this->hasValidSpawnPosition()){ if($this->hasValidSpawnPosition()){
$this->namedtag->SpawnLevel = new StringTag("SpawnLevel", $this->spawnPosition->getLevel()->getFolderName()); $this->namedtag->setString("SpawnLevel", $this->spawnPosition->getLevel()->getFolderName());
$this->namedtag->SpawnX = new IntTag("SpawnX", (int) $this->spawnPosition->x); $this->namedtag->setInt("SpawnX", (int) $this->spawnPosition->x);
$this->namedtag->SpawnY = new IntTag("SpawnY", (int) $this->spawnPosition->y); $this->namedtag->setInt("SpawnY", (int) $this->spawnPosition->y);
$this->namedtag->SpawnZ = new IntTag("SpawnZ", (int) $this->spawnPosition->z); $this->namedtag->setInt("SpawnZ", (int) $this->spawnPosition->z);
} }
$achievements = new CompoundTag("Achievements");
foreach($this->achievements as $achievement => $status){ foreach($this->achievements as $achievement => $status){
$this->namedtag->Achievements[$achievement] = new ByteTag($achievement, $status === true ? 1 : 0); $achievements->setByte($achievement, $status === true ? 1 : 0);
} }
$this->namedtag->setTag($achievements);
$this->namedtag["playerGameType"] = $this->gamemode; $this->namedtag->setInt("playerGameType", $this->gamemode);
$this->namedtag["lastPlayed"] = (int) floor(microtime(true) * 1000); $this->namedtag->setLong("lastPlayed", (int) floor(microtime(true) * 1000));
if($this->username != "" and $this->namedtag instanceof CompoundTag){ if($this->username != "" and $this->namedtag instanceof CompoundTag){
$this->server->saveOfflinePlayerData($this->username, $this->namedtag, $async); $this->server->saveOfflinePlayerData($this->username, $this->namedtag, $async);

View File

@ -129,30 +129,16 @@ namespace pocketmine {
define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR); define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR);
} }
$requiredSplVer = "0.0.1"; define('pocketmine\COMPOSER_AUTOLOADER_PATH', \pocketmine\PATH . 'vendor/autoload.php');
if(!is_file(\pocketmine\PATH . "src/spl/version.php")){
echo "[CRITICAL] Cannot find PocketMine-SPL or incompatible version." . PHP_EOL;
echo "[CRITICAL] Please update your submodules or use provided builds." . PHP_EOL;
exit(1);
}elseif(version_compare($requiredSplVer, require(\pocketmine\PATH . "src/spl/version.php")) > 0){
echo "[CRITICAL] Incompatible PocketMine-SPL submodule version ($requiredSplVer is required)." . PHP_EOL;
echo "[CRITICAL] Please update your submodules or use provided builds." . PHP_EOL;
exit(1);
}
if(is_file(\pocketmine\PATH . "vendor/autoload.php")){ if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
require_once(\pocketmine\PATH . "vendor/autoload.php"); require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}else{ }else{
echo "[CRITICAL] Composer autoloader not found" . PHP_EOL; echo "[CRITICAL] Composer autoloader not found" . PHP_EOL;
echo "[CRITICAL] Please initialize composer dependencies before running." . PHP_EOL; echo "[CRITICAL] Please initialize composer dependencies before running." . PHP_EOL;
exit(1); exit(1);
} }
if(!class_exists("ClassLoader", false)){
require_once(\pocketmine\PATH . "src/spl/ClassLoader.php");
require_once(\pocketmine\PATH . "src/spl/BaseClassLoader.php");
}
/* /*
* We now use the Composer autoloader, but this autoloader is still used by RakLib and for loading plugins. * We now use the Composer autoloader, but this autoloader is still used by RakLib and for loading plugins.
*/ */

View File

@ -77,10 +77,8 @@ class BurningFurnace extends Solid{
$furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this)); $furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this));
} }
if(isset($furnace->namedtag->Lock) and $furnace->namedtag->Lock instanceof StringTag){ if($furnace->namedtag->hasTag("Lock", StringTag::class) and $furnace->namedtag->getString("Lock") !== $item->getCustomName()){
if($furnace->namedtag->Lock->getValue() !== $item->getCustomName()){ return true;
return true;
}
} }
$player->addWindow($furnace->getInventory()); $player->addWindow($furnace->getInventory());

View File

@ -126,7 +126,7 @@ class Chest extends Transparent{
if( if(
!$this->getSide(Vector3::SIDE_UP)->isTransparent() or !$this->getSide(Vector3::SIDE_UP)->isTransparent() or
($chest->isPaired() and !$chest->getPair()->getBlock()->getSide(Vector3::SIDE_UP)->isTransparent()) or ($chest->isPaired() and !$chest->getPair()->getBlock()->getSide(Vector3::SIDE_UP)->isTransparent()) or
(isset($chest->namedtag->Lock) and $chest->namedtag->Lock instanceof StringTag and $chest->namedtag->Lock->getValue() !== $item->getCustomName()) ($chest->namedtag->hasTag("Lock", StringTag::class) and $chest->namedtag->getString("Lock") !== $item->getCustomName())
){ ){
return true; return true;
} }

View File

@ -51,12 +51,10 @@ use pocketmine\math\Vector2;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\metadata\Metadatable; use pocketmine\metadata\Metadatable;
use pocketmine\metadata\MetadataValue; use pocketmine\metadata\MetadataValue;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\DoubleTag; use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\AddEntityPacket; use pocketmine\network\mcpe\protocol\AddEntityPacket;
use pocketmine\network\mcpe\protocol\EntityEventPacket; use pocketmine\network\mcpe\protocol\EntityEventPacket;
@ -504,53 +502,36 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->server = $level->getServer(); $this->server = $level->getServer();
$this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
$this->setPositionAndRotation(
$this->temporalVector->setComponents(
$this->namedtag["Pos"][0],
$this->namedtag["Pos"][1],
$this->namedtag["Pos"][2]
),
$this->namedtag->Rotation[0],
$this->namedtag->Rotation[1]
);
if(isset($this->namedtag->Motion)){ /** @var float[] $pos */
$this->setMotion($this->temporalVector->setComponents($this->namedtag["Motion"][0], $this->namedtag["Motion"][1], $this->namedtag["Motion"][2])); $pos = $this->namedtag->getListTag("Pos")->getAllValues();
}else{ /** @var float[] $rotation */
$this->setMotion($this->temporalVector->setComponents(0, 0, 0)); $rotation = $this->namedtag->getListTag("Rotation")->getAllValues();
$this->setPositionAndRotation($this->temporalVector->setComponents(...$pos), ...$rotation);
/** @var float[] $motion */
$motion = [0, 0, 0];
if($this->namedtag->hasTag("Motion", ListTag::class)){
$motion = $this->namedtag->getListTag("Motion")->getAllValues();
} }
$this->setMotion($this->temporalVector->setComponents(...$motion));
$this->resetLastMovements(); $this->resetLastMovements();
assert(!is_nan($this->x) and !is_infinite($this->x) and !is_nan($this->y) and !is_infinite($this->y) and !is_nan($this->z) and !is_infinite($this->z)); assert(!is_nan($this->x) and !is_infinite($this->x) and !is_nan($this->y) and !is_infinite($this->y) and !is_nan($this->z) and !is_infinite($this->z));
if(!isset($this->namedtag->FallDistance)){ $this->fallDistance = $this->namedtag->getFloat("FallDistance", 0);
$this->namedtag->FallDistance = new FloatTag("FallDistance", 0);
}
$this->fallDistance = $this->namedtag["FallDistance"];
if(!isset($this->namedtag->Fire)){ $this->fireTicks = $this->namedtag->getShort("Fire", 0);
$this->namedtag->Fire = new ShortTag("Fire", 0);
}
$this->fireTicks = (int) $this->namedtag["Fire"];
if($this->isOnFire()){ if($this->isOnFire()){
$this->setGenericFlag(self::DATA_FLAG_ONFIRE); $this->setGenericFlag(self::DATA_FLAG_ONFIRE);
} }
if(!isset($this->namedtag->Air)){ $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $this->namedtag->getShort("Air", 300), false);
$this->namedtag->Air = new ShortTag("Air", 300); $this->onGround = $this->namedtag->getByte("OnGround", 0) !== 0;
} $this->invulnerable = $this->namedtag->getByte("Invulnerable", 0) !== 0;
$this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $this->namedtag["Air"], false);
if(!isset($this->namedtag->OnGround)){
$this->namedtag->OnGround = new ByteTag("OnGround", 0);
}
$this->onGround = $this->namedtag["OnGround"] !== 0;
if(!isset($this->namedtag->Invulnerable)){
$this->namedtag->Invulnerable = new ByteTag("Invulnerable", 0);
}
$this->invulnerable = $this->namedtag["Invulnerable"] !== 0;
$this->attributeMap = new AttributeMap(); $this->attributeMap = new AttributeMap();
$this->addAttributes(); $this->addAttributes();
@ -816,49 +797,46 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public function saveNBT(){ public function saveNBT(){
if(!($this instanceof Player)){ if(!($this instanceof Player)){
$this->namedtag->id = new StringTag("id", $this->getSaveId()); $this->namedtag->setString("id", $this->getSaveId(), true);
if($this->getNameTag() !== ""){ if($this->getNameTag() !== ""){
$this->namedtag->CustomName = new StringTag("CustomName", $this->getNameTag()); $this->namedtag->setString("CustomName", $this->getNameTag());
$this->namedtag->CustomNameVisible = new ByteTag("CustomNameVisible", $this->isNameTagVisible() ? 1 : 0); $this->namedtag->setByte("CustomNameVisible", $this->isNameTagVisible() ? 1 : 0);
}else{ }else{
unset($this->namedtag->CustomName); $this->namedtag->removeTag("CustomName", "CustomNameVisible");
unset($this->namedtag->CustomNameVisible);
} }
} }
$this->namedtag->Pos = new ListTag("Pos", [ $this->namedtag->setTag(new ListTag("Pos", [
new DoubleTag("", $this->x), new DoubleTag("", $this->x),
new DoubleTag("", $this->y), new DoubleTag("", $this->y),
new DoubleTag("", $this->z) new DoubleTag("", $this->z)
]); ]));
$this->namedtag->Motion = new ListTag("Motion", [ $this->namedtag->setTag(new ListTag("Motion", [
new DoubleTag("", $this->motionX), new DoubleTag("", $this->motionX),
new DoubleTag("", $this->motionY), new DoubleTag("", $this->motionY),
new DoubleTag("", $this->motionZ) new DoubleTag("", $this->motionZ)
]); ]));
$this->namedtag->Rotation = new ListTag("Rotation", [ $this->namedtag->setTag(new ListTag("Rotation", [
new FloatTag("", $this->yaw), new FloatTag("", $this->yaw),
new FloatTag("", $this->pitch) new FloatTag("", $this->pitch)
]); ]));
$this->namedtag->FallDistance = new FloatTag("FallDistance", $this->fallDistance); $this->namedtag->setFloat("FallDistance", $this->fallDistance);
$this->namedtag->Fire = new ShortTag("Fire", $this->fireTicks); $this->namedtag->setShort("Fire", $this->fireTicks);
$this->namedtag->Air = new ShortTag("Air", $this->getDataProperty(self::DATA_AIR)); $this->namedtag->setShort("Air", $this->getDataProperty(self::DATA_AIR));
$this->namedtag->OnGround = new ByteTag("OnGround", $this->onGround ? 1 : 0); $this->namedtag->setByte("OnGround", $this->onGround ? 1 : 0);
$this->namedtag->Invulnerable = new ByteTag("Invulnerable", $this->invulnerable ? 1 : 0); $this->namedtag->setByte("Invulnerable", $this->invulnerable ? 1 : 0);
} }
protected function initEntity(){ protected function initEntity(){
assert($this->namedtag instanceof CompoundTag); assert($this->namedtag instanceof CompoundTag);
if(isset($this->namedtag->CustomName)){ if($this->namedtag->hasTag("CustomName", StringTag::class)){
$this->setNameTag($this->namedtag["CustomName"]); $this->setNameTag($this->namedtag->getString("CustomName"));
if(isset($this->namedtag->CustomNameVisible)){ $this->setNameTagVisible($this->namedtag->getByte("CustomNameVisible", 1) !== 0);
$this->setNameTagVisible($this->namedtag["CustomNameVisible"] > 0);
}
} }
$this->scheduleUpdate(); $this->scheduleUpdate();
@ -1709,6 +1687,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$radius = $this->width / 2; $radius = $this->width / 2;
$this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius); $this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius);
$this->blocksAround = null;
$this->checkChunks(); $this->checkChunks();
return true; return true;

View File

@ -53,17 +53,13 @@ class FallingSand extends Entity{
parent::initEntity(); parent::initEntity();
$blockId = 0; $blockId = 0;
$damage = 0;
if(isset($this->namedtag->TileID)){ //TODO: 1.8+ save format
$blockId = (int) $this->namedtag["TileID"]; if($this->namedtag->hasTag("TileID", IntTag::class)){
}elseif(isset($this->namedtag->Tile)){ $blockId = $this->namedtag->getInt("TileID");
$blockId = (int) $this->namedtag["Tile"]; }elseif($this->namedtag->hasTag("Tile", ByteTag::class)){
$this->namedtag["TileID"] = new IntTag("TileID", $blockId); $blockId = $this->namedtag->getByte("Tile");
} $this->namedtag->removeTag("Tile");
if(isset($this->namedtag->Data)){
$damage = (int) $this->namedtag["Data"];
} }
if($blockId === 0){ if($blockId === 0){
@ -71,6 +67,8 @@ class FallingSand extends Entity{
return; return;
} }
$damage = $this->namedtag->getByte("Data", 0);
$this->block = BlockFactory::get($blockId, $damage); $this->block = BlockFactory::get($blockId, $damage);
$this->setDataProperty(self::DATA_VARIANT, self::DATA_TYPE_INT, $this->block->getId() | ($this->block->getDamage() << 8)); $this->setDataProperty(self::DATA_VARIANT, self::DATA_TYPE_INT, $this->block->getId() | ($this->block->getDamage() << 8));
@ -132,7 +130,7 @@ class FallingSand extends Entity{
} }
public function saveNBT(){ public function saveNBT(){
$this->namedtag->TileID = new IntTag("TileID", $this->block->getId()); $this->namedtag->setInt("TileID", $this->block->getId(), true);
$this->namedtag->Data = new ByteTag("Data", $this->block->getDamage()); $this->namedtag->setByte("Data", $this->block->getDamage());
} }
} }

View File

@ -34,7 +34,6 @@ use pocketmine\item\Item as ItemItem;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
@ -311,14 +310,15 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT. * For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
*/ */
protected function initHumanData(){ protected function initHumanData(){
if(isset($this->namedtag->NameTag)){ if($this->namedtag->hasTag("NameTag", StringTag::class)){
$this->setNameTag($this->namedtag["NameTag"]); $this->setNameTag($this->namedtag->getString("NameTag"));
} }
if(isset($this->namedtag->Skin) and $this->namedtag->Skin instanceof CompoundTag){ $skin = $this->namedtag->getCompoundTag("Skin");
if($skin !== null){
$this->setSkin(new Skin( $this->setSkin(new Skin(
$this->namedtag->Skin["Name"], $skin->getString("Name"),
$this->namedtag->Skin["Data"] $skin->getString("Data")
)); ));
} }
@ -333,71 +333,39 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->inventory = new PlayerInventory($this); $this->inventory = new PlayerInventory($this);
$this->initHumanData(); $this->initHumanData();
if(isset($this->namedtag->Inventory) and $this->namedtag->Inventory instanceof ListTag){ $inventoryTag = $this->namedtag->getListTag("Inventory");
foreach($this->namedtag->Inventory as $i => $item){ if($inventoryTag !== null){
if($item["Slot"] >= 0 and $item["Slot"] < 9){ //Hotbar /** @var CompoundTag $item */
foreach($inventoryTag as $i => $item){
$slot = $item->getByte("Slot");
if($slot >= 0 and $slot < 9){ //Hotbar
//Old hotbar saving stuff, remove it (useless now) //Old hotbar saving stuff, remove it (useless now)
unset($this->namedtag->Inventory->{$i}); unset($inventoryTag[$i]);
}elseif($item["Slot"] >= 100 and $item["Slot"] < 104){ //Armor }elseif($slot >= 100 and $slot < 104){ //Armor
$this->inventory->setItem($this->inventory->getSize() + $item["Slot"] - 100, ItemItem::nbtDeserialize($item)); $this->inventory->setItem($this->inventory->getSize() + $slot - 100, ItemItem::nbtDeserialize($item));
}else{ }else{
$this->inventory->setItem($item["Slot"] - 9, ItemItem::nbtDeserialize($item)); $this->inventory->setItem($slot - 9, ItemItem::nbtDeserialize($item));
} }
} }
} }
if(isset($this->namedtag->SelectedInventorySlot) and $this->namedtag->SelectedInventorySlot instanceof IntTag){ $this->inventory->setHeldItemIndex($this->namedtag->getInt("SelectedInventorySlot", 0), false);
$this->inventory->setHeldItemIndex($this->namedtag->SelectedInventorySlot->getValue(), false);
}else{
$this->inventory->setHeldItemIndex(0, false);
}
parent::initEntity(); parent::initEntity();
if(!isset($this->namedtag->foodLevel) or !($this->namedtag->foodLevel instanceof IntTag)){ $this->setFood((float) $this->namedtag->getInt("foodLevel", (int) $this->getFood(), true));
$this->namedtag->foodLevel = new IntTag("foodLevel", (int) $this->getFood()); $this->setExhaustion($this->namedtag->getFloat("foodExhaustionLevel", $this->getExhaustion(), true));
}else{ $this->setSaturation($this->namedtag->getFloat("foodSaturationLevel", $this->getSaturation(), true));
$this->setFood((float) $this->namedtag["foodLevel"]); $this->foodTickTimer = $this->namedtag->getInt("foodTickTimer", $this->foodTickTimer, true);
}
if(!isset($this->namedtag->foodExhaustionLevel) or !($this->namedtag->foodExhaustionLevel instanceof FloatTag)){ $this->setXpLevel($this->namedtag->getInt("XpLevel", $this->getXpLevel(), true));
$this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion()); $this->setXpProgress($this->namedtag->getFloat("XpP", $this->getXpProgress(), true));
}else{ $this->totalXp = $this->namedtag->getInt("XpTotal", $this->totalXp, true);
$this->setExhaustion((float) $this->namedtag["foodExhaustionLevel"]);
}
if(!isset($this->namedtag->foodSaturationLevel) or !($this->namedtag->foodSaturationLevel instanceof FloatTag)){ if($this->namedtag->hasTag("XpSeed", IntTag::class)){
$this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation()); $this->xpSeed = $this->namedtag->getInt("XpSeed");
}else{ }else{
$this->setSaturation((float) $this->namedtag["foodSaturationLevel"]); $this->xpSeed = random_int(INT32_MIN, INT32_MAX);
}
if(!isset($this->namedtag->foodTickTimer) or !($this->namedtag->foodTickTimer instanceof IntTag)){
$this->namedtag->foodTickTimer = new IntTag("foodTickTimer", $this->foodTickTimer);
}else{
$this->foodTickTimer = $this->namedtag["foodTickTimer"];
}
if(!isset($this->namedtag->XpLevel) or !($this->namedtag->XpLevel instanceof IntTag)){
$this->namedtag->XpLevel = new IntTag("XpLevel", $this->getXpLevel());
}else{
$this->setXpLevel((int) $this->namedtag["XpLevel"]);
}
if(!isset($this->namedtag->XpP) or !($this->namedtag->XpP instanceof FloatTag)){
$this->namedtag->XpP = new FloatTag("XpP", $this->getXpProgress());
}
if(!isset($this->namedtag->XpTotal) or !($this->namedtag->XpTotal instanceof IntTag)){
$this->namedtag->XpTotal = new IntTag("XpTotal", $this->totalXp);
}else{
$this->totalXp = $this->namedtag["XpTotal"];
}
if(!isset($this->namedtag->XpSeed) or !($this->namedtag->XpSeed instanceof IntTag)){
$this->namedtag->XpSeed = new IntTag("XpSeed", $this->xpSeed ?? ($this->xpSeed = mt_rand(-0x80000000, 0x7fffffff)));
}else{
$this->xpSeed = $this->namedtag["XpSeed"];
} }
} }
@ -479,19 +447,25 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
public function saveNBT(){ public function saveNBT(){
parent::saveNBT(); parent::saveNBT();
$this->namedtag->foodLevel = new IntTag("foodLevel", (int) $this->getFood()); $this->namedtag->setInt("foodLevel", (int) $this->getFood(), true);
$this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion()); $this->namedtag->setFloat("foodExhaustionLevel", $this->getExhaustion(), true);
$this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation()); $this->namedtag->setFloat("foodSaturationLevel", $this->getSaturation(), true);
$this->namedtag->foodTickTimer = new IntTag("foodTickTimer", $this->foodTickTimer); $this->namedtag->setInt("foodTickTimer", $this->foodTickTimer);
$this->namedtag->Inventory = new ListTag("Inventory", [], NBT::TAG_Compound); $this->namedtag->setInt("XpLevel", $this->getXpLevel());
$this->namedtag->setFloat("XpP", $this->getXpProgress());
$this->namedtag->setInt("XpTotal", $this->totalXp);
$this->namedtag->setInt("XpSeed", $this->xpSeed);
$inventoryTag = new ListTag("Inventory", [], NBT::TAG_Compound);
$this->namedtag->setTag($inventoryTag);
if($this->inventory !== null){ if($this->inventory !== null){
//Normal inventory //Normal inventory
$slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize(); $slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize();
for($slot = $this->inventory->getHotbarSize(); $slot < $slotCount; ++$slot){ for($slot = $this->inventory->getHotbarSize(); $slot < $slotCount; ++$slot){
$item = $this->inventory->getItem($slot - 9); $item = $this->inventory->getItem($slot - 9);
if(!$item->isNull()){ if(!$item->isNull()){
$this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot); $inventoryTag[$slot] = $item->nbtSerialize($slot);
} }
} }
@ -499,19 +473,19 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
for($slot = 100; $slot < 104; ++$slot){ for($slot = 100; $slot < 104; ++$slot){
$item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100); $item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100);
if(!$item->isNull()){ if(!$item->isNull()){
$this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot); $inventoryTag[$slot] = $item->nbtSerialize($slot);
} }
} }
$this->namedtag->SelectedInventorySlot = new IntTag("SelectedInventorySlot", $this->inventory->getHeldItemIndex()); $this->namedtag->setInt("SelectedInventorySlot", $this->inventory->getHeldItemIndex());
} }
if($this->skin !== null){ if($this->skin !== null){
$this->namedtag->Skin = new CompoundTag("Skin", [ $this->namedtag->setTag(new CompoundTag("Skin", [
//TODO: save cape & geometry //TODO: save cape & geometry
new StringTag("Data", $this->skin->getSkinData()), new StringTag("Data", $this->skin->getSkinData()),
new StringTag("Name", $this->skin->getSkinId()) new StringTag("Name", $this->skin->getSkinId())
]); ]));
} }
} }

View File

@ -27,9 +27,6 @@ use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\ItemDespawnEvent; use pocketmine\event\entity\ItemDespawnEvent;
use pocketmine\event\entity\ItemSpawnEvent; use pocketmine\event\entity\ItemSpawnEvent;
use pocketmine\item\Item as ItemItem; use pocketmine\item\Item as ItemItem;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\AddItemEntityPacket; use pocketmine\network\mcpe\protocol\AddItemEntityPacket;
use pocketmine\Player; use pocketmine\Player;
@ -59,28 +56,19 @@ class Item extends Entity{
$this->setMaxHealth(5); $this->setMaxHealth(5);
$this->setHealth((int) $this->namedtag["Health"]); $this->setHealth((int) $this->namedtag["Health"]);
if(isset($this->namedtag->Age)){ $this->age = $this->namedtag->getShort("Age", $this->age);
$this->age = $this->namedtag["Age"]; $this->pickupDelay = $this->namedtag->getShort("PickupDelay", $this->pickupDelay);
} $this->owner = $this->namedtag->getString("Owner", $this->owner);
if(isset($this->namedtag->PickupDelay)){ $this->thrower = $this->namedtag->getString("Thrower", $this->thrower);
$this->pickupDelay = $this->namedtag["PickupDelay"];
}
if(isset($this->namedtag->Owner)){
$this->owner = $this->namedtag["Owner"];
}
if(isset($this->namedtag->Thrower)){
$this->thrower = $this->namedtag["Thrower"];
}
if(!isset($this->namedtag->Item)){ $itemTag = $this->namedtag->getCompoundTag("Item");
if($itemTag === null){
$this->close(); $this->close();
return; return;
} }
assert($this->namedtag->Item instanceof CompoundTag); $this->item = ItemItem::nbtDeserialize($itemTag);
$this->item = ItemItem::nbtDeserialize($this->namedtag->Item);
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this)); $this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
@ -139,15 +127,15 @@ class Item extends Entity{
public function saveNBT(){ public function saveNBT(){
parent::saveNBT(); parent::saveNBT();
$this->namedtag->Item = $this->item->nbtSerialize(-1, "Item"); $this->namedtag->setTag($this->item->nbtSerialize(-1, "Item"));
$this->namedtag->Health = new ShortTag("Health", (int) $this->getHealth()); $this->namedtag->setShort("Health", (int) $this->getHealth());
$this->namedtag->Age = new ShortTag("Age", $this->age); $this->namedtag->setShort("Age", $this->age);
$this->namedtag->PickupDelay = new ShortTag("PickupDelay", $this->pickupDelay); $this->namedtag->setShort("PickupDelay", $this->pickupDelay);
if($this->owner !== null){ if($this->owner !== null){
$this->namedtag->Owner = new StringTag("Owner", $this->owner); $this->namedtag->setString("Owner", $this->owner);
} }
if($this->thrower !== null){ if($this->thrower !== null){
$this->namedtag->Thrower = new StringTag("Thrower", $this->thrower); $this->namedtag->setString("Thrower", $this->thrower);
} }
} }

View File

@ -65,29 +65,33 @@ abstract class Living extends Entity implements Damageable{
protected function initEntity(){ protected function initEntity(){
parent::initEntity(); parent::initEntity();
if(isset($this->namedtag->HealF)){ $health = $this->getMaxHealth();
$this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag["HealF"]);
unset($this->namedtag->HealF); if($this->namedtag->hasTag("HealF", FloatTag::class)){
}elseif(isset($this->namedtag->Health)){ $health = new FloatTag("Health", (float) $this->namedtag["HealF"]);
if(!($this->namedtag->Health instanceof FloatTag)){ $this->namedtag->removeTag("HealF");
$this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag->Health->getValue()); }elseif($this->namedtag->hasTag("Health")){
$healthTag = $this->namedtag->getTag("Health");
$health = (float) $healthTag->getValue(); //Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
if(!($healthTag instanceof FloatTag)){
$this->namedtag->removeTag("Health");
} }
}else{
$this->namedtag->Health = new FloatTag("Health", (float) $this->getMaxHealth());
} }
$this->setHealth((float) $this->namedtag["Health"]); $this->setHealth($health);
if(isset($this->namedtag->ActiveEffects)){ /** @var CompoundTag[]|ListTag $activeEffectsTag */
foreach($this->namedtag->ActiveEffects->getValue() as $e){ $activeEffectsTag = $this->namedtag->getListTag("ActiveEffects");
$amplifier = Binary::unsignByte($e->Amplifier->getValue()); //0-255 only if($activeEffectsTag !== null){
foreach($activeEffectsTag as $e){
$amplifier = Binary::unsignByte($e->getByte("Amplifier")); //0-255 only
$effect = Effect::getEffect($e["Id"]); $effect = Effect::getEffect($e->getByte("Id"));
if($effect === null){ if($effect === null){
continue; continue;
} }
$effect->setAmplifier($amplifier)->setDuration($e["Duration"])->setVisible($e["ShowParticles"] > 0); $effect->setAmplifier($amplifier)->setDuration($e->getInt("Duration"))->setVisible($e->getByte("ShowParticles", 1) > 0);
$this->addEffect($effect); $this->addEffect($effect);
} }
@ -130,7 +134,7 @@ abstract class Living extends Entity implements Damageable{
public function saveNBT(){ public function saveNBT(){
parent::saveNBT(); parent::saveNBT();
$this->namedtag->Health = new FloatTag("Health", $this->getHealth()); $this->namedtag->setFloat("Health", $this->getHealth(), true);
if(count($this->effects) > 0){ if(count($this->effects) > 0){
$effects = []; $effects = [];
@ -144,9 +148,9 @@ abstract class Living extends Entity implements Damageable{
]); ]);
} }
$this->namedtag->ActiveEffects = new ListTag("ActiveEffects", $effects); $this->namedtag->setTag(new ListTag("ActiveEffects", $effects));
}else{ }else{
unset($this->namedtag->ActiveEffects); $this->namedtag->removeTag("ActiveEffects");
} }
} }

View File

@ -26,7 +26,7 @@ namespace pocketmine\entity;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\ExplosionPrimeEvent; use pocketmine\event\entity\ExplosionPrimeEvent;
use pocketmine\level\Explosion; use pocketmine\level\Explosion;
use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\ShortTag;
use pocketmine\network\mcpe\protocol\LevelEventPacket; use pocketmine\network\mcpe\protocol\LevelEventPacket;
class PrimedTNT extends Entity implements Explosive{ class PrimedTNT extends Entity implements Explosive{
@ -54,8 +54,8 @@ class PrimedTNT extends Entity implements Explosive{
protected function initEntity(){ protected function initEntity(){
parent::initEntity(); parent::initEntity();
if(isset($this->namedtag->Fuse)){ if($this->namedtag->hasTag("Fuse", ShortTag::class)){
$this->fuse = $this->namedtag["Fuse"]; $this->fuse = $this->namedtag->getShort("Fuse");
}else{ }else{
$this->fuse = 80; $this->fuse = 80;
} }
@ -73,7 +73,7 @@ class PrimedTNT extends Entity implements Explosive{
public function saveNBT(){ public function saveNBT(){
parent::saveNBT(); parent::saveNBT();
$this->namedtag->Fuse = new ByteTag("Fuse", $this->fuse); $this->namedtag->setShort("Fuse", $this->fuse, true); //older versions incorrectly saved this as a byte
} }
public function entityBaseTick(int $tickDiff = 1) : bool{ public function entityBaseTick(int $tickDiff = 1) : bool{

View File

@ -23,8 +23,6 @@ declare(strict_types=1);
namespace pocketmine\entity; namespace pocketmine\entity;
use pocketmine\nbt\tag\IntTag;
class Villager extends Creature implements NPC, Ageable{ class Villager extends Creature implements NPC, Ageable{
const PROFESSION_FARMER = 0; const PROFESSION_FARMER = 0;
const PROFESSION_LIBRARIAN = 1; const PROFESSION_LIBRARIAN = 1;
@ -45,7 +43,7 @@ class Villager extends Creature implements NPC, Ageable{
parent::initEntity(); parent::initEntity();
/** @var int $profession */ /** @var int $profession */
$profession = $this->namedtag["Profession"] ?? self::PROFESSION_FARMER; $profession = $this->namedtag->getInt("Profession", self::PROFESSION_FARMER);
if($profession > 4 or $profession < 0){ if($profession > 4 or $profession < 0){
$profession = self::PROFESSION_FARMER; $profession = self::PROFESSION_FARMER;
@ -56,7 +54,7 @@ class Villager extends Creature implements NPC, Ageable{
public function saveNBT(){ public function saveNBT(){
parent::saveNBT(); parent::saveNBT();
$this->namedtag->Profession = new IntTag("Profession", $this->getProfession()); $this->namedtag->setInt("Profession", $this->getProfession());
} }
/** /**

View File

@ -34,7 +34,6 @@ use pocketmine\level\Level;
use pocketmine\level\MovingObjectPosition; use pocketmine\level\MovingObjectPosition;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ShortTag;
abstract class Projectile extends Entity{ abstract class Projectile extends Entity{
@ -62,9 +61,7 @@ abstract class Projectile extends Entity{
$this->setMaxHealth(1); $this->setMaxHealth(1);
$this->setHealth(1); $this->setHealth(1);
if(isset($this->namedtag->Age)){ $this->age = $this->namedtag->getShort("Age", $this->age);
$this->age = $this->namedtag["Age"];
}
} }
public function canCollideWith(Entity $entity) : bool{ public function canCollideWith(Entity $entity) : bool{
@ -107,7 +104,7 @@ abstract class Projectile extends Entity{
public function saveNBT(){ public function saveNBT(){
parent::saveNBT(); parent::saveNBT();
$this->namedtag->Age = new ShortTag("Age", $this->age); $this->namedtag->setShort("Age", $this->age);
} }
protected function applyDragBeforeGravity() : bool{ protected function applyDragBeforeGravity() : bool{

View File

@ -481,9 +481,7 @@ class Item implements ItemIds, \JsonSerializable{
public function getLore() : array{ public function getLore() : array{
$display = $this->getNamedTagEntry(self::TAG_DISPLAY); $display = $this->getNamedTagEntry(self::TAG_DISPLAY);
if($display instanceof CompoundTag and ($lore = $display->getListTag(self::TAG_DISPLAY_LORE)) !== null){ if($display instanceof CompoundTag and ($lore = $display->getListTag(self::TAG_DISPLAY_LORE)) !== null){
return array_map(function(StringTag $tag) : string{ return $lore->getAllValues();
return $tag->getValue();
}, $lore->getValue());
} }
return []; return [];

View File

@ -23,12 +23,15 @@ declare(strict_types=1);
namespace pocketmine\item; namespace pocketmine\item;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
class WritableBook extends Item{ class WritableBook extends Item{
const TAG_PAGES = "pages"; //TAG_List<TAG_Compound>
public function __construct(int $meta = 0){ public function __construct(int $meta = 0){
parent::__construct(self::WRITABLE_BOOK, $meta, "Book & Quill"); parent::__construct(self::WRITABLE_BOOK, $meta, "Book & Quill");
} }
@ -41,7 +44,8 @@ class WritableBook extends Item{
* @return bool * @return bool
*/ */
public function pageExists(int $pageId) : bool{ public function pageExists(int $pageId) : bool{
return isset($this->getNamedTag()->pages->{$pageId}); $pages = $this->getNamedTag()->getListTag(self::TAG_PAGES);
return $pages !== null and isset($pages[$pageId]);
} }
/** /**
@ -52,10 +56,17 @@ class WritableBook extends Item{
* @return string|null * @return string|null
*/ */
public function getPageText(int $pageId) : ?string{ public function getPageText(int $pageId) : ?string{
if(!$this->pageExists($pageId)){ $pages = $this->getNamedTag()->getListTag(self::TAG_PAGES);
if($pages === null){
return null; return null;
} }
return $this->getNamedTag()->pages->{$pageId}->text->getValue();
$page = $pages[$pageId] ?? null;
if($page instanceof CompoundTag){
return $page->getString("text", "");
}
return null;
} }
/** /**
@ -74,7 +85,11 @@ class WritableBook extends Item{
} }
$namedTag = $this->getNamedTag(); $namedTag = $this->getNamedTag();
$namedTag->pages->{$pageId}->text->setValue($pageText); /** @var CompoundTag[]|ListTag $pages */
$pages = $namedTag->getListTag(self::TAG_PAGES);
assert($pages instanceof ListTag);
$pages[$pageId]->setString("text", $pageText);
$this->setNamedTag($namedTag); $this->setNamedTag($namedTag);
return $created; return $created;
@ -92,19 +107,19 @@ class WritableBook extends Item{
} }
$namedTag = $this->getNamedTag(); $namedTag = $this->getNamedTag();
if(!isset($namedTag->pages) or !($namedTag->pages instanceof ListTag)){ /** @var CompoundTag[]|ListTag $pages */
$namedTag->pages = new ListTag("pages", []); $pages = $namedTag->getListTag(self::TAG_PAGES) ?? new ListTag(self::TAG_PAGES, [], NBT::TAG_Compound);
}
for($id = 0; $id <= $pageId; $id++){ for($id = 0; $id <= $pageId; $id++){
if(!$this->pageExists($id)){ if(!$this->pageExists($id)){
$namedTag->pages->{$id} = new CompoundTag("", [ $pages[$id] = new CompoundTag("", [
new StringTag("text", ""), new StringTag("text", ""),
new StringTag("photoname", "") new StringTag("photoname", "")
]); ]);
} }
} }
$namedTag->setTag($pages);
$this->setNamedTag($namedTag); $this->setNamedTag($namedTag);
} }
@ -121,7 +136,7 @@ class WritableBook extends Item{
} }
$namedTag = $this->getNamedTag(); $namedTag = $this->getNamedTag();
unset($namedTag->pages->{$pageId}); unset($namedTag->getListTag(self::TAG_PAGES)[$pageId]);
$this->pushPages($pageId, $namedTag); $this->pushPages($pageId, $namedTag);
$this->setNamedTag($namedTag); $this->setNamedTag($namedTag);
@ -186,6 +201,9 @@ class WritableBook extends Item{
return false; return false;
} }
$pagesTag = $namedTag->getListTag(self::TAG_PAGES);
assert($pagesTag !== null);
$type = $downwards ? -1 : 1; $type = $downwards ? -1 : 1;
foreach($pages as $key => $page){ foreach($pages as $key => $page){
if(($key <= $pageId and $downwards) or ($key < $pageId and !$downwards)){ if(($key <= $pageId and $downwards) or ($key < $pageId and !$downwards)){
@ -193,9 +211,9 @@ class WritableBook extends Item{
} }
if($downwards){ if($downwards){
unset($namedTag->pages->{$key}); unset($pagesTag[$key]);
} }
$namedTag->pages->{$key + $type} = new CompoundTag("", [ $pagesTag[$key + $type] = new CompoundTag("", [
new StringTag("text", $page->text->getValue()), new StringTag("text", $page->text->getValue()),
new StringTag("photoname", "") new StringTag("photoname", "")
]); ]);

View File

@ -23,9 +23,6 @@ declare(strict_types=1);
namespace pocketmine\item; namespace pocketmine\item;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
class WrittenBook extends WritableBook{ class WrittenBook extends WritableBook{
const GENERATION_ORIGINAL = 0; const GENERATION_ORIGINAL = 0;
@ -33,6 +30,10 @@ class WrittenBook extends WritableBook{
const GENERATION_COPY_OF_COPY = 2; const GENERATION_COPY_OF_COPY = 2;
const GENERATION_TATTERED = 3; const GENERATION_TATTERED = 3;
const TAG_GENERATION = "generation"; //TAG_Int
const TAG_AUTHOR = "author"; //TAG_String
const TAG_TITLE = "title"; //TAG_String
public function __construct(int $meta = 0){ public function __construct(int $meta = 0){
Item::__construct(self::WRITTEN_BOOK, $meta, "Written Book"); Item::__construct(self::WRITTEN_BOOK, $meta, "Written Book");
} }
@ -48,10 +49,7 @@ class WrittenBook extends WritableBook{
* @return int * @return int
*/ */
public function getGeneration() : int{ public function getGeneration() : int{
if(!isset($this->getNamedTag()->generation)) { return $this->getNamedTag()->getInt(self::TAG_GENERATION, -1);
return -1;
}
return $this->getNamedTag()->generation->getValue();
} }
/** /**
@ -64,12 +62,7 @@ class WrittenBook extends WritableBook{
throw new \InvalidArgumentException("Generation \"$generation\" is out of range"); throw new \InvalidArgumentException("Generation \"$generation\" is out of range");
} }
$namedTag = $this->getNamedTag(); $namedTag = $this->getNamedTag();
$namedTag->setInt(self::TAG_GENERATION, $generation);
if(isset($namedTag->generation)){
$namedTag->generation->setValue($generation);
}else{
$namedTag->generation = new IntTag("generation", $generation);
}
$this->setNamedTag($namedTag); $this->setNamedTag($namedTag);
} }
@ -81,10 +74,7 @@ class WrittenBook extends WritableBook{
* @return string * @return string
*/ */
public function getAuthor() : string{ public function getAuthor() : string{
if(!isset($this->getNamedTag()->author)){ return $this->getNamedTag()->getString(self::TAG_AUTHOR, "");
return "";
}
return $this->getNamedTag()->author->getValue();
} }
/** /**
@ -94,11 +84,7 @@ class WrittenBook extends WritableBook{
*/ */
public function setAuthor(string $authorName) : void{ public function setAuthor(string $authorName) : void{
$namedTag = $this->getNamedTag(); $namedTag = $this->getNamedTag();
if(isset($namedTag->author)){ $namedTag->setString(self::TAG_AUTHOR, $authorName);
$namedTag->author->setValue($authorName);
}else{
$namedTag->author = new StringTag("author", $authorName);
}
$this->setNamedTag($namedTag); $this->setNamedTag($namedTag);
} }
@ -108,10 +94,7 @@ class WrittenBook extends WritableBook{
* @return string * @return string
*/ */
public function getTitle() : string{ public function getTitle() : string{
if(!isset($this->getNamedTag()->title)){ return $this->getNamedTag()->getString(self::TAG_TITLE, "");
return "";
}
return $this->getNamedTag()->title->getValue();
} }
/** /**
@ -121,11 +104,7 @@ class WrittenBook extends WritableBook{
*/ */
public function setTitle(string $title) : void{ public function setTitle(string $title) : void{
$namedTag = $this->getNamedTag(); $namedTag = $this->getNamedTag();
if(isset($namedTag->title)){ $namedTag->setString(self::TAG_TITLE, $title);
$namedTag->title->setValue($title);
}else{
$namedTag->title = new StringTag("title", $title);
}
$this->setNamedTag($namedTag); $this->setNamedTag($namedTag);
} }
} }

View File

@ -31,8 +31,6 @@ use pocketmine\level\LevelException;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\LongTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
use pocketmine\scheduler\AsyncTask; use pocketmine\scheduler\AsyncTask;
@ -52,19 +50,19 @@ abstract class BaseLevelProvider implements LevelProvider{
} }
$nbt = new NBT(NBT::BIG_ENDIAN); $nbt = new NBT(NBT::BIG_ENDIAN);
$nbt->readCompressed(file_get_contents($this->getPath() . "level.dat")); $nbt->readCompressed(file_get_contents($this->getPath() . "level.dat"));
$levelData = $nbt->getData(); $levelData = $nbt->getData()->getCompoundTag("Data");
if($levelData->Data instanceof CompoundTag){ if($levelData !== null){
$this->levelData = $levelData->Data; $this->levelData = $levelData;
}else{ }else{
throw new LevelException("Invalid level.dat"); throw new LevelException("Invalid level.dat");
} }
if(!isset($this->levelData->generatorName)){ if(!$this->levelData->hasTag("generatorName", StringTag::class)){
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT")); $this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"), true);
} }
if(!isset($this->levelData->generatorOptions)){ if(!$this->levelData->hasTag("generatorOptions", StringTag::class)){
$this->levelData->generatorOptions = new StringTag("generatorOptions", ""); $this->levelData->setString("generatorOptions", "");
} }
} }
@ -81,33 +79,33 @@ abstract class BaseLevelProvider implements LevelProvider{
} }
public function getName() : string{ public function getName() : string{
return (string) $this->levelData["LevelName"]; return $this->levelData->getString("LevelName");
} }
public function getTime() : int{ public function getTime() : int{
return $this->levelData["Time"]; return $this->levelData->getLong("Time", 0, true);
} }
public function setTime(int $value){ public function setTime(int $value){
$this->levelData->Time = new LongTag("Time", $value); $this->levelData->setLong("Time", $value, true); //some older PM worlds had this in the wrong format
} }
public function getSeed() : int{ public function getSeed() : int{
return $this->levelData["RandomSeed"]; return $this->levelData->getLong("RandomSeed");
} }
public function setSeed(int $value){ public function setSeed(int $value){
$this->levelData->RandomSeed = new LongTag("RandomSeed", $value); $this->levelData->setLong("RandomSeed", $value);
} }
public function getSpawn() : Vector3{ public function getSpawn() : Vector3{
return new Vector3((float) $this->levelData["SpawnX"], (float) $this->levelData["SpawnY"], (float) $this->levelData["SpawnZ"]); return new Vector3($this->levelData->getInt("SpawnX"), $this->levelData->getInt("SpawnY"), $this->levelData->getInt("SpawnZ"));
} }
public function setSpawn(Vector3 $pos){ public function setSpawn(Vector3 $pos){
$this->levelData->SpawnX = new IntTag("SpawnX", (int) $pos->x); $this->levelData->setInt("SpawnX", (int) $pos->x);
$this->levelData->SpawnY = new IntTag("SpawnY", (int) $pos->y); $this->levelData->setInt("SpawnY", (int) $pos->y);
$this->levelData->SpawnZ = new IntTag("SpawnZ", (int) $pos->z); $this->levelData->setInt("SpawnZ", (int) $pos->z);
} }
public function doGarbageCollection(){ public function doGarbageCollection(){

View File

@ -96,26 +96,26 @@ class LevelDB extends BaseLevelProvider{
"compression" => LEVELDB_ZLIB_COMPRESSION "compression" => LEVELDB_ZLIB_COMPRESSION
]); ]);
if(isset($this->levelData->StorageVersion) and $this->levelData->StorageVersion->getValue() > self::CURRENT_STORAGE_VERSION){ if($this->levelData->getInt("StorageVersion", INT32_MAX, true) > self::CURRENT_STORAGE_VERSION){
throw new LevelException("Specified LevelDB world format version is newer than the version supported by the server"); throw new LevelException("Specified LevelDB world format version is not supported by " . \pocketmine\NAME);
} }
if(!isset($this->levelData->generatorName)){ if(!$this->levelData->hasTag("generatorName", StringTag::class)){
if(isset($this->levelData->Generator)){ if($this->levelData->hasTag("Generator", IntTag::class)){
switch((int) $this->levelData->Generator->getValue()){ //Detect correct generator from MCPE data switch($this->levelData->getInt("Generator")){ //Detect correct generator from MCPE data
case self::GENERATOR_FLAT: case self::GENERATOR_FLAT:
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("FLAT")); $this->levelData->setString("generatorName", (string) Generator::getGenerator("FLAT"));
if(($layers = $this->db->get(self::ENTRY_FLAT_WORLD_LAYERS)) !== false){ //Detect existing custom flat layers if(($layers = $this->db->get(self::ENTRY_FLAT_WORLD_LAYERS)) !== false){ //Detect existing custom flat layers
$layers = trim($layers, "[]"); $layers = trim($layers, "[]");
}else{ }else{
$layers = "7,3,3,2"; $layers = "7,3,3,2";
} }
$this->levelData->generatorOptions = new StringTag("generatorOptions", "2;" . $layers . ";1"); $this->levelData->setString("generatorOptions", "2;" . $layers . ";1");
break; break;
case self::GENERATOR_INFINITE: case self::GENERATOR_INFINITE:
//TODO: add a null generator which does not generate missing chunks (to allow importing back to MCPE and generating more normal terrain without PocketMine messing things up) //TODO: add a null generator which does not generate missing chunks (to allow importing back to MCPE and generating more normal terrain without PocketMine messing things up)
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT")); $this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->generatorOptions = new StringTag("generatorOptions", ""); $this->levelData->setString("generatorOptions", "");
break; break;
case self::GENERATOR_LIMITED: case self::GENERATOR_LIMITED:
throw new LevelException("Limited worlds are not currently supported"); throw new LevelException("Limited worlds are not currently supported");
@ -123,12 +123,12 @@ class LevelDB extends BaseLevelProvider{
throw new LevelException("Unknown LevelDB world format type, this level cannot be loaded"); throw new LevelException("Unknown LevelDB world format type, this level cannot be loaded");
} }
}else{ }else{
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT")); $this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"));
} }
} }
if(!isset($this->levelData->generatorOptions)){ if(!$this->levelData->hasTag("generatorOptions", StringTag::class)){
$this->levelData->generatorOptions = new StringTag("generatorOptions", ""); $this->levelData->setString("generatorOptions", "");
} }
} }
@ -226,8 +226,8 @@ class LevelDB extends BaseLevelProvider{
} }
public function saveLevelData(){ public function saveLevelData(){
$this->levelData->NetworkVersion = new IntTag("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL); $this->levelData->setInt("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL);
$this->levelData->StorageVersion = new IntTag("StorageVersion", self::CURRENT_STORAGE_VERSION); $this->levelData->setInt("StorageVersion", self::CURRENT_STORAGE_VERSION);
$nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt->setData($this->levelData); $nbt->setData($this->levelData);
@ -251,11 +251,11 @@ class LevelDB extends BaseLevelProvider{
} }
public function getDifficulty() : int{ public function getDifficulty() : int{
return isset($this->levelData->Difficulty) ? $this->levelData->Difficulty->getValue() : Level::DIFFICULTY_NORMAL; return $this->levelData->getInt("Difficulty", Level::DIFFICULTY_NORMAL);
} }
public function setDifficulty(int $difficulty){ public function setDifficulty(int $difficulty){
$this->levelData->Difficulty = new IntTag("Difficulty", $difficulty); $this->levelData->setInt("Difficulty", $difficulty); //yes, this is intended! (in PE: int, PC: byte)
} }
public function getLoadedChunks() : array{ public function getLoadedChunks() : array{

View File

@ -28,9 +28,10 @@ use pocketmine\level\format\ChunkException;
use pocketmine\level\format\io\ChunkUtils; use pocketmine\level\format\io\ChunkUtils;
use pocketmine\level\format\SubChunk; use pocketmine\level\format\SubChunk;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\{ use pocketmine\nbt\tag\ByteArrayTag;
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag use pocketmine\nbt\tag\CompoundTag;
}; use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\utils\MainLogger; use pocketmine\utils\MainLogger;
class Anvil extends McRegion{ class Anvil extends McRegion{
@ -39,28 +40,29 @@ class Anvil extends McRegion{
public function nbtSerialize(Chunk $chunk) : string{ public function nbtSerialize(Chunk $chunk) : string{
$nbt = new CompoundTag("Level", []); $nbt = new CompoundTag("Level", []);
$nbt->xPos = new IntTag("xPos", $chunk->getX()); $nbt->setInt("xPos", $chunk->getX());
$nbt->zPos = new IntTag("zPos", $chunk->getZ()); $nbt->setInt("zPos", $chunk->getZ());
$nbt->V = new ByteTag("V", 1); $nbt->setByte("V", 1);
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO $nbt->setLong("LastUpdate", 0); //TODO
$nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO $nbt->setLong("InhabitedTime", 0); //TODO
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated() ? 1 : 0); $nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated() ? 1 : 0); $nbt->setByte("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$nbt->Sections = new ListTag("Sections", [], NBT::TAG_Compound); $subChunks = [];
$subChunks = -1;
foreach($chunk->getSubChunks() as $y => $subChunk){ foreach($chunk->getSubChunks() as $y => $subChunk){
if($subChunk->isEmpty()){ if($subChunk->isEmpty()){
continue; continue;
} }
$tag = $this->serializeSubChunk($subChunk); $tag = $this->serializeSubChunk($subChunk);
$tag->setByte("Y", $y); $tag->setByte("Y", $y);
$nbt->Sections[++$subChunks] = $tag; $subChunks[] = $tag;
} }
$nbt->setTag(new ListTag("Sections", $subChunks, NBT::TAG_Compound));
$nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); $nbt->setByteArray("Biomes", $chunk->getBiomeIdArray());
$nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray()); $nbt->setIntArray("HeightMap", $chunk->getHeightMapArray());
$entities = []; $entities = [];
@ -71,7 +73,7 @@ class Anvil extends McRegion{
} }
} }
$nbt->Entities = new ListTag("Entities", $entities, NBT::TAG_Compound); $nbt->setTag(new ListTag("Entities", $entities, NBT::TAG_Compound));
$tiles = []; $tiles = [];
foreach($chunk->getTiles() as $tile){ foreach($chunk->getTiles() as $tile){
@ -79,7 +81,7 @@ class Anvil extends McRegion{
$tiles[] = $tile->namedtag; $tiles[] = $tile->namedtag;
} }
$nbt->TileEntities = new ListTag("TileEntities", $tiles, NBT::TAG_Compound); $nbt->setTag(new ListTag("TileEntities", $tiles, NBT::TAG_Compound));
//TODO: TileTicks //TODO: TileTicks
@ -104,43 +106,38 @@ class Anvil extends McRegion{
try{ try{
$nbt->readCompressed($data); $nbt->readCompressed($data);
$chunk = $nbt->getData(); $chunk = $nbt->getData()->getCompoundTag("Level");
if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ if($chunk === null){
throw new ChunkException("Invalid NBT format"); throw new ChunkException("Invalid NBT format");
} }
$chunk = $chunk->Level;
$subChunks = []; $subChunks = [];
if($chunk->Sections instanceof ListTag){ $subChunksTag = $chunk->getListTag("Sections") ?? [];
foreach($chunk->Sections as $subChunk){ foreach($subChunksTag as $subChunk){
if($subChunk instanceof CompoundTag){ if($subChunk instanceof CompoundTag){
$subChunks[$subChunk->Y->getValue()] = $this->deserializeSubChunk($subChunk); $subChunks[$subChunk->getByte("Y")] = $this->deserializeSubChunk($subChunk);
}
} }
} }
if(isset($chunk->BiomeColors)){ if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
$biomeIds = ChunkUtils::convertBiomeColors($chunk->BiomeColors->getValue()); //Convert back to original format $biomeIds = ChunkUtils::convertBiomeColors($chunk->getIntArray("BiomeColors")); //Convert back to original format
}elseif(isset($chunk->Biomes)){
$biomeIds = $chunk->Biomes->getValue();
}else{ }else{
$biomeIds = ""; $biomeIds = $chunk->getByteArray("Biomes", "", true);
} }
$result = new Chunk( $result = new Chunk(
$chunk["xPos"], $chunk->getInt("xPos"),
$chunk["zPos"], $chunk->getInt("zPos"),
$subChunks, $subChunks,
isset($chunk->Entities) ? $chunk->Entities->getValue() : [], $chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [],
isset($chunk->TileEntities) ? $chunk->TileEntities->getValue() : [], $chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [],
$biomeIds, $biomeIds,
isset($chunk->HeightMap) ? $chunk->HeightMap->getValue() : [] $chunk->getIntArray("HeightMap", [])
); );
$result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false); $result->setLightPopulated($chunk->getByte("LightPopulated", 0) !== 0);
$result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false); $result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
$result->setGenerated(true); $result->setGenerated();
return $result; return $result;
}catch(\Throwable $e){ }catch(\Throwable $e){
MainLogger::getLogger()->logException($e); MainLogger::getLogger()->logException($e);
@ -150,10 +147,10 @@ class Anvil extends McRegion{
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{ protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk( return new SubChunk(
ChunkUtils::reorderByteArray($subChunk->Blocks->getValue()), ChunkUtils::reorderByteArray($subChunk->getByteArray("Blocks")),
ChunkUtils::reorderNibbleArray($subChunk->Data->getValue()), ChunkUtils::reorderNibbleArray($subChunk->getByteArray("Data")),
ChunkUtils::reorderNibbleArray($subChunk->SkyLight->getValue(), "\xff"), ChunkUtils::reorderNibbleArray($subChunk->getByteArray("SkyLight"), "\xff"),
ChunkUtils::reorderNibbleArray($subChunk->BlockLight->getValue()) ChunkUtils::reorderNibbleArray($subChunk->getByteArray("BlockLight"))
); );
} }

View File

@ -53,12 +53,12 @@ class McRegion extends BaseLevelProvider{
*/ */
public function nbtSerialize(Chunk $chunk) : string{ public function nbtSerialize(Chunk $chunk) : string{
$nbt = new CompoundTag("Level", []); $nbt = new CompoundTag("Level", []);
$nbt->xPos = new IntTag("xPos", $chunk->getX()); $nbt->setInt("xPos", $chunk->getX());
$nbt->zPos = new IntTag("zPos", $chunk->getZ()); $nbt->setInt("zPos", $chunk->getZ());
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO $nbt->setLong("LastUpdate", 0); //TODO
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated() ? 1 : 0); $nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated() ? 1 : 0); $nbt->setByte("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$ids = ""; $ids = "";
$data = ""; $data = "";
@ -77,13 +77,13 @@ class McRegion extends BaseLevelProvider{
} }
} }
$nbt->Blocks = new ByteArrayTag("Blocks", $ids); $nbt->setByteArray("Blocks", $ids);
$nbt->Data = new ByteArrayTag("Data", $data); $nbt->setByteArray("Data", $data);
$nbt->SkyLight = new ByteArrayTag("SkyLight", $skyLight); $nbt->setByteArray("SkyLight", $skyLight);
$nbt->BlockLight = new ByteArrayTag("BlockLight", $blockLight); $nbt->setByteArray("BlockLight", $blockLight);
$nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); //doesn't exist in regular McRegion, this is here for PocketMine-MP only $nbt->setByteArray("Biomes", $chunk->getBiomeIdArray()); //doesn't exist in regular McRegion, this is here for PocketMine-MP only
$nbt->HeightMap = new ByteArrayTag("HeightMap", pack("C*", ...$chunk->getHeightMapArray())); $nbt->setByteArray("HeightMap", pack("C*", ...$chunk->getHeightMapArray())); //this is ByteArray in McRegion, but IntArray in Anvil (due to raised build height)
$entities = []; $entities = [];
@ -94,7 +94,7 @@ class McRegion extends BaseLevelProvider{
} }
} }
$nbt->Entities = new ListTag("Entities", $entities, NBT::TAG_Compound); $nbt->setTag(new ListTag("Entities", $entities, NBT::TAG_Compound));
$tiles = []; $tiles = [];
foreach($chunk->getTiles() as $tile){ foreach($chunk->getTiles() as $tile){
@ -102,7 +102,7 @@ class McRegion extends BaseLevelProvider{
$tiles[] = $tile->namedtag; $tiles[] = $tile->namedtag;
} }
$nbt->TileEntities = new ListTag("TileEntities", $tiles, NBT::TAG_Compound); $nbt->setTag(new ListTag("TileEntities", $tiles, NBT::TAG_Compound));
$writer = new NBT(NBT::BIG_ENDIAN); $writer = new NBT(NBT::BIG_ENDIAN);
$nbt->setName("Level"); $nbt->setName("Level");
@ -281,11 +281,11 @@ class McRegion extends BaseLevelProvider{
} }
public function getDifficulty() : int{ public function getDifficulty() : int{
return isset($this->levelData->Difficulty) ? $this->levelData->Difficulty->getValue() : Level::DIFFICULTY_NORMAL; return $this->levelData->getByte("Difficulty", Level::DIFFICULTY_NORMAL);
} }
public function setDifficulty(int $difficulty){ public function setDifficulty(int $difficulty){
$this->levelData->Difficulty = new ByteTag("Difficulty", $difficulty); $this->levelData->setByte("Difficulty", $difficulty);
} }
public function getChunk(int $chunkX, int $chunkZ, bool $create = false){ public function getChunk(int $chunkX, int $chunkZ, bool $create = false){

View File

@ -23,14 +23,9 @@ declare(strict_types=1);
namespace pocketmine\level\format\io\region; namespace pocketmine\level\format\io\region;
use pocketmine\level\format\Chunk;
use pocketmine\level\format\ChunkException;
use pocketmine\level\format\SubChunk; use pocketmine\level\format\SubChunk;
use pocketmine\nbt\NBT; use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\{ use pocketmine\nbt\tag\CompoundTag;
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag
};
use pocketmine\utils\MainLogger;
/** /**
* This format is exactly the same as the PC Anvil format, with the only difference being that the stored data order * This format is exactly the same as the PC Anvil format, with the only difference being that the stored data order
@ -51,10 +46,10 @@ class PMAnvil extends Anvil{
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{ protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk( return new SubChunk(
$subChunk->Blocks->getValue(), $subChunk->getByteArray("Blocks"),
$subChunk->Data->getValue(), $subChunk->getByteArray("Data"),
$subChunk->SkyLight->getValue(), $subChunk->getByteArray("SkyLight"),
$subChunk->BlockLight->getValue() $subChunk->getByteArray("BlockLight")
); );
} }

View File

@ -88,6 +88,23 @@ class ListTag extends NamedTag implements \ArrayAccess, \Countable{
return $count; return $count;
} }
public function getAllValues() : array{
$result = [];
foreach($this as $tag){
if(!($tag instanceof NamedTag)){
continue;
}
if($tag instanceof \ArrayAccess){
$result[] = $tag;
}else{
$result[] = $tag->getValue();
}
}
return $result;
}
public function offsetExists($offset){ public function offsetExists($offset){
return isset($this->{$offset}); return isset($this->{$offset});
} }

View File

@ -65,7 +65,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
public function __construct(Server $server){ public function __construct(Server $server){
$this->server = $server; $this->server = $server;
$this->rakLib = new RakLibServer($this->server->getLogger(), $this->server->getLoader(), $this->server->getPort(), $this->server->getIp() === "" ? "0.0.0.0" : $this->server->getIp(), false); $this->rakLib = new RakLibServer($this->server->getLogger(), \pocketmine\COMPOSER_AUTOLOADER_PATH, $this->server->getPort(), $this->server->getIp() === "" ? "0.0.0.0" : $this->server->getIp(), false);
$this->interface = new ServerHandler($this->rakLib, $this); $this->interface = new ServerHandler($this->rakLib, $this);
} }

View File

@ -107,10 +107,10 @@ abstract class Spawnable extends Tile{
*/ */
final public function getSpawnCompound() : CompoundTag{ final public function getSpawnCompound() : CompoundTag{
$nbt = new CompoundTag("", [ $nbt = new CompoundTag("", [
$this->namedtag->getTag("id"), $this->namedtag->getTag(self::TAG_ID),
$this->namedtag->getTag("x"), $this->namedtag->getTag(self::TAG_X),
$this->namedtag->getTag("y"), $this->namedtag->getTag(self::TAG_Y),
$this->namedtag->getTag("z") $this->namedtag->getTag(self::TAG_Z)
]); ]);
$this->addAdditionalSpawnData($nbt); $this->addAdditionalSpawnData($nbt);
return $nbt; return $nbt;

View File

@ -44,6 +44,11 @@ use pocketmine\Server;
abstract class Tile extends Position{ abstract class Tile extends Position{
const TAG_ID = "id";
const TAG_X = "x";
const TAG_Y = "y";
const TAG_Z = "z";
const BREWING_STAND = "BrewingStand"; const BREWING_STAND = "BrewingStand";
const CHEST = "Chest"; const CHEST = "Chest";
const ENCHANT_TABLE = "EnchantTable"; const ENCHANT_TABLE = "EnchantTable";
@ -136,14 +141,14 @@ abstract class Tile extends Position{
$this->namedtag = $nbt; $this->namedtag = $nbt;
$this->server = $level->getServer(); $this->server = $level->getServer();
$this->setLevel($level); $this->setLevel($level);
$this->chunk = $level->getChunk($this->namedtag->getInt("x") >> 4, $this->namedtag->getInt("z") >> 4, false); $this->chunk = $level->getChunk($this->namedtag->getInt(self::TAG_X) >> 4, $this->namedtag->getInt(self::TAG_Z) >> 4, false);
assert($this->chunk !== null); assert($this->chunk !== null);
$this->name = ""; $this->name = "";
$this->id = Tile::$tileCount++; $this->id = Tile::$tileCount++;
$this->x = $this->namedtag->getInt("x"); $this->x = $this->namedtag->getInt(self::TAG_X);
$this->y = $this->namedtag->getInt("y"); $this->y = $this->namedtag->getInt(self::TAG_Y);
$this->z = $this->namedtag->getInt("z"); $this->z = $this->namedtag->getInt(self::TAG_Z);
$this->chunk->addTile($this); $this->chunk->addTile($this);
$this->getLevel()->addTile($this); $this->getLevel()->addTile($this);
@ -154,10 +159,10 @@ abstract class Tile extends Position{
} }
public function saveNBT() : void{ public function saveNBT() : void{
$this->namedtag->setString("id", static::getSaveId()); $this->namedtag->setString(self::TAG_ID, static::getSaveId());
$this->namedtag->setInt("x", $this->x); $this->namedtag->setInt(self::TAG_X, $this->x);
$this->namedtag->setInt("y", $this->y); $this->namedtag->setInt(self::TAG_Y, $this->y);
$this->namedtag->setInt("z", $this->z); $this->namedtag->setInt(self::TAG_Z, $this->z);
} }
public function getNBT() : CompoundTag{ public function getNBT() : CompoundTag{
@ -167,7 +172,7 @@ abstract class Tile extends Position{
public function getCleanedNBT() : ?CompoundTag{ public function getCleanedNBT() : ?CompoundTag{
$this->saveNBT(); $this->saveNBT();
$tag = clone $this->namedtag; $tag = clone $this->namedtag;
$tag->removeTag("x", "y", "z", "id"); $tag->removeTag(self::TAG_X, self::TAG_Y, self::TAG_Z, self::TAG_ID);
if($tag->getCount() > 0){ if($tag->getCount() > 0){
return $tag; return $tag;
}else{ }else{
@ -187,10 +192,10 @@ abstract class Tile extends Position{
*/ */
public static function createNBT(Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : CompoundTag{ public static function createNBT(Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : CompoundTag{
$nbt = new CompoundTag("", [ $nbt = new CompoundTag("", [
new StringTag("id", static::getSaveId()), new StringTag(self::TAG_ID, static::getSaveId()),
new IntTag("x", (int) $pos->x), new IntTag(self::TAG_X, (int) $pos->x),
new IntTag("y", (int) $pos->y), new IntTag(self::TAG_Y, (int) $pos->y),
new IntTag("z", (int) $pos->z) new IntTag(self::TAG_Z, (int) $pos->z)
]); ]);
static::createAdditionalNBT($nbt, $pos, $face, $item, $player); static::createAdditionalNBT($nbt, $pos, $face, $item, $player);

@ -1 +0,0 @@
Subproject commit 0e26115330808b91ac3ab0b39e54fc9f9f679189

@ -1 +0,0 @@
Subproject commit cf7738721e7342c018a91ad300108b2dd95c7224