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"]
path = src/pocketmine/lang/locale
url = https://github.com/pmmp/PocketMine-Language.git

View File

@ -5,7 +5,7 @@
"homepage": "https://pmmp.io",
"license": "LGPL-3.0",
"require": {
"php": ">=7.2",
"php": ">=7.2.0RC3",
"ext-bcmath": "*",
"ext-curl": "*",
"ext-hash": "*",
@ -20,14 +20,23 @@
"ext-spl": "*",
"ext-yaml": ">=2.0.0",
"ext-zip": "*",
"ext-zlib": ">=1.2.11"
"ext-zlib": ">=1.2.11",
"pmmp/raklib": "^0.9.0",
"pmmp/pocketmine-spl": "^0.0.2"
},
"autoload": {
"exclude-from-classmap": [
"src/spl/stubs"
],
"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",
"This file is @generated automatically"
],
"content-hash": "d4fecad9dce5314493052c870c8cf059",
"packages": [],
"content-hash": "55bdbaf13ac4ea71f0705f03f715172d",
"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": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"php": 5,
"ext-pthreads": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.2",
"php": ">=7.2.0RC3",
"ext-bcmath": "*",
"ext-curl": "*",
"ext-hash": "*",

View File

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

View File

@ -129,30 +129,16 @@ namespace pocketmine {
define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR);
}
$requiredSplVer = "0.0.1";
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);
}
define('pocketmine\COMPOSER_AUTOLOADER_PATH', \pocketmine\PATH . 'vendor/autoload.php');
if(is_file(\pocketmine\PATH . "vendor/autoload.php")){
require_once(\pocketmine\PATH . "vendor/autoload.php");
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
}else{
echo "[CRITICAL] Composer autoloader not found" . PHP_EOL;
echo "[CRITICAL] Please initialize composer dependencies before running." . PHP_EOL;
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.
*/

View File

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

View File

@ -126,7 +126,7 @@ class Chest extends Transparent{
if(
!$this->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;
}

View File

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

View File

@ -53,17 +53,13 @@ class FallingSand extends Entity{
parent::initEntity();
$blockId = 0;
$damage = 0;
if(isset($this->namedtag->TileID)){
$blockId = (int) $this->namedtag["TileID"];
}elseif(isset($this->namedtag->Tile)){
$blockId = (int) $this->namedtag["Tile"];
$this->namedtag["TileID"] = new IntTag("TileID", $blockId);
}
if(isset($this->namedtag->Data)){
$damage = (int) $this->namedtag["Data"];
//TODO: 1.8+ save format
if($this->namedtag->hasTag("TileID", IntTag::class)){
$blockId = $this->namedtag->getInt("TileID");
}elseif($this->namedtag->hasTag("Tile", ByteTag::class)){
$blockId = $this->namedtag->getByte("Tile");
$this->namedtag->removeTag("Tile");
}
if($blockId === 0){
@ -71,6 +67,8 @@ class FallingSand extends Entity{
return;
}
$damage = $this->namedtag->getByte("Data", 0);
$this->block = BlockFactory::get($blockId, $damage);
$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(){
$this->namedtag->TileID = new IntTag("TileID", $this->block->getId());
$this->namedtag->Data = new ByteTag("Data", $this->block->getDamage());
$this->namedtag->setInt("TileID", $this->block->getId(), true);
$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\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
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.
*/
protected function initHumanData(){
if(isset($this->namedtag->NameTag)){
$this->setNameTag($this->namedtag["NameTag"]);
if($this->namedtag->hasTag("NameTag", StringTag::class)){
$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->namedtag->Skin["Name"],
$this->namedtag->Skin["Data"]
$skin->getString("Name"),
$skin->getString("Data")
));
}
@ -333,71 +333,39 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->inventory = new PlayerInventory($this);
$this->initHumanData();
if(isset($this->namedtag->Inventory) and $this->namedtag->Inventory instanceof ListTag){
foreach($this->namedtag->Inventory as $i => $item){
if($item["Slot"] >= 0 and $item["Slot"] < 9){ //Hotbar
$inventoryTag = $this->namedtag->getListTag("Inventory");
if($inventoryTag !== null){
/** @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)
unset($this->namedtag->Inventory->{$i});
}elseif($item["Slot"] >= 100 and $item["Slot"] < 104){ //Armor
$this->inventory->setItem($this->inventory->getSize() + $item["Slot"] - 100, ItemItem::nbtDeserialize($item));
unset($inventoryTag[$i]);
}elseif($slot >= 100 and $slot < 104){ //Armor
$this->inventory->setItem($this->inventory->getSize() + $slot - 100, ItemItem::nbtDeserialize($item));
}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->SelectedInventorySlot->getValue(), false);
}else{
$this->inventory->setHeldItemIndex(0, false);
}
$this->inventory->setHeldItemIndex($this->namedtag->getInt("SelectedInventorySlot", 0), false);
parent::initEntity();
if(!isset($this->namedtag->foodLevel) or !($this->namedtag->foodLevel instanceof IntTag)){
$this->namedtag->foodLevel = new IntTag("foodLevel", (int) $this->getFood());
}else{
$this->setFood((float) $this->namedtag["foodLevel"]);
}
$this->setFood((float) $this->namedtag->getInt("foodLevel", (int) $this->getFood(), true));
$this->setExhaustion($this->namedtag->getFloat("foodExhaustionLevel", $this->getExhaustion(), true));
$this->setSaturation($this->namedtag->getFloat("foodSaturationLevel", $this->getSaturation(), true));
$this->foodTickTimer = $this->namedtag->getInt("foodTickTimer", $this->foodTickTimer, true);
if(!isset($this->namedtag->foodExhaustionLevel) or !($this->namedtag->foodExhaustionLevel instanceof FloatTag)){
$this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion());
}else{
$this->setExhaustion((float) $this->namedtag["foodExhaustionLevel"]);
}
$this->setXpLevel($this->namedtag->getInt("XpLevel", $this->getXpLevel(), true));
$this->setXpProgress($this->namedtag->getFloat("XpP", $this->getXpProgress(), true));
$this->totalXp = $this->namedtag->getInt("XpTotal", $this->totalXp, true);
if(!isset($this->namedtag->foodSaturationLevel) or !($this->namedtag->foodSaturationLevel instanceof FloatTag)){
$this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation());
if($this->namedtag->hasTag("XpSeed", IntTag::class)){
$this->xpSeed = $this->namedtag->getInt("XpSeed");
}else{
$this->setSaturation((float) $this->namedtag["foodSaturationLevel"]);
}
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"];
$this->xpSeed = random_int(INT32_MIN, INT32_MAX);
}
}
@ -479,19 +447,25 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
public function saveNBT(){
parent::saveNBT();
$this->namedtag->foodLevel = new IntTag("foodLevel", (int) $this->getFood());
$this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion());
$this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation());
$this->namedtag->foodTickTimer = new IntTag("foodTickTimer", $this->foodTickTimer);
$this->namedtag->setInt("foodLevel", (int) $this->getFood(), true);
$this->namedtag->setFloat("foodExhaustionLevel", $this->getExhaustion(), true);
$this->namedtag->setFloat("foodSaturationLevel", $this->getSaturation(), true);
$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){
//Normal inventory
$slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize();
for($slot = $this->inventory->getHotbarSize(); $slot < $slotCount; ++$slot){
$item = $this->inventory->getItem($slot - 9);
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){
$item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100);
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){
$this->namedtag->Skin = new CompoundTag("Skin", [
$this->namedtag->setTag(new CompoundTag("Skin", [
//TODO: save cape & geometry
new StringTag("Data", $this->skin->getSkinData()),
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\ItemSpawnEvent;
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\Player;
@ -59,28 +56,19 @@ class Item extends Entity{
$this->setMaxHealth(5);
$this->setHealth((int) $this->namedtag["Health"]);
if(isset($this->namedtag->Age)){
$this->age = $this->namedtag["Age"];
}
if(isset($this->namedtag->PickupDelay)){
$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"];
}
$this->age = $this->namedtag->getShort("Age", $this->age);
$this->pickupDelay = $this->namedtag->getShort("PickupDelay", $this->pickupDelay);
$this->owner = $this->namedtag->getString("Owner", $this->owner);
$this->thrower = $this->namedtag->getString("Thrower", $this->thrower);
if(!isset($this->namedtag->Item)){
$itemTag = $this->namedtag->getCompoundTag("Item");
if($itemTag === null){
$this->close();
return;
}
assert($this->namedtag->Item instanceof CompoundTag);
$this->item = ItemItem::nbtDeserialize($this->namedtag->Item);
$this->item = ItemItem::nbtDeserialize($itemTag);
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
@ -139,15 +127,15 @@ class Item extends Entity{
public function saveNBT(){
parent::saveNBT();
$this->namedtag->Item = $this->item->nbtSerialize(-1, "Item");
$this->namedtag->Health = new ShortTag("Health", (int) $this->getHealth());
$this->namedtag->Age = new ShortTag("Age", $this->age);
$this->namedtag->PickupDelay = new ShortTag("PickupDelay", $this->pickupDelay);
$this->namedtag->setTag($this->item->nbtSerialize(-1, "Item"));
$this->namedtag->setShort("Health", (int) $this->getHealth());
$this->namedtag->setShort("Age", $this->age);
$this->namedtag->setShort("PickupDelay", $this->pickupDelay);
if($this->owner !== null){
$this->namedtag->Owner = new StringTag("Owner", $this->owner);
$this->namedtag->setString("Owner", $this->owner);
}
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(){
parent::initEntity();
if(isset($this->namedtag->HealF)){
$this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag["HealF"]);
unset($this->namedtag->HealF);
}elseif(isset($this->namedtag->Health)){
if(!($this->namedtag->Health instanceof FloatTag)){
$this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag->Health->getValue());
$health = $this->getMaxHealth();
if($this->namedtag->hasTag("HealF", FloatTag::class)){
$health = new FloatTag("Health", (float) $this->namedtag["HealF"]);
$this->namedtag->removeTag("HealF");
}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)){
foreach($this->namedtag->ActiveEffects->getValue() as $e){
$amplifier = Binary::unsignByte($e->Amplifier->getValue()); //0-255 only
/** @var CompoundTag[]|ListTag $activeEffectsTag */
$activeEffectsTag = $this->namedtag->getListTag("ActiveEffects");
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){
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);
}
@ -130,7 +134,7 @@ abstract class Living extends Entity implements Damageable{
public function saveNBT(){
parent::saveNBT();
$this->namedtag->Health = new FloatTag("Health", $this->getHealth());
$this->namedtag->setFloat("Health", $this->getHealth(), true);
if(count($this->effects) > 0){
$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{
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\ExplosionPrimeEvent;
use pocketmine\level\Explosion;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\network\mcpe\protocol\LevelEventPacket;
class PrimedTNT extends Entity implements Explosive{
@ -54,8 +54,8 @@ class PrimedTNT extends Entity implements Explosive{
protected function initEntity(){
parent::initEntity();
if(isset($this->namedtag->Fuse)){
$this->fuse = $this->namedtag["Fuse"];
if($this->namedtag->hasTag("Fuse", ShortTag::class)){
$this->fuse = $this->namedtag->getShort("Fuse");
}else{
$this->fuse = 80;
}
@ -73,7 +73,7 @@ class PrimedTNT extends Entity implements Explosive{
public function 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{

View File

@ -23,8 +23,6 @@ declare(strict_types=1);
namespace pocketmine\entity;
use pocketmine\nbt\tag\IntTag;
class Villager extends Creature implements NPC, Ageable{
const PROFESSION_FARMER = 0;
const PROFESSION_LIBRARIAN = 1;
@ -45,7 +43,7 @@ class Villager extends Creature implements NPC, Ageable{
parent::initEntity();
/** @var int $profession */
$profession = $this->namedtag["Profession"] ?? self::PROFESSION_FARMER;
$profession = $this->namedtag->getInt("Profession", self::PROFESSION_FARMER);
if($profession > 4 or $profession < 0){
$profession = self::PROFESSION_FARMER;
@ -56,7 +54,7 @@ class Villager extends Creature implements NPC, Ageable{
public function 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\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ShortTag;
abstract class Projectile extends Entity{
@ -62,9 +61,7 @@ abstract class Projectile extends Entity{
$this->setMaxHealth(1);
$this->setHealth(1);
if(isset($this->namedtag->Age)){
$this->age = $this->namedtag["Age"];
}
$this->age = $this->namedtag->getShort("Age", $this->age);
}
public function canCollideWith(Entity $entity) : bool{
@ -107,7 +104,7 @@ abstract class Projectile extends Entity{
public function saveNBT(){
parent::saveNBT();
$this->namedtag->Age = new ShortTag("Age", $this->age);
$this->namedtag->setShort("Age", $this->age);
}
protected function applyDragBeforeGravity() : bool{

View File

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

View File

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

View File

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

View File

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

View File

@ -96,26 +96,26 @@ class LevelDB extends BaseLevelProvider{
"compression" => LEVELDB_ZLIB_COMPRESSION
]);
if(isset($this->levelData->StorageVersion) and $this->levelData->StorageVersion->getValue() > self::CURRENT_STORAGE_VERSION){
throw new LevelException("Specified LevelDB world format version is newer than the version supported by the server");
if($this->levelData->getInt("StorageVersion", INT32_MAX, true) > self::CURRENT_STORAGE_VERSION){
throw new LevelException("Specified LevelDB world format version is not supported by " . \pocketmine\NAME);
}
if(!isset($this->levelData->generatorName)){
if(isset($this->levelData->Generator)){
switch((int) $this->levelData->Generator->getValue()){ //Detect correct generator from MCPE data
if(!$this->levelData->hasTag("generatorName", StringTag::class)){
if($this->levelData->hasTag("Generator", IntTag::class)){
switch($this->levelData->getInt("Generator")){ //Detect correct generator from MCPE data
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
$layers = trim($layers, "[]");
}else{
$layers = "7,3,3,2";
}
$this->levelData->generatorOptions = new StringTag("generatorOptions", "2;" . $layers . ";1");
$this->levelData->setString("generatorOptions", "2;" . $layers . ";1");
break;
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)
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->generatorOptions = new StringTag("generatorOptions", "");
$this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->setString("generatorOptions", "");
break;
case self::GENERATOR_LIMITED:
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");
}
}else{
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"));
}
}
if(!isset($this->levelData->generatorOptions)){
$this->levelData->generatorOptions = new StringTag("generatorOptions", "");
if(!$this->levelData->hasTag("generatorOptions", StringTag::class)){
$this->levelData->setString("generatorOptions", "");
}
}
@ -226,8 +226,8 @@ class LevelDB extends BaseLevelProvider{
}
public function saveLevelData(){
$this->levelData->NetworkVersion = new IntTag("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL);
$this->levelData->StorageVersion = new IntTag("StorageVersion", self::CURRENT_STORAGE_VERSION);
$this->levelData->setInt("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL);
$this->levelData->setInt("StorageVersion", self::CURRENT_STORAGE_VERSION);
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt->setData($this->levelData);
@ -251,11 +251,11 @@ class LevelDB extends BaseLevelProvider{
}
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){
$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{

View File

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

View File

@ -53,12 +53,12 @@ class McRegion extends BaseLevelProvider{
*/
public function nbtSerialize(Chunk $chunk) : string{
$nbt = new CompoundTag("Level", []);
$nbt->xPos = new IntTag("xPos", $chunk->getX());
$nbt->zPos = new IntTag("zPos", $chunk->getZ());
$nbt->setInt("xPos", $chunk->getX());
$nbt->setInt("zPos", $chunk->getZ());
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$nbt->setLong("LastUpdate", 0); //TODO
$nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->setByte("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$ids = "";
$data = "";
@ -77,13 +77,13 @@ class McRegion extends BaseLevelProvider{
}
}
$nbt->Blocks = new ByteArrayTag("Blocks", $ids);
$nbt->Data = new ByteArrayTag("Data", $data);
$nbt->SkyLight = new ByteArrayTag("SkyLight", $skyLight);
$nbt->BlockLight = new ByteArrayTag("BlockLight", $blockLight);
$nbt->setByteArray("Blocks", $ids);
$nbt->setByteArray("Data", $data);
$nbt->setByteArray("SkyLight", $skyLight);
$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->HeightMap = new ByteArrayTag("HeightMap", pack("C*", ...$chunk->getHeightMapArray()));
$nbt->setByteArray("Biomes", $chunk->getBiomeIdArray()); //doesn't exist in regular McRegion, this is here for PocketMine-MP only
$nbt->setByteArray("HeightMap", pack("C*", ...$chunk->getHeightMapArray())); //this is ByteArray in McRegion, but IntArray in Anvil (due to raised build height)
$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 = [];
foreach($chunk->getTiles() as $tile){
@ -102,7 +102,7 @@ class McRegion extends BaseLevelProvider{
$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);
$nbt->setName("Level");
@ -281,11 +281,11 @@ class McRegion extends BaseLevelProvider{
}
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){
$this->levelData->Difficulty = new ByteTag("Difficulty", $difficulty);
$this->levelData->setByte("Difficulty", $difficulty);
}
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;
use pocketmine\level\format\Chunk;
use pocketmine\level\format\ChunkException;
use pocketmine\level\format\SubChunk;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\{
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag
};
use pocketmine\utils\MainLogger;
use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag;
/**
* 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{
return new SubChunk(
$subChunk->Blocks->getValue(),
$subChunk->Data->getValue(),
$subChunk->SkyLight->getValue(),
$subChunk->BlockLight->getValue()
$subChunk->getByteArray("Blocks"),
$subChunk->getByteArray("Data"),
$subChunk->getByteArray("SkyLight"),
$subChunk->getByteArray("BlockLight")
);
}

View File

@ -88,6 +88,23 @@ class ListTag extends NamedTag implements \ArrayAccess, \Countable{
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){
return isset($this->{$offset});
}

View File

@ -65,7 +65,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
public function __construct(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);
}

View File

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

View File

@ -44,6 +44,11 @@ use pocketmine\Server;
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 CHEST = "Chest";
const ENCHANT_TABLE = "EnchantTable";
@ -136,14 +141,14 @@ abstract class Tile extends Position{
$this->namedtag = $nbt;
$this->server = $level->getServer();
$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);
$this->name = "";
$this->id = Tile::$tileCount++;
$this->x = $this->namedtag->getInt("x");
$this->y = $this->namedtag->getInt("y");
$this->z = $this->namedtag->getInt("z");
$this->x = $this->namedtag->getInt(self::TAG_X);
$this->y = $this->namedtag->getInt(self::TAG_Y);
$this->z = $this->namedtag->getInt(self::TAG_Z);
$this->chunk->addTile($this);
$this->getLevel()->addTile($this);
@ -154,10 +159,10 @@ abstract class Tile extends Position{
}
public function saveNBT() : void{
$this->namedtag->setString("id", static::getSaveId());
$this->namedtag->setInt("x", $this->x);
$this->namedtag->setInt("y", $this->y);
$this->namedtag->setInt("z", $this->z);
$this->namedtag->setString(self::TAG_ID, static::getSaveId());
$this->namedtag->setInt(self::TAG_X, $this->x);
$this->namedtag->setInt(self::TAG_Y, $this->y);
$this->namedtag->setInt(self::TAG_Z, $this->z);
}
public function getNBT() : CompoundTag{
@ -167,7 +172,7 @@ abstract class Tile extends Position{
public function getCleanedNBT() : ?CompoundTag{
$this->saveNBT();
$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){
return $tag;
}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{
$nbt = new CompoundTag("", [
new StringTag("id", static::getSaveId()),
new IntTag("x", (int) $pos->x),
new IntTag("y", (int) $pos->y),
new IntTag("z", (int) $pos->z)
new StringTag(self::TAG_ID, static::getSaveId()),
new IntTag(self::TAG_X, (int) $pos->x),
new IntTag(self::TAG_Y, (int) $pos->y),
new IntTag(self::TAG_Z, (int) $pos->z)
]);
static::createAdditionalNBT($nbt, $pos, $face, $item, $player);

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

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