diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 1d1cfd93b..a56d9469b 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -762,6 +762,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } + /** + * {@inheritdoc} + * + * If null is given, will additionally send the skin to the player itself as well as its viewers. + */ + public function sendSkin(array $targets = null) : void{ + parent::sendSkin($targets ?? $this->server->getOnlinePlayers()); + } + /** * Gets the player IP address * @@ -1988,24 +1997,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } - /* Mojang, some stupid reason, send every single model for every single skin in the selected skin-pack. - * Not only that, they are pretty-printed. This decode/encode is to get rid of the pretty-print, which cuts down - * significantly on the amount of wasted bytes. - * TODO: find out what model crap can be safely dropped from the packet (unless it gets fixed first) - */ - - $geometryJsonEncoded = base64_decode($packet->clientData["SkinGeometry"] ?? ""); - if($geometryJsonEncoded !== ""){ - $geometryJsonEncoded = json_encode(json_decode($geometryJsonEncoded)); - } - $skin = new Skin( $packet->clientData["SkinId"], base64_decode($packet->clientData["SkinData"] ?? ""), base64_decode($packet->clientData["CapeData"] ?? ""), $packet->clientData["SkinGeometryName"], - $geometryJsonEncoded + base64_decode($packet->clientData["SkinGeometry"] ?? "") ); + $skin->debloatGeometryData(); if(!$skin->isValid()){ $this->close("", "disconnectionScreen.invalidSkin"); @@ -2911,7 +2910,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } - $tile = $this->level->getTile($this->temporalVector->setComponents($packet->x, $packet->y, $packet->z)); + $tile = $this->level->getTileAt($packet->x, $packet->y, $packet->z); if($tile instanceof ItemFrame){ $ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $tile->getBlock(), null, 5 - $tile->getBlock()->getDamage(), PlayerInteractEvent::LEFT_CLICK_BLOCK); $this->server->getPluginManager()->callEvent($ev); diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 4f5695040..8c10cf69f 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -410,6 +410,12 @@ namespace pocketmine { return -1; } + /** + * @param int $start + * @param array|null $trace + * + * @return array + */ function getTrace($start = 0, $trace = null){ if($trace === null){ if(function_exists("xdebug_get_function_stack")){ diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index f7a206c8a..8f63c1f96 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -2156,6 +2156,10 @@ class Server{ } } + /** + * @param \Throwable $e + * @param array|null $trace + */ public function exceptionHandler(\Throwable $e, $trace = null){ if($e === null){ return; diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 689bf715d..f53c494eb 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -126,16 +126,20 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } $this->skin = $skin; + $this->skin->debloatGeometryData(); } /** - * @param Player[] $targets + * Sends the human's skin to the specified list of players. If null is given for targets, the skin will be sent to + * all viewers. + * + * @param Player[]|null $targets */ - public function sendSkin(array $targets) : void{ + public function sendSkin(array $targets = null) : void{ $pk = new PlayerSkinPacket(); $pk->uuid = $this->getUniqueId(); $pk->skin = $this->skin; - $this->server->broadcastPacket($targets, $pk); + $this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk); } public function jump(){ diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index 7c5e3b759..b1d04f192 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -87,4 +87,17 @@ class Skin{ return $this->geometryData; } + /** + * Hack to cut down on network overhead due to skins, by un-pretty-printing geometry JSON. + * + * Mojang, some stupid reason, send every single model for every single skin in the selected skin-pack. + * Not only that, they are pretty-printed. + * TODO: find out what model crap can be safely dropped from the packet (unless it gets fixed first) + */ + public function debloatGeometryData() : void{ + if($this->geometryData !== ""){ + $this->geometryData = (string) json_encode(json_decode($this->geometryData)); + } + } + } \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingManager.php b/src/pocketmine/inventory/CraftingManager.php index 3513971db..4d869c960 100644 --- a/src/pocketmine/inventory/CraftingManager.php +++ b/src/pocketmine/inventory/CraftingManager.php @@ -26,6 +26,7 @@ namespace pocketmine\inventory; use pocketmine\event\Timings; use pocketmine\item\Item; use pocketmine\item\ItemFactory; +use pocketmine\network\mcpe\protocol\BatchPacket; use pocketmine\network\mcpe\protocol\CraftingDataPacket; use pocketmine\Server; use pocketmine\utils\Config; @@ -35,19 +36,18 @@ use pocketmine\utils\UUID; class CraftingManager{ /** @var CraftingRecipe[] */ - public $recipes = []; + protected $recipes = []; /** @var ShapedRecipe[][] */ protected $shapedRecipes = []; /** @var ShapelessRecipe[][] */ protected $shapelessRecipes = []; - /** @var FurnaceRecipe[] */ - public $furnaceRecipes = []; + protected $furnaceRecipes = []; private static $RECIPE_COUNT = 0; - /** @var CraftingDataPacket */ + /** @var BatchPacket */ private $craftingDataCache; public function __construct(){ @@ -113,16 +113,21 @@ class CraftingManager{ $pk->encode(); - $this->craftingDataCache = $pk; + $batch = new BatchPacket(); + $batch->addPacket($pk); + $batch->setCompressionLevel(Server::getInstance()->networkCompressionLevel); + $batch->encode(); + + $this->craftingDataCache = $batch; Timings::$craftingDataCacheRebuildTimer->stopTiming(); } /** - * Returns a CraftingDataPacket for sending to players. Rebuilds the cache if it is outdated. + * Returns a pre-compressed CraftingDataPacket for sending to players. Rebuilds the cache if it is not found. * - * @return CraftingDataPacket + * @return BatchPacket */ - public function getCraftingDataPacket() : CraftingDataPacket{ + public function getCraftingDataPacket() : BatchPacket{ if($this->craftingDataCache === null){ $this->buildCraftingDataCache(); } @@ -230,7 +235,7 @@ class CraftingManager{ /** @var Item[] $row */ foreach($map as $y => $row){ foreach($row as $x => $item){ - $item = clone $item; + $map[$y][$x] = clone $item; } } diff --git a/src/pocketmine/inventory/MultiRecipe.php b/src/pocketmine/inventory/MultiRecipe.php index 453e77f63..c63cbbb1e 100644 --- a/src/pocketmine/inventory/MultiRecipe.php +++ b/src/pocketmine/inventory/MultiRecipe.php @@ -26,6 +26,14 @@ namespace pocketmine\inventory; use pocketmine\utils\UUID; class MultiRecipe{ + const TYPE_REPAIR_ITEM = "00000000-0000-0000-0000-000000000001"; + const TYPE_MAP_EXTENDING = "D392B075-4BA1-40AE-8789-AF868D56F6CE"; + const TYPE_MAP_CLONING = "85939755-BA10-4D9D-A4CC-EFB7A8E943C4"; + const TYPE_MAP_UPGRADING = "AECD2294-4B94-434B-8667-4499BB2C9327"; + const TYPE_BOOK_CLONING = "D1CA6B84-338E-4F2F-9C6B-76CC8B4BD98D"; + const TYPE_BANNER_DUPLICATE = "B5C5D105-75A2-4076-AF2B-923EA2BF4BF0"; + const TYPE_BANNER_ADD_PATTERN = "D81AAEAF-E172-4440-9225-868DF030D27B"; + const TYPE_FIREWORKS = "00000000-0000-0000-0000-000000000002"; private $uuid; diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index 604eba574..7bc6d53e0 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -211,7 +211,7 @@ class Explosion{ $this->level->setBlockIdAt($block->x, $block->y, $block->z, 0); - $t = $this->level->getTile($block); + $t = $this->level->getTileAt($block->x, $block->y, $block->z); if($t instanceof Tile){ if($yieldDrops and $t instanceof Container){ if($t instanceof Chest){ diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 1db236ca1..47a029651 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -1962,17 +1962,33 @@ class Level implements ChunkManager, Metadatable{ } /** - * Returns the Tile in a position, or null if not found + * Returns the Tile in a position, or null if not found. + * + * Note: This method wraps getTileAt(). If you're guaranteed to be passing integers, and you're using this method + * in performance-sensitive code, consider using getTileAt() instead of this method for better performance. * * @param Vector3 $pos * * @return Tile|null */ - public function getTile(Vector3 $pos){ - $chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false); + public function getTile(Vector3 $pos) : ?Tile{ + return $this->getTileAt((int) floor($pos->x), (int) floor($pos->y), (int) floor($pos->z)); + } + + /** + * Returns the tile at the specified x,y,z coordinates, or null if it does not exist. + * + * @param int $x + * @param int $y + * @param int $z + * + * @return Tile|null + */ + public function getTileAt(int $x, int $y, int $z) : ?Tile{ + $chunk = $this->getChunk($x >> 4, $z >> 4); if($chunk !== null){ - return $chunk->getTile($pos->x & 0x0f, $pos->y, $pos->z & 0x0f); + return $chunk->getTile($x & 0x0f, $y, $z & 0x0f); } return null; diff --git a/src/pocketmine/level/format/io/region/Anvil.php b/src/pocketmine/level/format/io/region/Anvil.php index 5e21170bf..7e7268a04 100644 --- a/src/pocketmine/level/format/io/region/Anvil.php +++ b/src/pocketmine/level/format/io/region/Anvil.php @@ -54,13 +54,9 @@ class Anvil extends McRegion{ if($subChunk->isEmpty()){ continue; } - $nbt->Sections[++$subChunks] = new CompoundTag("", [ - new ByteTag("Y", $y), - new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY - new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())), - new ByteArrayTag("SkyLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockSkyLightArray(), "\xff")), - new ByteArrayTag("BlockLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockLightArray())) - ]); + $tag = $this->serializeSubChunk($subChunk); + $tag->setByte("Y", $y); + $nbt->Sections[++$subChunks] = $tag; } $nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); @@ -94,6 +90,15 @@ class Anvil extends McRegion{ return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); } + protected function serializeSubChunk(SubChunk $subChunk) : CompoundTag{ + return new CompoundTag("", [ + new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY + new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())), + new ByteArrayTag("SkyLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockSkyLightArray(), "\xff")), + new ByteArrayTag("BlockLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockLightArray())) + ]); + } + public function nbtDeserialize(string $data){ $nbt = new NBT(NBT::BIG_ENDIAN); try{ @@ -111,12 +116,7 @@ class Anvil extends McRegion{ if($chunk->Sections instanceof ListTag){ foreach($chunk->Sections as $subChunk){ if($subChunk instanceof CompoundTag){ - $subChunks[$subChunk->Y->getValue()] = new SubChunk( - ChunkUtils::reorderByteArray($subChunk->Blocks->getValue()), - ChunkUtils::reorderNibbleArray($subChunk->Data->getValue()), - ChunkUtils::reorderNibbleArray($subChunk->SkyLight->getValue(), "\xff"), - ChunkUtils::reorderNibbleArray($subChunk->BlockLight->getValue()) - ); + $subChunks[$subChunk->Y->getValue()] = $this->deserializeSubChunk($subChunk); } } } @@ -148,6 +148,15 @@ 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()) + ); + } + public static function getProviderName() : string{ return "anvil"; } diff --git a/src/pocketmine/level/format/io/region/PMAnvil.php b/src/pocketmine/level/format/io/region/PMAnvil.php index 10e436ae2..6e7cef20d 100644 --- a/src/pocketmine/level/format/io/region/PMAnvil.php +++ b/src/pocketmine/level/format/io/region/PMAnvil.php @@ -40,108 +40,22 @@ class PMAnvil extends Anvil{ const REGION_FILE_EXTENSION = "mcapm"; - 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->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->Sections = new ListTag("Sections", [], NBT::TAG_Compound); - - $subChunks = -1; - foreach($chunk->getSubChunks() as $y => $subChunk){ - if($subChunk->isEmpty()){ - continue; - } - $nbt->Sections[++$subChunks] = new CompoundTag("", [ - new ByteTag("Y", $y), - new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()), - new ByteArrayTag("Data", $subChunk->getBlockDataArray()), - new ByteArrayTag("SkyLight", $subChunk->getBlockSkyLightArray()), - new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray()) - ]); - } - - $nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); - $nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray()); - - $entities = []; - - foreach($chunk->getEntities() as $entity){ - if($entity->canSaveWithChunk() and !$entity->isClosed()){ - $entity->saveNBT(); - $entities[] = $entity->namedtag; - } - } - - $nbt->Entities = new ListTag("Entities", $entities, NBT::TAG_Compound); - - $tiles = []; - foreach($chunk->getTiles() as $tile){ - $tile->saveNBT(); - $tiles[] = $tile->namedtag; - } - - $nbt->TileEntities = new ListTag("TileEntities", $tiles, NBT::TAG_Compound); - - //TODO: TileTicks - - $writer = new NBT(NBT::BIG_ENDIAN); - $nbt->setName("Level"); - $writer->setData(new CompoundTag("", [$nbt])); - - return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); + protected function serializeSubChunk(SubChunk $subChunk) : CompoundTag{ + return new CompoundTag("", [ + new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()), + new ByteArrayTag("Data", $subChunk->getBlockDataArray()), + new ByteArrayTag("SkyLight", $subChunk->getBlockSkyLightArray()), + new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray()) + ]); } - public function nbtDeserialize(string $data){ - $nbt = new NBT(NBT::BIG_ENDIAN); - try{ - $nbt->readCompressed($data); - - $chunk = $nbt->getData(); - - if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ - 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()] = new SubChunk( - $subChunk->Blocks->getValue(), - $subChunk->Data->getValue(), - $subChunk->SkyLight->getValue(), - $subChunk->BlockLight->getValue() - ); - } - } - } - - $result = new Chunk( - $chunk["xPos"], - $chunk["zPos"], - $subChunks, - isset($chunk->Entities) ? $chunk->Entities->getValue() : [], - isset($chunk->TileEntities) ? $chunk->TileEntities->getValue() : [], - isset($chunk->Biomes) ? $chunk->Biomes->getValue() : "", - isset($chunk->HeightMap) ? $chunk->HeightMap->getValue() : [] - ); - $result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false); - $result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false); - $result->setGenerated(true); - return $result; - }catch(\Throwable $e){ - MainLogger::getLogger()->logException($e); - return null; - } + protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{ + return new SubChunk( + $subChunk->Blocks->getValue(), + $subChunk->Data->getValue(), + $subChunk->SkyLight->getValue(), + $subChunk->BlockLight->getValue() + ); } public static function getProviderName() : string{ diff --git a/src/pocketmine/nbt/tag/CompoundTag.php b/src/pocketmine/nbt/tag/CompoundTag.php index 3fdd4d5f6..496a3a55a 100644 --- a/src/pocketmine/nbt/tag/CompoundTag.php +++ b/src/pocketmine/nbt/tag/CompoundTag.php @@ -151,22 +151,24 @@ class CompoundTag extends NamedTag implements \ArrayAccess{ /** * Returns the value of the child tag with the specified name, or $default if the tag doesn't exist. If the child - * tag is not of type $expectedType, an exception will be thrown. + * tag is not of type $expectedType, an exception will be thrown, unless a default is given and $badTagDefault is + * true. * * @param string $name * @param string $expectedClass * @param mixed $default + * @param bool $badTagDefault Return the specified default if the tag is not of the expected type. * * @return mixed */ - public function getTagValue(string $name, string $expectedClass, $default = null){ - $tag = $this->getTag($name, $expectedClass); - if($tag !== null){ + public function getTagValue(string $name, string $expectedClass, $default = null, bool $badTagDefault = false){ + $tag = $this->getTag($name, $badTagDefault ? NamedTag::class : $expectedClass); + if($tag instanceof $expectedClass){ return $tag->getValue(); } if($default === null){ - throw new \RuntimeException("Tag with name \"$name\" not found and no default value given"); + throw new \RuntimeException("Tag with name \"$name\" " . ($tag !== null ? "not of expected type" : "not found") . " and no valid default value given"); } return $default; @@ -179,91 +181,100 @@ class CompoundTag extends NamedTag implements \ArrayAccess{ /** * @param string $name * @param int|null $default + * @param bool $badTagDefault * * @return int */ - public function getByte(string $name, ?int $default = null) : int{ - return $this->getTagValue($name, ByteTag::class, $default); + public function getByte(string $name, ?int $default = null, bool $badTagDefault = false) : int{ + return $this->getTagValue($name, ByteTag::class, $default, $badTagDefault); } /** * @param string $name * @param int|null $default + * @param bool $badTagDefault * * @return int */ - public function getShort(string $name, ?int $default = null) : int{ - return $this->getTagValue($name, ShortTag::class, $default); + public function getShort(string $name, ?int $default = null, bool $badTagDefault = false) : int{ + return $this->getTagValue($name, ShortTag::class, $default, $badTagDefault); } /** * @param string $name * @param int|null $default + * @param bool $badTagDefault * * @return int */ - public function getInt(string $name, ?int $default = null) : int{ - return $this->getTagValue($name, IntTag::class, $default); + public function getInt(string $name, ?int $default = null, bool $badTagDefault = false) : int{ + return $this->getTagValue($name, IntTag::class, $default, $badTagDefault); } /** * @param string $name * @param int|null $default + * @param bool $badTagDefault * * @return int */ - public function getLong(string $name, ?int $default = null) : int{ - return $this->getTagValue($name, LongTag::class, $default); + public function getLong(string $name, ?int $default = null, bool $badTagDefault = false) : int{ + return $this->getTagValue($name, LongTag::class, $default, $badTagDefault); } /** * @param string $name * @param float|null $default + * @param bool $badTagDefault * * @return float */ - public function getFloat(string $name, ?float $default = null) : float{ - return $this->getTagValue($name, FloatTag::class, $default); + public function getFloat(string $name, ?float $default = null, bool $badTagDefault = false) : float{ + return $this->getTagValue($name, FloatTag::class, $default, $badTagDefault); } /** * @param string $name * @param float|null $default + * @param bool $badTagDefault * * @return float */ - public function getDouble(string $name, ?float $default = null) : float{ - return $this->getTagValue($name, DoubleTag::class, $default); + public function getDouble(string $name, ?float $default = null, bool $badTagDefault = false) : float{ + return $this->getTagValue($name, DoubleTag::class, $default, $badTagDefault); } /** * @param string $name - * @param null|string $default + * @param string|null $default + * @param bool $badTagDefault * * @return string */ - public function getByteArray(string $name, ?string $default = null) : string{ - return $this->getTagValue($name, ByteArrayTag::class, $default); + public function getByteArray(string $name, ?string $default = null, bool $badTagDefault = false) : string{ + return $this->getTagValue($name, ByteArrayTag::class, $default, $badTagDefault); } /** * @param string $name - * @param null|string $default + * @param string|null $default + * @param bool $badTagDefault * * @return string */ - public function getString(string $name, ?string $default = null) : string{ - return $this->getTagValue($name, StringTag::class, $default); + public function getString(string $name, ?string $default = null, bool $badTagDefault = false) : string{ + return $this->getTagValue($name, StringTag::class, $default, $badTagDefault); } /** * @param string $name * @param int[]|null $default + * @param bool $badTagDefault * * @return int[] */ - public function getIntArray(string $name, ?array $default = null) : array{ - return $this->getTagValue($name, IntArrayTag::class, $default); + public function getIntArray(string $name, ?array $default = null, bool $badTagDefault = false) : array{ + return $this->getTagValue($name, IntArrayTag::class, $default, $badTagDefault); } /** @@ -273,11 +284,12 @@ class CompoundTag extends NamedTag implements \ArrayAccess{ * @param string $name Name of the tag to set * @param string $tagClass Class that extends NamedTag * @param mixed $value Value to set. This should be compatible with the specified tag type. + * @param bool $force Force set the value even if the existing tag is not the correct type (overwrite the old tag) */ - public function setTagValue(string $name, string $tagClass, $value) : void{ + public function setTagValue(string $name, string $tagClass, $value, bool $force) : void{ assert(is_a($tagClass, NamedTag::class, true)); - $tag = $this->getTag($name, $tagClass); - if($tag !== null){ + $tag = $this->getTag($name, $force ? NamedTag::class : $tagClass); + if($tag instanceof $tagClass){ $tag->setValue($value); }else{ $this->setTag(new $tagClass($name, $value)); @@ -291,73 +303,82 @@ class CompoundTag extends NamedTag implements \ArrayAccess{ /** * @param string $name * @param int $value + * @param bool $force */ - public function setByte(string $name, int $value) : void{ - $this->setTagValue($name, ByteTag::class, $value); + public function setByte(string $name, int $value, bool $force = false) : void{ + $this->setTagValue($name, ByteTag::class, $value, $force); } /** * @param string $name * @param int $value + * @param bool $force */ - public function setShort(string $name, int $value) : void{ - $this->setTagValue($name, ShortTag::class, $value); + public function setShort(string $name, int $value, bool $force = false) : void{ + $this->setTagValue($name, ShortTag::class, $value, $force); } /** * @param string $name * @param int $value + * @param bool $force */ - public function setInt(string $name, int $value) : void{ - $this->setTagValue($name, IntTag::class, $value); + public function setInt(string $name, int $value, bool $force = false) : void{ + $this->setTagValue($name, IntTag::class, $value, $force); } /** * @param string $name * @param int $value + * @param bool $force */ - public function setLong(string $name, int $value) : void{ - $this->setTagValue($name, LongTag::class, $value); + public function setLong(string $name, int $value, bool $force = false) : void{ + $this->setTagValue($name, LongTag::class, $value, $force); } /** * @param string $name * @param float $value + * @param bool $force */ - public function setFloat(string $name, float $value) : void{ - $this->setTagValue($name, FloatTag::class, $value); + public function setFloat(string $name, float $value, bool $force = false) : void{ + $this->setTagValue($name, FloatTag::class, $value, $force); } /** * @param string $name * @param float $value + * @param bool $force */ - public function setDouble(string $name, float $value) : void{ - $this->setTagValue($name, DoubleTag::class, $value); + public function setDouble(string $name, float $value, bool $force = false) : void{ + $this->setTagValue($name, DoubleTag::class, $value, $force); } /** * @param string $name * @param string $value + * @param bool $force */ - public function setByteArray(string $name, string $value) : void{ - $this->setTagValue($name, ByteArrayTag::class, $value); + public function setByteArray(string $name, string $value, bool $force = false) : void{ + $this->setTagValue($name, ByteArrayTag::class, $value, $force); } /** * @param string $name * @param string $value + * @param bool $force */ - public function setString(string $name, string $value) : void{ - $this->setTagValue($name, StringTag::class, $value); + public function setString(string $name, string $value, bool $force = false) : void{ + $this->setTagValue($name, StringTag::class, $value, $force); } /** * @param string $name * @param int[] $value + * @param bool $force */ - public function setIntArray(string $name, array $value) : void{ - $this->setTagValue($name, IntArrayTag::class, $value); + public function setIntArray(string $name, array $value, bool $force = false) : void{ + $this->setTagValue($name, IntArrayTag::class, $value, $force); } diff --git a/src/pocketmine/network/mcpe/protocol/EntityPickRequestPacket.php b/src/pocketmine/network/mcpe/protocol/EntityPickRequestPacket.php index 323ddbb35..e893c7783 100644 --- a/src/pocketmine/network/mcpe/protocol/EntityPickRequestPacket.php +++ b/src/pocketmine/network/mcpe/protocol/EntityPickRequestPacket.php @@ -31,17 +31,17 @@ class EntityPickRequestPacket extends DataPacket{ const NETWORK_ID = ProtocolInfo::ENTITY_PICK_REQUEST_PACKET; /** @var int */ - public $entityTypeId; + public $entityUniqueId; /** @var int */ public $hotbarSlot; protected function decodePayload(){ - $this->entityTypeId = $this->getLLong(); + $this->entityUniqueId = $this->getLLong(); $this->hotbarSlot = $this->getByte(); } protected function encodePayload(){ - $this->putLLong($this->entityTypeId); + $this->putLLong($this->entityUniqueId); $this->putByte($this->hotbarSlot); } diff --git a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php index 9caeac76f..e71e9fba9 100644 --- a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php +++ b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php @@ -45,5 +45,6 @@ interface WindowTypes{ const STRUCTURE_EDITOR = 14; const TRADING = 15; const COMMAND_BLOCK = 16; + const JUKEBOX = 17; } \ No newline at end of file diff --git a/src/pocketmine/tile/Bed.php b/src/pocketmine/tile/Bed.php index fb1351f72..6c241f9b9 100644 --- a/src/pocketmine/tile/Bed.php +++ b/src/pocketmine/tile/Bed.php @@ -35,8 +35,8 @@ class Bed extends Spawnable{ const TAG_COLOR = "color"; public function __construct(Level $level, CompoundTag $nbt){ - if(!$nbt->hasTag(self::TAG_COLOR, ByteTag::class)){ - $nbt->setTag(new ByteTag(self::TAG_COLOR, 14)); //default to old red + if(!$nbt->hasTag(self::TAG_COLOR, ByteTag::class)){ //TODO: check PC format + $nbt->setByte(self::TAG_COLOR, 14, true); //default to old red } parent::__construct($level, $nbt); } diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index a97760a4e..8f2a01a3b 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -136,7 +136,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ */ public function getPair() : ?Chest{ if($this->isPaired()){ - $tile = $this->getLevel()->getTile(new Vector3($this->namedtag->getInt(self::TAG_PAIRX), $this->y, $this->namedtag->getInt(self::TAG_PAIRZ))); + $tile = $this->getLevel()->getTileAt($this->namedtag->getInt(self::TAG_PAIRX), $this->y, $this->namedtag->getInt(self::TAG_PAIRZ)); if($tile instanceof Chest){ return $tile; } @@ -152,8 +152,8 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ $this->createPair($tile); - $this->spawnToAll(); - $tile->spawnToAll(); + $this->onChanged(); + $tile->onChanged(); $this->checkPairing(); return true; @@ -175,12 +175,12 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ $tile = $this->getPair(); $this->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ); - $this->spawnToAll(); + $this->onChanged(); if($tile instanceof Chest){ $tile->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ); $tile->checkPairing(); - $tile->spawnToAll(); + $tile->onChanged(); } $this->checkPairing(); diff --git a/src/pocketmine/tile/FlowerPot.php b/src/pocketmine/tile/FlowerPot.php index 0ae6a58dd..7dc3fad36 100644 --- a/src/pocketmine/tile/FlowerPot.php +++ b/src/pocketmine/tile/FlowerPot.php @@ -37,11 +37,12 @@ class FlowerPot extends Spawnable{ const TAG_ITEM_DATA = "mData"; public function __construct(Level $level, CompoundTag $nbt){ + //TODO: check PC format if(!$nbt->hasTag(self::TAG_ITEM, ShortTag::class)){ - $nbt->setTag(new ShortTag(self::TAG_ITEM, 0)); + $nbt->setShort(self::TAG_ITEM, 0, true); } if(!$nbt->hasTag(self::TAG_ITEM_DATA, IntTag::class)){ - $nbt->setTag(new IntTag(self::TAG_ITEM_DATA, 0)); + $nbt->setInt(self::TAG_ITEM_DATA, 0, true); } parent::__construct($level, $nbt); } diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index 91289501c..a0f9a1b73 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -54,7 +54,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ public function __construct(Level $level, CompoundTag $nbt){ if(!$nbt->hasTag(self::TAG_BURN_TIME, ShortTag::class) or $nbt->getShort(self::TAG_BURN_TIME) < 0){ - $nbt->setTag(new ShortTag(self::TAG_BURN_TIME, 0)); + $nbt->setShort(self::TAG_BURN_TIME, 0, true); } if( @@ -62,16 +62,16 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ $nbt->getShort(self::TAG_COOK_TIME) < 0 or ($nbt->getShort(self::TAG_BURN_TIME) === 0 and $nbt->getShort(self::TAG_COOK_TIME) > 0) ){ - $nbt->setTag(new ShortTag(self::TAG_COOK_TIME, 0)); + $nbt->setShort(self::TAG_COOK_TIME, 0, true); } if(!$nbt->hasTag(self::TAG_MAX_TIME, ShortTag::class)){ - $nbt->setTag(new ShortTag(self::TAG_MAX_TIME, $nbt->getShort(self::TAG_BURN_TIME))); + $nbt->setShort(self::TAG_MAX_TIME, $nbt->getShort(self::TAG_BURN_TIME), true); $nbt->removeTag(self::TAG_BURN_TICKS); } if(!$nbt->getTag(self::TAG_BURN_TICKS, ShortTag::class)){ - $nbt->setTag(new ShortTag(self::TAG_BURN_TICKS, 0)); + $nbt->setShort(self::TAG_BURN_TICKS, 0, true); } parent::__construct($level, $nbt); diff --git a/src/pocketmine/tile/ItemFrame.php b/src/pocketmine/tile/ItemFrame.php index 5c6e3396e..6bf003e7c 100644 --- a/src/pocketmine/tile/ItemFrame.php +++ b/src/pocketmine/tile/ItemFrame.php @@ -39,11 +39,11 @@ class ItemFrame extends Spawnable{ public function __construct(Level $level, CompoundTag $nbt){ if(!$nbt->hasTag(self::TAG_ITEM_ROTATION, ByteTag::class)){ - $nbt->setTag(new ByteTag(self::TAG_ITEM_ROTATION, 0)); + $nbt->setByte(self::TAG_ITEM_ROTATION, 0, true); } if(!$nbt->hasTag(self::TAG_ITEM_DROP_CHANCE, FloatTag::class)){ - $nbt->setTag(new FloatTag(self::TAG_ITEM_DROP_CHANCE, 1.0)); + $nbt->setFloat(self::TAG_ITEM_DROP_CHANCE, 1.0, true); } parent::__construct($level, $nbt); diff --git a/src/pocketmine/tile/Skull.php b/src/pocketmine/tile/Skull.php index fa6b30c1e..891b8cbb3 100644 --- a/src/pocketmine/tile/Skull.php +++ b/src/pocketmine/tile/Skull.php @@ -38,15 +38,17 @@ class Skull extends Spawnable{ const TYPE_CREEPER = 4; const TYPE_DRAGON = 5; - const TAG_SKULL_TYPE = "SkullType"; - const TAG_ROT = "Rot"; + const TAG_SKULL_TYPE = "SkullType"; //TAG_Byte + const TAG_ROT = "Rot"; //TAG_Byte + const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte + const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int public function __construct(Level $level, CompoundTag $nbt){ if(!$nbt->hasTag(self::TAG_SKULL_TYPE, ByteTag::class)){ - $nbt->setTag(new ByteTag(self::TAG_SKULL_TYPE, 0)); + $nbt->setByte(self::TAG_SKULL_TYPE, 0, true); } if(!$nbt->hasTag(self::TAG_ROT, ByteTag::class)){ - $nbt->setTag(new ByteTag(self::TAG_ROT, 0)); + $nbt->setByte(self::TAG_ROT, 0, true); } parent::__construct($level, $nbt); } diff --git a/src/pocketmine/utils/Binary.php b/src/pocketmine/utils/Binary.php index 5677549c7..ada1ac1b6 100644 --- a/src/pocketmine/utils/Binary.php +++ b/src/pocketmine/utils/Binary.php @@ -164,8 +164,7 @@ class Binary{ /** * Reads a 16-bit signed little-endian number * - * @param $str - * + * @param string $str * @return int */ public static function readSignedLShort(string $str) : int{ @@ -175,8 +174,7 @@ class Binary{ /** * Writes a 16-bit signed/unsigned little-endian number * - * @param $value - * + * @param int $value * @return string */ public static function writeLShort(int $value) : string{ @@ -379,12 +377,11 @@ class Binary{ /** * Reads an 8-byte integer. * - * @param string $x + * @param string $str * @return int */ - public static function readLong(string $x) : int{ - $int = unpack("N*", $x); - return ($int[1] << 32) | $int[2]; + public static function readLong(string $str) : int{ + return unpack("J", $str)[1]; } /** @@ -394,7 +391,7 @@ class Binary{ * @return string */ public static function writeLong(int $value) : string{ - return pack("NN", $value >> 32, $value & 0xFFFFFFFF); + return pack("J", $value); } /** @@ -404,7 +401,7 @@ class Binary{ * @return int */ public static function readLLong(string $str) : int{ - return self::readLong(strrev($str)); + return unpack("P", $str)[1]; } /** @@ -414,7 +411,7 @@ class Binary{ * @return string */ public static function writeLLong(int $value) : string{ - return strrev(self::writeLong($value)); + return pack("P", $value); } diff --git a/src/pocketmine/utils/Config.php b/src/pocketmine/utils/Config.php index 9831e7465..5075a7ac2 100644 --- a/src/pocketmine/utils/Config.php +++ b/src/pocketmine/utils/Config.php @@ -342,7 +342,7 @@ class Config{ } $base = $value; - $this->nestedCache[$key] = $value; + $this->nestedCache = []; } /** @@ -376,6 +376,26 @@ class Config{ return $this->nestedCache[$key] = $base; } + public function removeNested(string $key) : void{ + $this->nestedCache = []; + + $vars = explode(".", $key); + + $currentNode =& $this->config; + while(count($vars) > 0){ + $nodeName = array_shift($vars); + if(isset($currentNode[$nodeName])){ + if(empty($vars)){ //final node + unset($currentNode[$nodeName]); + }elseif(is_array($currentNode[$nodeName])){ + $currentNode =& $currentNode[$nodeName]; + } + }else{ + break; + } + } + } + /** * @param $k * @param mixed $default diff --git a/src/pocketmine/utils/MainLogger.php b/src/pocketmine/utils/MainLogger.php index 8e97d6104..0f9426a27 100644 --- a/src/pocketmine/utils/MainLogger.php +++ b/src/pocketmine/utils/MainLogger.php @@ -119,6 +119,10 @@ class MainLogger extends \AttachableThreadedLogger{ $this->logDebug = $logDebug; } + /** + * @param \Throwable $e + * @param array|null $trace + */ public function logException(\Throwable $e, $trace = null){ if($trace === null){ $trace = $e->getTrace(); diff --git a/src/raklib b/src/raklib index 9142e7dec..a10881b00 160000 --- a/src/raklib +++ b/src/raklib @@ -1 +1 @@ -Subproject commit 9142e7decec23b302ab470a55e5ed160df4878db +Subproject commit a10881b0078559f646b570a12304dba71658357c diff --git a/tests/plugins/PocketMine-DevTools b/tests/plugins/PocketMine-DevTools index 4e28d74c9..3446bcc77 160000 --- a/tests/plugins/PocketMine-DevTools +++ b/tests/plugins/PocketMine-DevTools @@ -1 +1 @@ -Subproject commit 4e28d74c9aafdf51b10bc274ddcf78fcb3b317d4 +Subproject commit 3446bcc774bcc278430e2cecd9d46a47f491656f