diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index 94b7bb99d..f73113098 100644 --- a/.github/workflows/discord-release-notify.yml +++ b/.github/workflows/discord-release-notify.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.26.0 + uses: shivammathur/setup-php@2.27.1 with: php-version: 8.1 diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index f7026c646..03bfedc89 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -20,7 +20,7 @@ jobs: submodules: true - name: Setup PHP - uses: shivammathur/setup-php@2.26.0 + uses: shivammathur/setup-php@2.27.1 with: php-version: ${{ matrix.php-version }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1fd1731a2..c57cecb84 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -173,7 +173,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.26.0 + uses: shivammathur/setup-php@2.27.1 with: php-version: 8.1 tools: php-cs-fixer:3.17 diff --git a/composer.json b/composer.json index 2f0b68730..c7bfd7d27 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.3.0" }, "require-dev": { - "phpstan/phpstan": "1.10.40", + "phpstan/phpstan": "1.10.41", "phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-strict-rules": "^1.2.0", "phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0" diff --git a/composer.lock b/composer.lock index 4c9588263..2888a3291 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ca499c3c5acafac837f7384ecdae136e", + "content-hash": "5c19f4766fd04be0cbd38d9f4681864e", "packages": [ { "name": "adhocore/json-comment", @@ -1378,16 +1378,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.40", + "version": "1.10.41", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "93c84b5bf7669920d823631e39904d69b9c7dc5d" + "reference": "c6174523c2a69231df55bdc65b61655e72876d76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/93c84b5bf7669920d823631e39904d69b9c7dc5d", - "reference": "93c84b5bf7669920d823631e39904d69b9c7dc5d", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6174523c2a69231df55bdc65b61655e72876d76", + "reference": "c6174523c2a69231df55bdc65b61655e72876d76", "shasum": "" }, "require": { @@ -1436,7 +1436,7 @@ "type": "tidelift" } ], - "time": "2023-10-30T14:48:31+00:00" + "time": "2023-11-05T12:57:57+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -2964,5 +2964,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/PocketMine.php b/src/PocketMine.php index 2a5609231..72d72506d 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -144,6 +144,13 @@ namespace pocketmine { $messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version."; } + if(($libdeflate_version = phpversion("libdeflate")) !== false){ + //make sure level 0 compression is available + if(version_compare($libdeflate_version, "0.2.0") < 0 || version_compare($libdeflate_version, "0.3.0") >= 0){ + $messages[] = "php-libdeflate ^0.2.0 is required, while you have $libdeflate_version."; + } + } + if(extension_loaded("pocketmine")){ $messages[] = "The native PocketMine extension is no longer supported."; } diff --git a/src/block/GoldOre.php b/src/block/GoldOre.php index ae26d8b4e..75374c1b0 100644 --- a/src/block/GoldOre.php +++ b/src/block/GoldOre.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\FortuneDropHelper; use pocketmine\item\Item; use pocketmine\item\VanillaItems; final class GoldOre extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ - return [VanillaItems::RAW_GOLD()]; + return [VanillaItems::RAW_GOLD()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))]; } public function isAffectedBySilkTouch() : bool{ return true; } diff --git a/src/block/IronOre.php b/src/block/IronOre.php index 8d7e9bd3f..11c8cc299 100644 --- a/src/block/IronOre.php +++ b/src/block/IronOre.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\FortuneDropHelper; use pocketmine\item\Item; use pocketmine\item\VanillaItems; final class IronOre extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ - return [VanillaItems::RAW_IRON()]; + return [VanillaItems::RAW_IRON()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))]; } public function isAffectedBySilkTouch() : bool{ return true; } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 8349f70c0..67a77c183 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -1495,7 +1495,9 @@ final class VanillaBlocks{ //for some reason, slabs have weird hardness like the legacy ones $slabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("ancient_debris", new Opaque(new BID(Ids::ANCIENT_DEBRIS), "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0)))); + self::register("ancient_debris", new class(new BID(Ids::ANCIENT_DEBRIS), "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))) extends Opaque{ + public function isFireProofAsItem() : bool{ return true; } + }); $netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 3600.0)); self::register("netherite", new class(new BID(Ids::NETHERITE), "Netherite Block", $netheriteBreakInfo) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index d5d314ee3..2ced414ff 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -77,22 +77,30 @@ class Sign extends Spawnable{ parent::__construct($world, $pos); } + private function readTextTag(CompoundTag $nbt, bool $lightingBugResolved) : void{ + $baseColor = new Color(0, 0, 0); + $glowingText = false; + if(($baseColorTag = $nbt->getTag(self::TAG_TEXT_COLOR)) instanceof IntTag){ + $baseColor = Color::fromARGB(Binary::unsignInt($baseColorTag->getValue())); + } + if($lightingBugResolved && ($glowingTextTag = $nbt->getTag(self::TAG_GLOWING_TEXT)) instanceof ByteTag){ + //both of these must be 1 - if only one is set, it's a leftover from 1.16.210 experimental features + //see https://bugs.mojang.com/browse/MCPE-117835 + $glowingText = $glowingTextTag->getValue() !== 0; + } + $this->text = SignText::fromBlob(mb_scrub($nbt->getString(self::TAG_TEXT_BLOB), 'UTF-8'), $baseColor, $glowingText); + } + public function readSaveData(CompoundTag $nbt) : void{ - if(($textBlobTag = $nbt->getTag(self::TAG_TEXT_BLOB)) instanceof StringTag){ //MCPE 1.2 save format - $baseColor = new Color(0, 0, 0); - $glowingText = false; - if(($baseColorTag = $nbt->getTag(self::TAG_TEXT_COLOR)) instanceof IntTag){ - $baseColor = Color::fromARGB(Binary::unsignInt($baseColorTag->getValue())); + $frontTextTag = $nbt->getTag(self::TAG_FRONT_TEXT); + if($frontTextTag instanceof CompoundTag){ + $this->readTextTag($frontTextTag, true); + }elseif($nbt->getTag(self::TAG_TEXT_BLOB) instanceof StringTag){ //MCPE 1.2 save format + $lightingBugResolved = false; + if(($lightingBugResolvedTag = $nbt->getTag(self::TAG_LEGACY_BUG_RESOLVE)) instanceof ByteTag){ + $lightingBugResolved = $lightingBugResolvedTag->getValue() !== 0; } - if( - ($glowingTextTag = $nbt->getTag(self::TAG_GLOWING_TEXT)) instanceof ByteTag && - ($lightingBugResolvedTag = $nbt->getTag(self::TAG_LEGACY_BUG_RESOLVE)) instanceof ByteTag - ){ - //both of these must be 1 - if only one is set, it's a leftover from 1.16.210 experimental features - //see https://bugs.mojang.com/browse/MCPE-117835 - $glowingText = $glowingTextTag->getValue() !== 0 && $lightingBugResolvedTag->getValue() !== 0; - } - $this->text = SignText::fromBlob(mb_scrub($textBlobTag->getValue(), 'UTF-8'), $baseColor, $glowingText); + $this->readTextTag($nbt, $lightingBugResolved); }else{ $text = []; for($i = 0; $i < SignText::LINE_COUNT; ++$i){ @@ -107,15 +115,19 @@ class Sign extends Spawnable{ } protected function writeSaveData(CompoundTag $nbt) : void{ - $nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines())); + $nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create() + ->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines())) + ->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())) + ->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0) + ->setByte(self::TAG_PERSIST_FORMATTING, 1) + ); + $nbt->setTag(self::TAG_BACK_TEXT, CompoundTag::create() + ->setString(self::TAG_TEXT_BLOB, "") + ->setInt(self::TAG_TEXT_COLOR, Binary::signInt(0xff_00_00_00)) + ->setByte(self::TAG_GLOWING_TEXT, 0) + ->setByte(self::TAG_PERSIST_FORMATTING, 1) + ); - for($i = 0; $i < SignText::LINE_COUNT; ++$i){ //Backwards-compatibility - $textKey = sprintf(self::TAG_TEXT_LINE, $i + 1); - $nbt->setString($textKey, $this->text->getLine($i)); - } - $nbt->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())); - $nbt->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0); - $nbt->setByte(self::TAG_LEGACY_BUG_RESOLVE, 1); $nbt->setByte(self::TAG_WAXED, $this->waxed ? 1 : 0); } diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index 5c84cd383..582039305 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -86,7 +86,7 @@ final class BlockStateUpgrader{ if(is_string($remap->newName)){ $newName = $remap->newName; }else{ - $flattenedValue = $oldState[$remap->newName->flattenedProperty]; + $flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null; if($flattenedValue instanceof StringTag){ $newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix); unset($oldState[$remap->newName->flattenedProperty]); diff --git a/src/network/mcpe/compression/ZlibCompressor.php b/src/network/mcpe/compression/ZlibCompressor.php index 317a64451..066ba876a 100644 --- a/src/network/mcpe/compression/ZlibCompressor.php +++ b/src/network/mcpe/compression/ZlibCompressor.php @@ -67,17 +67,12 @@ final class ZlibCompressor implements Compressor{ return $result; } - private static function zlib_encode(string $data, int $level) : string{ - return Utils::assumeNotFalse(zlib_encode($data, ZLIB_ENCODING_RAW, $level), "ZLIB compression failed"); - } - public function compress(string $payload) : string{ $compressible = $this->minCompressionSize !== null && strlen($payload) >= $this->minCompressionSize; - if(function_exists('libdeflate_deflate_compress')){ - return $compressible ? - libdeflate_deflate_compress($payload, $this->level) : - self::zlib_encode($payload, 0); - } - return self::zlib_encode($payload, $compressible ? $this->level : 0); + $level = $compressible ? $this->level : 0; + + return function_exists('libdeflate_deflate_compress') ? + libdeflate_deflate_compress($payload, $level) : + Utils::assumeNotFalse(zlib_encode($payload, ZLIB_ENCODING_RAW, $level), "ZLIB compression failed"); } } diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/generate-blockstate-upgrade-schema.php index f247d6112..dfb8f6066 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/generate-blockstate-upgrade-schema.php @@ -37,7 +37,6 @@ use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; -use function array_filter; use function array_key_first; use function array_keys; use function array_map; @@ -481,16 +480,9 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad throw new \RuntimeException("States with the same ID should be fully consistent"); } }else{ - if(isset($newNameFound[$oldName])){ - //some of the states stayed under the same ID - we can process these as normal states - $stateGroup = array_filter($blockStateMappings, fn(BlockStateMapping $m) => $m->new->getName() === $oldName); - if(processStateGroup($oldName, $stateGroup, $result)){ - foreach(Utils::stringifyKeys($stateGroup) as $k => $mapping){ - unset($blockStateMappings[$k]); - } - } - } //block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap + //even if some of the states stay under the same ID, the compression techniques used by this function + //implicitly rely on knowing the full set of old states and their new transformations $result->remappedStates[$oldName] = processRemappedStates($blockStateMappings); } }